Merge pull request #688 from MatejKozlovsky/gpu-resident-effects

This commit is contained in:
Brandon Thomas
2026-10-13 04:33:22 -04:00
6 changed files with 335 additions and 19 deletions
+9 -4
View File
@@ -423,8 +423,10 @@ fn hash_debug(h: &mut KeyHasher, v: &impl std::fmt::Debug) {
/// Time-based layer content: does any part of the source/effects read the clock directly
/// (rather than through property values)?
fn time_dependent(layer: &Layer) -> bool {
if layer.switches.effects
/// `effects: false` ignores the effects (a key of the source and masks only).
fn time_dependent(layer: &Layer, effects: bool) -> bool {
if effects
&& layer.switches.effects
&& let Some(fx) = layer.effects()
{
for g in fx.groups() {
@@ -522,7 +524,10 @@ fn has_expression(g: &PropGroup) -> bool {
/// Cache key for an adjustment layer's footprint (its source through its masks, see
/// `Renderer::adjustment_footprint`): the GPU compositor then uploads it once, not every frame.
pub fn footprint_key(ctx: &EvalCtx, layer: &Layer, scale: f64, draft: bool, blur: bool) -> Option<u64> {
Some(derive(key_any(ctx, layer, scale, draft, blur, false, None)?, 0xf007_9417))
// The footprint is the source through the masks, without the effects: a time-dependent
// effect (Noise on a finishing adjustment layer) must not re-key it every frame (on the GPU,
// a full-frame re-upload per frame).
Some(derive(key_any(ctx, layer, scale, draft, blur, false, Some(0))?, 0xf007_9417))
}
fn key_with(ctx: &EvalCtx, layer: &Layer, scale: f64, draft: bool, blur: bool, footage: bool) -> Option<u64> {
@@ -620,7 +625,7 @@ fn key_any(ctx: &EvalCtx, layer: &Layer, scale: f64, draft: bool, blur: bool, fo
effectcraft_effects::paint::cache_key(g, lt).0.hash(&mut h);
}
}
if time_dependent(layer) {
if time_dependent(layer, effects != Some(0)) {
layer.layer_time(ctx.time).0.hash(&mut h);
// Effects also see the layer's time mapping.
layer.start_time.0.hash(&mut h);
+1 -1
View File
@@ -78,7 +78,7 @@ impl Dependencies<'_> {
hash_debug(self.h, layer);
hash_values(self.h, ctx, layer, &layer.props);
hash_debug(self.h, &ctx.world_matrix(layer));
if super::time_dependent(layer) {
if super::time_dependent(layer, true) {
layer.layer_time(ctx.time).0.hash(self.h);
}
match layer.source {
+13
View File
@@ -861,6 +861,19 @@ impl<'a> Renderer<'a> {
Region::Full
}
/// Whether `layer`'s processed buffer (source → masks → effects) is cached and the same as
/// one frame earlier: drawing it from the layer cache (an accelerator uploads it once) beats
/// re-running its effects every frame.
pub fn layer_is_static(&self, ctx: &EvalCtx, layer: &Layer) -> bool {
if self.cache.is_none() {
return false;
}
let (scale, draft, blur) = (self.raster_scale(ctx, layer), self.opts.draft, self.mb_on(ctx, layer));
let prev = ctx.time - ctx.comp.frame_rate.frame_duration();
let now = cache::layer_key(ctx, layer, scale, draft, blur);
now.is_some() && now == cache::layer_key(&ctx.at(prev), layer, scale, draft, blur)
}
/// Rasterisation scale of a layer's source: the output scale, or — for text and shape layers,
/// which are always continuously rasterised (#390, #751), and Continuously Rasterize vector
/// footage — the output scale times the layer's on-screen scale (quarter-octave steps, rounded