G5: a styled layer rendered by two workers at once shares its body and passes (#1812)

Review: styled() kept the buffers it rendered instead of the ones LayerCache::insert returned, so
without a blending-space conversion concurrent misses still returned separate body and pass
buffers (and the GPU uploaded each).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ded
2026-10-20 16:12:22 +03:00
co-authored by Claude Opus 5.5
parent 9c5494fd29
commit a330110476
2 changed files with 38 additions and 5 deletions
+6 -5
View File
@@ -1589,12 +1589,13 @@ impl<'a> Renderer<'a> {
t.cached = false;
t.effects.push(("layerStyles".into(), t0.elapsed().as_secs_f64() * 1e3));
}
let body = Arc::new(st.body);
let passes: Vec<_> = st.passes.into_iter().map(|(b, m)| (Arc::new(b), m)).collect();
let mut body = Arc::new(st.body);
let mut passes: Vec<_> = st.passes.into_iter().map(|(b, m)| (Arc::new(b), m)).collect();
// The buffers the cache keeps (another worker's, if it kept them first).
if let (Some(c), Some(k)) = (self.cache_to_keep(), key) {
c.insert(k, body.clone());
for (i, (b, _)) in passes.iter().enumerate() {
c.insert(cache::derive(k, i as u64 + 1), b.clone());
body = c.insert(k, body);
for (i, (b, _)) in passes.iter_mut().enumerate() {
*b = c.insert(cache::derive(k, i as u64 + 1), b.clone());
}
}
Some(StyledLayer { passes, body, content, key, ckey, plain: false })
+32
View File
@@ -475,3 +475,35 @@ fn gradient_glow_uses_gradient() {
let px = img.straight(121, 50);
assert!(px[0] > px[2], "near the edge the glow takes the gradient's start colour {px:?}");
}
/// #1812 (review of #1928): a styled layer rendered by several workers at once shares its body and
/// style passes, as unstyled layers share their buffer.
#[test]
fn workers_styling_a_layer_at_once_share_its_buffers() {
let (p, cid) = scene([1.0; 3], None, |p, c, l| {
add(p, c, l, "dropShadow");
add(p, c, l, "stroke");
});
let cache = crate::LayerCache::default();
let start = std::sync::Barrier::new(8);
let styled: Vec<_> = std::thread::scope(|s| {
let hs: Vec<_> = (0..8)
.map(|_| {
s.spawn(|| {
let mut r = Renderer::new(&p, &NoFootage, RenderOpts::default());
r.cache = Some(&cache);
let ctx = r.eval_ctx(cid, Tick::ZERO).unwrap();
start.wait();
r.styled_layer(&ctx, &p.comp(cid).unwrap().layers[0]).unwrap()
})
})
.collect();
hs.into_iter().map(|h| h.join().unwrap()).collect()
});
let first = &styled[0];
assert!(!first.passes.is_empty(), "the drop shadow is a pass");
for st in &styled {
assert!(std::sync::Arc::ptr_eq(&st.body, &first.body), "one body");
assert!(st.passes.iter().zip(&first.passes).all(|(a, b)| std::sync::Arc::ptr_eq(&a.0, &b.0)), "one set of passes");
}
}