From 97297bd167aed7e05542fbbc85959365539f1e8b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 25 Sep 2025 08:48:56 +0200 Subject: [PATCH] Fix #146484: Stack overflow due to recursive strip rendering When effect of adjustment layer strip is moved below the adjustment layer, this causes infinite loop in strip rendering. Same happens when you use multicam strip and set source channel to one of its effects. This is fixed by passing `SeqRenderState` to the effects. If any strip renders "seqbase" pointer of strip is stored in set in the render state struct. If the pointer exists in this set, function returns without rendering anything. In other words, The strip must never render itself. Pull Request: https://projects.blender.org/blender/blender/pulls/146624 --- source/blender/sequencer/SEQ_effects.hh | 2 ++ .../intern/effects/vse_effect_add_sub_mul.cc | 3 +++ .../intern/effects/vse_effect_adjustment.cc | 15 +++++++---- .../intern/effects/vse_effect_blend.cc | 4 +++ .../intern/effects/vse_effect_cross.cc | 2 ++ .../effects/vse_effect_gaussian_blur.cc | 1 + .../intern/effects/vse_effect_glow.cc | 1 + .../intern/effects/vse_effect_multi_camera.cc | 6 +++-- .../intern/effects/vse_effect_solid_color.cc | 1 + .../intern/effects/vse_effect_speed.cc | 3 ++- .../intern/effects/vse_effect_text.cc | 1 + .../intern/effects/vse_effect_transform.cc | 1 + .../intern/effects/vse_effect_wipe.cc | 1 + source/blender/sequencer/intern/render.cc | 26 ++++++++++++------- source/blender/sequencer/intern/render.hh | 3 +++ .../recursive_render_crash_146484.blend | 3 +++ .../recursive_render_crash_146484.png | 3 +++ 17 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 tests/files/sequence_editing/effects/recursive_render_crash_146484.blend create mode 100644 tests/files/sequence_editing/effects/reference/recursive_render_crash_146484.png diff --git a/source/blender/sequencer/SEQ_effects.hh b/source/blender/sequencer/SEQ_effects.hh index 0aa63b2de6e..fc573ec0d26 100644 --- a/source/blender/sequencer/SEQ_effects.hh +++ b/source/blender/sequencer/SEQ_effects.hh @@ -20,6 +20,7 @@ struct TextVars; namespace blender::seq { +struct SeqRenderState; struct RenderData; enum class StripEarlyOut { @@ -58,6 +59,7 @@ struct EffectHandle { /* execute the effect */ ImBuf *(*execute)(const RenderData *context, + SeqRenderState *state, Strip *strip, float timeline_frame, float fac, diff --git a/source/blender/sequencer/intern/effects/vse_effect_add_sub_mul.cc b/source/blender/sequencer/intern/effects/vse_effect_add_sub_mul.cc index 54c23694e86..20c8f440c1d 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_add_sub_mul.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_add_sub_mul.cc @@ -47,6 +47,7 @@ struct AddEffectOp { }; static ImBuf *do_add_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*seq*/, float /*timeline_frame*/, float fac, @@ -91,6 +92,7 @@ struct SubEffectOp { }; static ImBuf *do_sub_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*seq*/, float /*timeline_frame*/, float fac, @@ -135,6 +137,7 @@ struct MulEffectOp { }; static ImBuf *do_mul_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*seq*/, float /*timeline_frame*/, float fac, diff --git a/source/blender/sequencer/intern/effects/vse_effect_adjustment.cc b/source/blender/sequencer/intern/effects/vse_effect_adjustment.cc index 162a78ccdb7..718d5876433 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_adjustment.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_adjustment.cc @@ -31,7 +31,10 @@ static StripEarlyOut early_out_adjustment(const Strip * /*strip*/, float /*fac*/ return StripEarlyOut::NoInput; } -static ImBuf *do_adjustment_impl(const RenderData *context, Strip *strip, float timeline_frame) +static ImBuf *do_adjustment_impl(const RenderData *context, + SeqRenderState *state, + Strip *strip, + float timeline_frame) { Editing *ed; ImBuf *i = nullptr; @@ -50,7 +53,7 @@ static ImBuf *do_adjustment_impl(const RenderData *context, Strip *strip, float if (strip->channel > 1) { i = seq_render_give_ibuf_seqbase( - context, timeline_frame, strip->channel - 1, channels, seqbasep); + context, state, timeline_frame, strip->channel - 1, channels, seqbasep); } /* Found nothing? so let's work the way up the meta-strip stack, so @@ -63,7 +66,7 @@ static ImBuf *do_adjustment_impl(const RenderData *context, Strip *strip, float meta = lookup_meta_by_strip(ed, strip); if (meta) { - i = do_adjustment_impl(context, meta, timeline_frame); + i = do_adjustment_impl(context, state, meta, timeline_frame); } } @@ -71,6 +74,7 @@ static ImBuf *do_adjustment_impl(const RenderData *context, Strip *strip, float } static ImBuf *do_adjustment(const RenderData *context, + SeqRenderState *state, Strip *strip, float timeline_frame, float /*fac*/, @@ -82,11 +86,12 @@ static ImBuf *do_adjustment(const RenderData *context, ed = context->scene->ed; - if (!ed) { + if (!ed || state->strips_rendering_seqbase.contains(strip)) { return nullptr; } - out = do_adjustment_impl(context, strip, timeline_frame); + state->strips_rendering_seqbase.add(strip); + out = do_adjustment_impl(context, state, strip, timeline_frame); return out; } diff --git a/source/blender/sequencer/intern/effects/vse_effect_blend.cc b/source/blender/sequencer/intern/effects/vse_effect_blend.cc index a05b4f1b191..9461025614d 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_blend.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_blend.cc @@ -76,6 +76,7 @@ struct AlphaOverEffectOp { }; static ImBuf *do_alphaover_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*strip*/, float /*timeline_frame*/, float fac, @@ -125,6 +126,7 @@ struct AlphaUnderEffectOp { }; static ImBuf *do_alphaunder_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*strip*/, float /*timeline_frame*/, float fac, @@ -325,6 +327,7 @@ struct BlendModeEffectOp { }; static ImBuf *do_blend_mode_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float fac, @@ -356,6 +359,7 @@ static void init_colormix_effect(Strip *strip) } static ImBuf *do_colormix_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float /*fac*/, diff --git a/source/blender/sequencer/intern/effects/vse_effect_cross.cc b/source/blender/sequencer/intern/effects/vse_effect_cross.cc index b44b12c63c5..22cb59b5364 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_cross.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_cross.cc @@ -45,6 +45,7 @@ struct CrossEffectOp { }; static ImBuf *do_cross_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*seq*/, float /*timeline_frame*/, float fac, @@ -98,6 +99,7 @@ struct GammaCrossEffectOp { }; static ImBuf *do_gammacross_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip * /*seq*/, float /*timeline_frame*/, float fac, diff --git a/source/blender/sequencer/intern/effects/vse_effect_gaussian_blur.cc b/source/blender/sequencer/intern/effects/vse_effect_gaussian_blur.cc index 98c4a6add94..029ddac66db 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_gaussian_blur.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_gaussian_blur.cc @@ -139,6 +139,7 @@ static void gaussian_blur_y(const Span gaussian, } static ImBuf *do_gaussian_blur_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float /*fac*/, diff --git a/source/blender/sequencer/intern/effects/vse_effect_glow.cc b/source/blender/sequencer/intern/effects/vse_effect_glow.cc index 45637775cc6..e9fd0aaa862 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_glow.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_glow.cc @@ -224,6 +224,7 @@ static void do_glow_effect_float(Strip *strip, } static ImBuf *do_glow_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float fac, diff --git a/source/blender/sequencer/intern/effects/vse_effect_multi_camera.cc b/source/blender/sequencer/intern/effects/vse_effect_multi_camera.cc index 7a2cb1b81ec..066fcae9467 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_multi_camera.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_multi_camera.cc @@ -30,6 +30,7 @@ static StripEarlyOut early_out_multicam(const Strip * /*strip*/, float /*fac*/) } static ImBuf *do_multicam(const RenderData *context, + SeqRenderState *state, Strip *strip, float timeline_frame, float /*fac*/, @@ -44,7 +45,7 @@ static ImBuf *do_multicam(const RenderData *context, } ed = context->scene->ed; - if (!ed) { + if (!ed || state->strips_rendering_seqbase.contains(strip)) { return nullptr; } ListBase *seqbasep = get_seqbase_by_strip(context->scene, strip); @@ -53,8 +54,9 @@ static ImBuf *do_multicam(const RenderData *context, return nullptr; } + state->strips_rendering_seqbase.add(strip); out = seq_render_give_ibuf_seqbase( - context, timeline_frame, strip->multicam_source, channels, seqbasep); + context, state, timeline_frame, strip->multicam_source, channels, seqbasep); return out; } diff --git a/source/blender/sequencer/intern/effects/vse_effect_solid_color.cc b/source/blender/sequencer/intern/effects/vse_effect_solid_color.cc index 75c672da9b4..845492c4542 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_solid_color.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_solid_color.cc @@ -50,6 +50,7 @@ static StripEarlyOut early_out_color(const Strip * /*strip*/, float /*fac*/) } static ImBuf *do_solid_color(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float /*fac*/, diff --git a/source/blender/sequencer/intern/effects/vse_effect_speed.cc b/source/blender/sequencer/intern/effects/vse_effect_speed.cc index 8a2a56becfc..e9c96fa7e4b 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_speed.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_speed.cc @@ -184,6 +184,7 @@ static float speed_effect_interpolation_ratio_get(Scene *scene, } static ImBuf *do_speed_effect(const RenderData *context, + SeqRenderState *state, Strip *strip, float timeline_frame, float fac, @@ -197,7 +198,7 @@ static ImBuf *do_speed_effect(const RenderData *context, if (s->flags & SEQ_SPEED_USE_INTERPOLATION) { fac = speed_effect_interpolation_ratio_get(context->scene, strip, timeline_frame); /* Current frame is ibuf1, next frame is ibuf2. */ - out = cross_effect.execute(context, nullptr, timeline_frame, fac, ibuf1, ibuf2); + out = cross_effect.execute(context, state, nullptr, timeline_frame, fac, ibuf1, ibuf2); return out; } diff --git a/source/blender/sequencer/intern/effects/vse_effect_text.cc b/source/blender/sequencer/intern/effects/vse_effect_text.cc index 27117ee80fc..57627144f7e 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_text.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_text.cc @@ -1021,6 +1021,7 @@ TextVarsRuntime *text_effect_calc_runtime(const Strip *strip, int font, const in } static ImBuf *do_text_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float /*fac*/, diff --git a/source/blender/sequencer/intern/effects/vse_effect_transform.cc b/source/blender/sequencer/intern/effects/vse_effect_transform.cc index 11c65ad01c4..d2c8295ef2d 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_transform.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_transform.cc @@ -130,6 +130,7 @@ static void transform_image(int x, } static ImBuf *do_transform_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float /*fac*/, diff --git a/source/blender/sequencer/intern/effects/vse_effect_wipe.cc b/source/blender/sequencer/intern/effects/vse_effect_wipe.cc index 016f7536901..c4083358265 100644 --- a/source/blender/sequencer/intern/effects/vse_effect_wipe.cc +++ b/source/blender/sequencer/intern/effects/vse_effect_wipe.cc @@ -354,6 +354,7 @@ static void do_wipe_effect( } static ImBuf *do_wipe_effect(const RenderData *context, + SeqRenderState * /*state*/, Strip *strip, float /*timeline_frame*/, float fac, diff --git a/source/blender/sequencer/intern/render.cc b/source/blender/sequencer/intern/render.cc index 66f9c496ab8..5b494a642f3 100644 --- a/source/blender/sequencer/intern/render.cc +++ b/source/blender/sequencer/intern/render.cc @@ -770,7 +770,7 @@ static ImBuf *seq_render_effect_strip_impl(const RenderData *context, switch (early_out) { case StripEarlyOut::NoInput: - out = sh.execute(context, strip, timeline_frame, fac, nullptr, nullptr); + out = sh.execute(context, state, strip, timeline_frame, fac, nullptr, nullptr); break; case StripEarlyOut::DoEffect: for (i = 0; i < 2; i++) { @@ -795,7 +795,7 @@ static ImBuf *seq_render_effect_strip_impl(const RenderData *context, } if (ibuf[0] && (ibuf[1] || effect_get_num_inputs(strip->type) == 1)) { - out = sh.execute(context, strip, timeline_frame, fac, ibuf[0], ibuf[1]); + out = sh.execute(context, state, strip, timeline_frame, fac, ibuf[0], ibuf[1]); } break; case StripEarlyOut::UseInput1: @@ -1789,7 +1789,13 @@ static StripEarlyOut strip_get_early_out_for_blend_mode(Strip *strip) } static ImBuf *seq_render_strip_stack_apply_effect( - const RenderData *context, Strip *strip, float timeline_frame, ImBuf *ibuf1, ImBuf *ibuf2) + + const RenderData *context, + SeqRenderState *state, + Strip *strip, + float timeline_frame, + ImBuf *ibuf1, + ImBuf *ibuf2) { ImBuf *out; EffectHandle sh = strip_blend_mode_handle_get(strip); @@ -1798,10 +1804,10 @@ static ImBuf *seq_render_strip_stack_apply_effect( int swap_input = seq_must_swap_input_in_blend_mode(strip); if (swap_input) { - out = sh.execute(context, strip, timeline_frame, fac, ibuf2, ibuf1); + out = sh.execute(context, state, strip, timeline_frame, fac, ibuf2, ibuf1); } else { - out = sh.execute(context, strip, timeline_frame, fac, ibuf1, ibuf2); + out = sh.execute(context, state, strip, timeline_frame, fac, ibuf1, ibuf2); } return out; @@ -1911,7 +1917,8 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, context->rectx, context->recty, 32, use_float ? IB_float_data : IB_byte_data); seq_imbuf_assign_spaces(context->scene, ibuf1); - out = seq_render_strip_stack_apply_effect(context, strip, timeline_frame, ibuf1, ibuf2); + out = seq_render_strip_stack_apply_effect( + context, state, strip, timeline_frame, ibuf1, ibuf2); IMB_metadata_copy(out, ibuf2); intra_frame_cache_put_composite(context->scene, strip, out); @@ -1939,7 +1946,8 @@ static ImBuf *seq_render_strip_stack(const RenderData *context, ImBuf *ibuf1 = out; ImBuf *ibuf2 = seq_render_strip(context, state, strip, timeline_frame); - out = seq_render_strip_stack_apply_effect(context, strip, timeline_frame, ibuf1, ibuf2); + out = seq_render_strip_stack_apply_effect( + context, state, strip, timeline_frame, ibuf1, ibuf2); IMB_freeImBuf(ibuf1); IMB_freeImBuf(ibuf2); @@ -2013,14 +2021,14 @@ ImBuf *render_give_ibuf(const RenderData *context, float timeline_frame, int cha } ImBuf *seq_render_give_ibuf_seqbase(const RenderData *context, + SeqRenderState *state, float timeline_frame, int chan_shown, ListBase *channels, ListBase *seqbasep) { - SeqRenderState state; - return seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chan_shown); + return seq_render_strip_stack(context, state, channels, seqbasep, timeline_frame, chan_shown); } ImBuf *render_give_ibuf_direct(const RenderData *context, float timeline_frame, Strip *strip) diff --git a/source/blender/sequencer/intern/render.hh b/source/blender/sequencer/intern/render.hh index 437377825eb..144fd51ebc6 100644 --- a/source/blender/sequencer/intern/render.hh +++ b/source/blender/sequencer/intern/render.hh @@ -9,6 +9,7 @@ */ #include "BLI_math_vector_types.hh" +#include "BLI_set.hh" #include "BLI_vector.hh" struct ImBuf; @@ -24,6 +25,7 @@ namespace blender::seq { /* Mutable state while rendering one sequencer frame. */ struct SeqRenderState { LinkNode *scene_parents = nullptr; + Set strips_rendering_seqbase; }; /* Strip corner coordinates in screen pixel space. Note that they might not be @@ -38,6 +40,7 @@ struct StripScreenQuad { }; ImBuf *seq_render_give_ibuf_seqbase(const RenderData *context, + SeqRenderState *state, float timeline_frame, int chan_shown, ListBase *channels, diff --git a/tests/files/sequence_editing/effects/recursive_render_crash_146484.blend b/tests/files/sequence_editing/effects/recursive_render_crash_146484.blend new file mode 100644 index 00000000000..05cda8b9913 --- /dev/null +++ b/tests/files/sequence_editing/effects/recursive_render_crash_146484.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ea97c8a65733cefe820f171d22d0bea91498401491eb0bb857c118554ff7d34 +size 278090 diff --git a/tests/files/sequence_editing/effects/reference/recursive_render_crash_146484.png b/tests/files/sequence_editing/effects/reference/recursive_render_crash_146484.png new file mode 100644 index 00000000000..208532a1a5c --- /dev/null +++ b/tests/files/sequence_editing/effects/reference/recursive_render_crash_146484.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a8575461ef5880eec3b52544c72f11b58b3462b1db029537fffd1db6ba26378 +size 1799