export: free the frame pool when an export ends

The export pipeline hands the float image of every frame it converts back to
`filmcraft_frame::pool` (#184), which keeps up to 320 MiB of them, and the decoders' plane shelves
hold up to 192 MiB of each type. An export's images are the size of its frames, which nothing else
asks for, so a finished export left them idle until the next one.

`pool::trim()` frees every idle buffer. `Pipeline` calls it when it is dropped, one place for every
way an export ends (done, failed, cancelled, dropped half way by a host that steps it, as the web
build does) and for every caller of `export` / `Exporter` (previews, proxies and AAF/OMF mixdowns
share the pipeline). A job still rendering at that moment allocates a few images again: the pool is
only a cache.

Measured with a synthetic 3 s 1080p24 matte exported as PNG frames three times in one process
(Apple M1, `footprint`, 10 s after the last export): 575 MB without the trim, the pool holding
332 MB idle, and 258 MB with it. macOS returns the freed pages over a few seconds, not at once.

Tests: `crates/frame/tests/pool_trim.rs` (every shelf emptied, the pool works afterwards) and
`crates/export/tests/pool_trim.rs` (an image sequence, a finished and a cancelled video, an export
that could not start and a stepped export dropped half way each leave nothing idle; without the
trim they fail).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Dario Carnelutti
2026-10-08 10:13:07 -06:00
co-authored by Claude Sonnet 5.5
parent 5231852443
commit 10879f385e
7 changed files with 190 additions and 4 deletions
+11
View File
@@ -95,6 +95,17 @@ compositor's savings are a larger part of the export.
What is left in a frame without overlays is the Y'CbCr → linear float conversion of the camera
picture (4.5 ms in the synthetic case) and the conversion to 8 bits (2 ms).
### Memory after an export
The recycled images stay on the shelves of `filmcraft_frame::pool` (up to 320 MiB of them, and
192 MiB of each plane type), and nothing else asks for images of an export's size, so a finished
export left them idle. The export pipeline now trims the pool when it goes away, however the
export ended (done, failed, cancelled, dropped half way): `perf.stats` reads `decode.planePoolMB`
0 afterwards. A process that exported a synthetic 3 s 1080p24 matte as PNG frames three times
(Apple M1, `footprint`, 10 s after the last export) rested at **575 MB** with the pool holding
332 MB idle and at **258 MB** with the trim. The memory does not come back at once: macOS returns
freed pages over a few seconds (892 MB right after the export in both cases, 726 MB after 2 s).
## Results (GPU2: standard effects on the GPU compositor, #30, before → after)
Before = this change with clips that carry standard effects sent back to the CPU layer path in