From c33d5c20df766508bea366c4cbe3754c7999741d Mon Sep 17 00:00:00 2001 From: ded Date: Tue, 20 Oct 2026 04:01:07 +0300 Subject: [PATCH] G5: a still asked for at its own size is the unsized frame (#1812) Review: frame_at_size at a still's native size made a second cache key, so the pool decoded and kept a second full-size copy of a frame thumbnail() or frame() already had. Co-Authored-By: Claude Opus 5.5 --- crates/media/src/pool.rs | 3 ++- crates/media/tests/footage.rs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/media/src/pool.rs b/crates/media/src/pool.rs index d3b6da0d..6bc4bd8d 100644 --- a/crates/media/src/pool.rs +++ b/crates/media/src/pool.rs @@ -716,7 +716,8 @@ impl Inner { } let mut loc = Self::locate(footage, t, float); let movie = loc.media_t.is_some(); - if let Some(size) = size { + // A still asked for at its own size is the unsized frame (one decode, one entry). + if let Some(size) = size.filter(|s| movie || *s != (footage.width, footage.height)) { loc.key.size = size; } let mut c = lock(&self.cache); diff --git a/crates/media/tests/footage.rs b/crates/media/tests/footage.rs index 9e68e3f6..2e882d73 100644 --- a/crates/media/tests/footage.rs +++ b/crates/media/tests/footage.rs @@ -595,6 +595,11 @@ fn stills_at_reduced_sizes_are_resampled_once() { let f = probe(&p).expect("probe"); let pool = MediaPool::new(); let full = pool.frame(ItemId(1), &f, Tick::ZERO).expect("frame"); + // At its own size it is the unsized frame: no second decode, no second full-size copy. + let misses = pool.stats().misses; + let native = pool.frame_at_size(ItemId(1), &f, Tick::ZERO, f.width, f.height).expect("frame"); + assert!(Arc::ptr_eq(&full, &native), "the native size reuses the unsized frame"); + assert_eq!(pool.stats().misses, misses); for (w, h) in [(306, 48), (153, 24), (77, 12)] { let sized = pool.frame_at_size(ItemId(1), &f, Tick::ZERO, w, h).expect("frame"); assert_eq!(*sized, effectcraft_raster::resample(&full, w, h), "{w}×{h}: the renderer's resample");