14 Commits
Author SHA1 Message Date
Davi AndreiandClaude Sonnet 5.5 b485e2c180 Thumbnails decode with small decoders that are not kept
Right after importing many 4K clips the Project panel and the timeline ask
every clip for a thumbnail. Up to six frame workers decoded them at once,
each with a frame-threaded software H.264 decoder (16 threads and 18
pictures in flight here, about 0.5 GB at 4K), and the decoders stayed: 42
4K clips with hardware decoding Off took the process past 6 GB during import.

Thumbnail jobs (item frames at the Media Browser / thumbnail priorities) now
run under filmcraft_media::cancel::with_background: H.264 decoders made for
them use 2 threads (thread_limited), parallelism comes from the workers
decoding different files, and the decoders of caches only thumbnails used
are released after each thumbnail (the clip served last keeps its own). A
monitor or playback request on such a cache replaces the small decoder with
a full one. Sequence frames are never background (the scopes ask for the
program frame at 40 while playing).

42 4K clips (Windows), peak private bytes: hardware Off 6.1-6.8 GB (killed)
-> 3.1-3.2 GB; hardware Auto 2.7-2.8 GB (#381) -> 2.3-2.4 GB. Thumbnails of
12 / 42 clips ready in 0.6 / 2.2 s instead of 2.2 s / not finishing.
Example crates/ui-egui/examples/thumb_burst.rs reproduces the burst.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 04:32:43 -03:00
Davi AndreiandClaude Sonnet 5.5 0c64e41b53 Decode caches: navigation bursts no longer keep every clip's decoder and frames (#55)
A cache asked for a frame within IDLE (3 s) counted as in use, and the caps
of #41 (4 live decoders, 1 GiB of frames) only bound caches idle for longer,
so moving the playhead across 42 4K clips kept 42 hardware decoders (6.5 GB
of VRAM) and up to 3.9 GB of frames. Now only caches touched within RECENT
(500 ms) are protected: caches idle 0.5-3 s give up decoders beyond
HARD_DECODERS (8) and frames beyond the shared budget, least recently used
first; the 3 s rule down to 4 decoders is unchanged. store() refreshes
last_used.

Measured with a 42-clip 4K reproduction (Windows, Media Foundation): peak
private bytes 6.6 -> 4.0 GB, frame cache 3.9 -> 1.1 GB, live decoders
42 -> 8-14, GPU memory 6.5 -> 4.3-4.6 GB, 0 dropped frames before and after.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:13:42 -03:00
Davi AndreiandClaude Sonnet 5.5 9510e26584 Export: render the next batch while the current one is encoded
A step now renders batch k+1 on the rayon pool while the calling thread
encodes, muxes and mixes the audio of batch k. The file is byte-identical
with or without overlap. A prefetch that hits pending media is dropped and
rendered again, cancel frees it, a render panic becomes an error, and wasm
never overlaps. perf.stats export.stages gains waitMs.

Memory: up to 2 x batch x frame bytes of finished frames are in flight, so
the batch is capped to a 512 MiB budget (1080p keeps 16, 4K SDR 8, 4K HDR 2
frames). Measured peak RSS vs before: 1080p +67..+158 MB; 4K SDR -535..-927
MB; 4K HDR about -2.6 GB (5.2 -> 2.6 GB). Wall time 1.06-1.16x faster.

Known pre-existing web issue, untouched: AudioOut::pull advances before the
pending check, so a retried audio group is lost.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 01:26:41 -03:00
Davi AndreiandClaude Opus 5.5 1996f319c3 Export: stage wall times in perf.stats (export.stages)
filmcraft_export::Stage, stage_times(), note_stage() and timed(): the wall
time of setup, loudness, render, encode (and its RGB->YUV convert part),
audio, mux and finish, summed per process like export.hardware. Converters
in the H.264 / ProRes / NVENC / VideoToolbox encoders are timed as Convert.
Two clock reads per stage per batch; the exported file does not change
(determinism tests). Test: an export grows every stage counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 01:26:09 -03:00
Davi AndreiandClaude Sonnet 5.5 dbae11b843 Windows: NVENC H.265 Main 10 HDR (PQ / HLG) export
HDR sequences export as real 10-bit HEVC Main 10 through NVENC where the GPU
reports 10-bit HEVC: float BT.2020 R'G'B' -> rgbf_to_yuv420_10 -> P010 input;
VUI BT.2020 + PQ/HLG; HDR10 mastering-display / content-light as SEI on every
IDR (PQ); colr/mdcv/clli in the sample entry. New register_hdr_probe /
hdr_available in the export crate; macOS and GPUs without 10-bit keep
tone-mapping HDR sequences to 8-bit SDR HEVC. ABI asserts extended and
re-verified against nvEncodeAPI.h 12.1 (0 drift). 8-bit SDR HEVC unchanged;
the two #328 tests that asserted HDR is declined now expect Main 10.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:38:14 -03:00
Davi AndreiandClaude Opus 5.5 a8f91e3ffe M2: H.265 stays in the Main tier (pick the level)
Left to choose, NVENC answered a peak bitrate above its level's Main tier
limit with the High tier: 1080p30 at 12/18 Mbit/s was written as level 4 High
tier, 2160p30/60 at 40/60 Mbit/s as 5.0/5.1 High tier, which many hardware
decoders refuse. The level is now the lowest whose Main tier limits hold the
picture size, sample rate and peak bitrate (H.265 Table A.8), e.g. 1080p30 at
18 Mbit/s is level 4.1 Main tier. Unit test of the table; hardware test that
the hvcC says Main tier at the expected level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:23:25 -03:00
Davi AndreiandClaude Opus 5.5 29b454ac17 M2: review fixes for NVENC H.265
- register() asks the HEVC probe on a thread of its own (the first probe is
  about half a second: D3D11 device + NVENC session), so the first draw of
  Export's format list does not wait for it; tests/nvenc_hevc_warm.rs.
- A machine with no HEVC encoder is not a declined hardware attempt: the
  factory returns None without counting `declined`.
- HEVC tests skip only when the probe says there is no HEVC encoder; on a
  machine that has one, a configuration NVENC refuses fails the test.
- Wording: NVENC is FilmCraft's only HEVC encoder on Windows (Windows itself
  ships others).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:48:43 -03:00
