#3335: isolate proxy caches and settle small render batches

This commit is contained in:
FredApps committed 2026-11-09 18:16:57 +01:00
1 parent d77d978973
commit 45e81e603e
4 files changed
+109 -2

No files matched your search

+8
View File
@@ -295,7 +295,15 @@ impl<'a> Renderer<'a> {
/// in this render ([`Self::mask_blur`]'s gate: the render options, the precomp layers above).
/// The key has the layer's own motion blur state, but a mask set to On blurs without it.
pub(crate) fn mask_gate_key(&self, ctx: &EvalCtx, layer: &Layer, key: Option<u64>) -> Option<u64> {
// Queue jobs share this cache but may override Proxy Use differently. The item's
// proxy switch alone does not describe the source a renderer actually reads.
let k = key?;
let k = match self.opts.proxy {
effectcraft_project::render_queue::ProxyUse::CurrentSettings => k,
effectcraft_project::render_queue::ProxyUse::UseAll => cache::derive(k, 0x7072_6f78_7901),
effectcraft_project::render_queue::ProxyUse::UseCompOnly => cache::derive(k, 0x7072_6f78_7902),
effectcraft_project::render_queue::ProxyUse::UseNone => cache::derive(k, 0x7072_6f78_7903),
};
let masked = layer.masks().is_some_and(|m| !m.children.is_empty());
Some(if masked && !self.mask_blur(ctx, layer).allowed { cache::derive(k, 0x6d62_6f66) } else { k })
}
+1
View File
@@ -20,6 +20,7 @@ mod mask_feather;
mod motion_tile;
mod nested;
mod paint;
mod proxy_cache;
mod remap;
mod roi;
mod stretch_limits;
+45
View File
@@ -0,0 +1,45 @@
use super::setup;
use crate::eval::EvalCtx;
use crate::{FootageSource, Image, LayerCache, RenderOpts, Renderer};
use effectcraft_color::Label;
use effectcraft_project::{Footage, FootageKind, ItemId, ItemKind, LayerSource, Proxy, build, render_queue::ProxyUse};
use effectcraft_time::Tick;
use std::sync::Arc;
/// A source and its proxy deliberately have different pixels.
struct Pictures;
impl FootageSource for Pictures {
fn frame(&self, _: ItemId, footage: &Footage, _: Tick) -> Option<Arc<Image>> {
let color = if footage.path == "proxy" { [0.0, 1.0, 0.0, 1.0] } else { [1.0, 0.0, 0.0, 1.0] };
Some(Arc::new(Image::filled(16, 16, color)))
}
}
/// A queue run reuses its layer cache across jobs with different Proxy Use settings.
/// Time effects read layer_input, which must never serve another job's source pixels.
#[test]
fn shared_layer_cache_separates_proxy_use() {
let (mut p, cid, comp) = setup();
let footage = Footage { path: "original".into(), kind: FootageKind::Still, width: 16, height: 16, has_video: true, ..Default::default() };
let item = p.add_item("Footage", Label::Red, None, ItemKind::Footage(footage.clone()));
p.item_mut(item).unwrap().proxy = Some(Box::new(Proxy { footage: Footage { path: "proxy".into(), ..footage }, enabled: false }));
let layer = build::layer(&mut p, &comp, "Footage", LayerSource::Footage { item }, (16, 16), None);
p.comp_mut(cid).unwrap().layers.push(layer);
for enabled in [false, true] {
p.item_mut(item).unwrap().proxy.as_mut().unwrap().enabled = enabled;
let cache = LayerCache::default();
let ctx = EvalCtx::new(&p, cid, p.comp(cid).unwrap(), Tick::ZERO);
let layer = &ctx.comp.layers[0];
for proxy in [ProxyUse::UseAll, ProxyUse::UseNone, ProxyUse::CurrentSettings, ProxyUse::UseCompOnly, ProxyUse::UseAll] {
let mut r = Renderer::new(&p, &Pictures, RenderOpts { proxy, ..Default::default() });
let input = r.layer_input(&ctx, layer, 0).unwrap();
let content = r.content_buf(&ctx, layer).unwrap();
r.cache = Some(&cache);
assert_eq!(r.layer_input(&ctx, layer, 0).unwrap().img.data, input.img.data, "{proxy:?}, enabled={enabled}");
assert_eq!(r.content_buf(&ctx, layer).unwrap().img.data, content.img.data, "{proxy:?}, enabled={enabled}");
let hits = cache.stats().hits;
r.layer_input(&ctx, layer, 0).unwrap();
assert!(cache.stats().hits > hits, "unchanged proxy settings still reuse inputs");
}
}
}