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
This commit is contained in:
Richard Antalik
2025-09-25 08:48:56 +02:00
committed by Richard Antalik
parent 84559f8bd4
commit 97297bd167
17 changed files with 59 additions and 17 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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*/,

View File

@@ -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,

View File

@@ -139,6 +139,7 @@ static void gaussian_blur_y(const Span<float> gaussian,
}
static ImBuf *do_gaussian_blur_effect(const RenderData *context,
SeqRenderState * /*state*/,
Strip *strip,
float /*timeline_frame*/,
float /*fac*/,

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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*/,

View File

@@ -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;
}

View File

@@ -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*/,

View File

@@ -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*/,

View File

@@ -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,

View File

@@ -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)

View File

@@ -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<Strip *> 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,

Binary file not shown.

Binary file not shown.