Davi AndreiandClaude Sonnet 5.5 deebe2e5b2 Windows: NVENC H.265 (HEVC) Main hardware encoding for Export
One NVENC session, ring and NV12 path now serve H.264 and HEVC (Codec enum,
Profile::HevcMain). HEVC Main 8-bit 4:2:0 SDR BT.709, MP4/MOV hvc1, parameter
sets only in an hvcC built from the SPS the encoder wrote. The H.265 format is
available through a cached NVENC probe; choosing it is the opt-in, declines are
counted errors. ABI asserts regenerated from nvEncodeAPI.h 12.1 (no drift) and
extended for HEVC; tests, docs and roadmap updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 16:28:50 -03:00
Davi Andrei a7b51aa313 nvenc: fix a doc path 2026-10-07 15:42:38 -03:00
Davi AndreiandClaude Sonnet 5.5 11b199dbcd Docs: NVENC H.264 hardware encoding
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 15:42:28 -03:00
Davi AndreiandClaude Sonnet 5.5 6db932a9ba Windows: NVENC H.264 hardware encoding for Export (work in progress, #30)
NVENC through nvEncodeAPI64.dll (API 12.1, FFI layout checked against the C header by generated
tests), registered as an Export encoder in front of the software one; Export > Hardware encoding
(off by default). Declines two-pass VBR, HDR, MXF, unsupported sizes; B-frames, IDR/GOP, VUI, avcC.
Tests: decode with our decoder (PSNR), pipeline export vs software, decline paths.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 15:41:02 -03:00
Davi AndreiandClaude Sonnet 5.5 79cb1d0f8d Windows: VP9 + AV1 hardware decode through Media Foundation / DXVA (#30)
Extends the Windows backend (#141) to VP9 (profiles 0 and 2) and AV1 (main, 8/10-bit), 4:2:0.

- codecs::hw: FrameStreamInfo (vpcC / av1C): profile, bit depth, chroma, picture size and colour as
  the software decoders report them (vp9_color / av1_color are now shared with them), random-access
  samples, in-band format changes; StreamInfo lets HybridDecoder handle NAL and frame-coded streams.
- platform: media_foundation::stream (what each codec needs: DXVA profile, MFT request, input
  format), MfDecoder generalised; VP9 frames / AV1 temporal units go in as they are; the MFT's
  declared picture size (display aperture) is checked against the bitstream's.
- Declined up front: VP9 profiles 1/3, AV1 profiles 1/2, 4:2:2/4:4:4, mono, 12-bit, no codec
  extension, no DXVA profile. A key frame of another size or a different sequence header falls back
  to the software decoder.
- Tests: bit-exact parity (360p / 1080p / 2160p, seeks, resets, flush, hidden frames), forced
  failure, damaged samples and av1C, format change fallback, WebM / Matroska through the media stack.
- Bench: AV1 fixtures with libaom when ffmpeg lacks SVT-AV1.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 12:39:45 -03:00
Davi AndreiandClaude Sonnet 5.5 932cdb77f5 Docs: Windows hardware decoding (platform README, ADR 0001, architecture, performance, roadmap)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 10:07:40 -03:00
Davi AndreiandClaude Sonnet 5.5 5e440a6607 Windows: Media Foundation / Direct3D 11 hardware H.264 + HEVC decode (#30)
New backend in crates/platform (media_foundation/): a Direct3D-aware decoder MFT is driven at the
level of single access units (Annex B), given the process's D3D11 video device through an
IMFDXGIDeviceManager so it decodes with DXVA, and its NV12 / P010 textures are read back through a
staging texture into planar Yuv8 / Yuv16. H.264 Baseline/Main/High and HEVC Main / Main 10, 4:2:0.

- Hardware is verified: non-D3D-aware or async MFTs, streams the GPU's DXVA decoder does not list,
  and system-memory output (Microsoft's MFT software fallback) are declined or fail the stream so
  HybridDecoder continues with our decoder.
- mfplat.dll is loaded at run time so Windows N editions still start.
- register() / registered() / hardware_decoder_for() cover Windows; perf.stats reports the backend.
- Tests (Windows): bit-exact parity with the software decoders at 640x360, 1080p and 2160p
  (resets, seeks, flush), forced mid-stream failure, damaged input, concurrency, repeated init.
- Bench: runs on Windows (ffmpeg on PATH, process/thread CPU clocks), HEVC Main 10 2160p row,
  hardware counters per row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
2026-10-07 10:07:40 -03:00