VSE: Cleanup speed effect math

Simplify logic of speed effect frame calculation by using discrete math
where possible. Only `SEQ_SPEED_MULTIPLY` mode with animation requires
frame map to be built. Frame map building was simplified by removing
unused branches.

Functional change: Animating strip in negative range will reverse playback.
I assume this was limitation of previous system, where each frame map item
was limited to be within correct frame range. Now frame map can contain
values that point beyond usable range and they are limited by
`seq_speed_effect_target_frame_get`. This way it is possible to control
playback rate in both directions.

Mostly fixes T89120 apart from offset handling.

Reviewed By: mano-wii

Differential Revision: https://developer.blender.org/D11939
This commit is contained in:
2021-08-24 01:01:48 +02:00
parent a57ba4147f
commit 929d7597b3
6 changed files with 86 additions and 151 deletions

View File

@@ -1176,11 +1176,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
seq->flag |= SEQ_EFFECT_NOT_LOADED; seq->flag |= SEQ_EFFECT_NOT_LOADED;
} }
if (seq->type == SEQ_TYPE_SPEED) {
SpeedControlVars *s = seq->effectdata;
s->frameMap = NULL;
}
if (seq->type == SEQ_TYPE_TEXT) { if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata; TextVars *t = seq->effectdata;
t->text_blf_id = SEQ_FONT_NOT_LOADED; t->text_blf_id = SEQ_FONT_NOT_LOADED;

View File

@@ -339,11 +339,8 @@ typedef struct SpeedControlVars {
float *frameMap; float *frameMap;
/* DEPRECATED, only used for versioning. */ /* DEPRECATED, only used for versioning. */
float globalSpeed; float globalSpeed;
/* DEPRECATED, only used for versioning. */
int flags; int flags;
int length;
int lastValidFrame;
int speed_control_type; int speed_control_type;
float speed_fader; float speed_fader;

View File

@@ -3069,8 +3069,6 @@ static void init_speed_effect(Sequence *seq)
seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars"); seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
v = (SpeedControlVars *)seq->effectdata; v = (SpeedControlVars *)seq->effectdata;
v->frameMap = NULL;
v->length = 0;
v->speed_control_type = SEQ_SPEED_STRETCH; v->speed_control_type = SEQ_SPEED_STRETCH;
v->speed_fader = 1.0f; v->speed_fader = 1.0f;
v->speed_fader_length = 0.0f; v->speed_fader_length = 0.0f;
@@ -3080,9 +3078,7 @@ static void init_speed_effect(Sequence *seq)
static void load_speed_effect(Sequence *seq) static void load_speed_effect(Sequence *seq)
{ {
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata; SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
v->frameMap = NULL; v->frameMap = NULL;
v->length = 0;
} }
static int num_inputs_speed(void) static int num_inputs_speed(void)
@@ -3105,7 +3101,6 @@ static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(fla
dst->effectdata = MEM_dupallocN(src->effectdata); dst->effectdata = MEM_dupallocN(src->effectdata);
v = (SpeedControlVars *)dst->effectdata; v = (SpeedControlVars *)dst->effectdata;
v->frameMap = NULL; v->frameMap = NULL;
v->length = 0;
} }
static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1)) static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
@@ -3127,164 +3122,112 @@ static int seq_effect_speed_get_strip_content_length(const Sequence *seq)
return seq->len; return seq->len;
} }
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) static FCurve *seq_effect_speed_speed_factor_curve_get(Scene *scene, Sequence *seq)
{ {
int timeline_frame; return id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
float fallback_fac = 1.0f; }
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
FCurve *fcu = NULL;
/* if not already done, load / initialize data */ /* Build frame map when speed in mode #SEQ_SPEED_MULTIPLY is animated.
SEQ_effect_handle_get(seq); * This is, because `target_frame` value is integrated over time. */
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq)
if ((force == false) && (seq->len == v->length) && (v->frameMap != NULL)) { {
return;
}
if ((seq->seq1 == NULL) || (seq->len < 1)) { if ((seq->seq1 == NULL) || (seq->len < 1)) {
/* make coverity happy and check for (CID 598) input strip ... */ return; /* Make coverity happy and check for (CID 598) input strip... */
}
FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq);
if (fcu == NULL) {
return; return;
} }
/* XXX(campbell): new in 2.5x. should we use the animation system this way? SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
* The fcurve is needed because many frames need evaluating at once. */
switch (v->speed_control_type) {
case SEQ_SPEED_MULTIPLY: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
break;
}
case SEQ_SPEED_FRAME_NUMBER: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_frame_number", 0, NULL);
break;
}
case SEQ_SPEED_LENGTH: {
fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_length", 0, NULL);
break;
}
}
if (!v->frameMap || v->length != seq->len) {
if (v->frameMap) { if (v->frameMap) {
MEM_freeN(v->frameMap); MEM_freeN(v->frameMap);
} }
v->length = seq->len; const int effect_strip_length = seq->enddisp - seq->startdisp;
v->frameMap = MEM_mallocN(sizeof(float) * effect_strip_length, __func__);
v->frameMap[0] = 0.0f;
v->frameMap = MEM_callocN(sizeof(float) * v->length, "speedcontrol frameMap"); float target_frame = 0;
for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
target_frame += evaluate_fcurve(fcu, seq->startdisp + frame_index);
v->frameMap[frame_index] = target_frame;
}
}
static void seq_effect_speed_frame_map_ensure(Scene *scene, Sequence *seq, FCurve *fcu)
{
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
if (v->frameMap != NULL) {
return;
} }
fallback_fac = 1.0; seq_effect_speed_rebuild_map(scene, seq);
const int target_strip_length = seq_effect_speed_get_strip_content_length(seq->seq1);
if (v->speed_control_type == SEQ_SPEED_STRETCH) {
if ((seq->seq1->enddisp != seq->seq1->start) && (target_strip_length != 0)) {
fallback_fac = (float)target_strip_length / (float)(seq->seq1->enddisp - seq->seq1->start);
fcu = NULL;
}
}
else {
/* if there is no fcurve, use value as simple multiplier */
if (!fcu) {
switch (v->speed_control_type) {
case SEQ_SPEED_MULTIPLY: {
fallback_fac = v->speed_fader;
break;
}
case SEQ_SPEED_FRAME_NUMBER: {
fallback_fac = v->speed_fader_frame_number;
break;
}
case SEQ_SPEED_LENGTH: {
fallback_fac = v->speed_fader_length;
break;
}
}
}
}
if (ELEM(v->speed_control_type, SEQ_SPEED_MULTIPLY, SEQ_SPEED_STRETCH)) {
float cursor = 0;
float facf;
v->frameMap[0] = 0;
v->lastValidFrame = 0;
for (timeline_frame = 1; timeline_frame < v->length; timeline_frame++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + timeline_frame);
}
else {
facf = fallback_fac;
}
cursor += facf;
if (cursor >= target_strip_length) {
v->frameMap[timeline_frame] = target_strip_length - 1;
}
else {
v->frameMap[timeline_frame] = cursor;
v->lastValidFrame = timeline_frame;
}
}
}
else {
float facf;
v->lastValidFrame = 0;
for (timeline_frame = 0; timeline_frame < v->length; timeline_frame++) {
if (fcu) {
facf = evaluate_fcurve(fcu, seq->startdisp + timeline_frame);
}
else {
facf = fallback_fac;
}
if (v->speed_control_type == SEQ_SPEED_LENGTH) {
facf *= target_strip_length;
facf /= 100.0f;
}
if (facf >= target_strip_length) {
facf = target_strip_length - 1;
}
else {
v->lastValidFrame = timeline_frame;
}
v->frameMap[timeline_frame] = facf;
}
}
} }
/* Override timeline_frame when rendering speed effect input. */ /* Override timeline_frame when rendering speed effect input. */
float seq_speed_effect_target_frame_get(const SeqRenderData *context, float seq_speed_effect_target_frame_get(Scene *scene,
Sequence *seq, Sequence *seq_speed,
float timeline_frame, float timeline_frame,
int input) int input)
{ {
int frame_index = seq_give_frame_index(seq, timeline_frame); if (seq_speed->seq1 == NULL) {
SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; return 0.0f;
seq_effect_speed_rebuild_map(context->scene, seq, false); }
SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
int frame_index = seq_give_frame_index(seq_speed, timeline_frame);
SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
const Sequence *source = seq_speed->seq1;
float target_frame = 0.0f;
switch (s->speed_control_type) {
case SEQ_SPEED_STRETCH:
const float target_content_length = seq_effect_speed_get_strip_content_length(source);
const float target_strip_length = source->enddisp - source->startdisp;
const float ratio = target_content_length / target_strip_length;
target_frame = frame_index * ratio;
break;
case SEQ_SPEED_MULTIPLY:
FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq_speed);
if (fcu != NULL) {
seq_effect_speed_frame_map_ensure(scene, seq_speed, fcu);
target_frame = s->frameMap[frame_index];
}
else {
target_frame = frame_index * s->speed_fader;
}
break;
case SEQ_SPEED_LENGTH:
target_frame = seq_effect_speed_get_strip_content_length(source) *
(s->speed_fader_length / 100.0f);
break;
case SEQ_SPEED_FRAME_NUMBER:
target_frame = s->speed_fader_frame_number;
break;
}
CLAMP(target_frame, 0, seq_effect_speed_get_strip_content_length(source));
target_frame += seq_speed->start;
/* No interpolation. */ /* No interpolation. */
if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) { if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
return seq->start + s->frameMap[frame_index]; return target_frame;
} }
/* We need to provide current and next image for interpolation. */ /* Interpolation is used, switch between current and next frame based on which input is
if (input == 0) { /* Current frame. */ * requested. */
return floor(seq->start + s->frameMap[frame_index]); return input == 0 ? target_frame : ceil(target_frame);
}
/* Next frame. */
return ceil(seq->start + s->frameMap[frame_index]);
} }
static float speed_effect_interpolation_ratio_get(SpeedControlVars *s, static float speed_effect_interpolation_ratio_get(Scene *scene,
Sequence *seq, Sequence *seq_speed,
float timeline_frame) float timeline_frame)
{ {
int frame_index = seq_give_frame_index(seq, timeline_frame); const float target_frame = seq_speed_effect_target_frame_get(
return s->frameMap[frame_index] - floor(s->frameMap[frame_index]); scene, seq_speed, timeline_frame, 0);
return target_frame - floor(target_frame);
} }
static ImBuf *do_speed_effect(const SeqRenderData *context, static ImBuf *do_speed_effect(const SeqRenderData *context,
@@ -3302,7 +3245,7 @@ static ImBuf *do_speed_effect(const SeqRenderData *context,
if (s->flags & SEQ_SPEED_USE_INTERPOLATION) { if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3); out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3);
facf0 = facf1 = speed_effect_interpolation_ratio_get(s, seq, timeline_frame); facf0 = facf1 = speed_effect_interpolation_ratio_get(context->scene, seq, timeline_frame);
/* Current frame is ibuf1, next frame is ibuf2. */ /* Current frame is ibuf1, next frame is ibuf2. */
out = seq_render_effect_execute_threaded( out = seq_render_effect_execute_threaded(
&cross_effect, context, NULL, timeline_frame, facf0, facf1, ibuf1, ibuf2, ibuf3); &cross_effect, context, NULL, timeline_frame, facf0, facf1, ibuf1, ibuf2, ibuf3);

View File

@@ -39,8 +39,8 @@ struct Sequence;
*/ */
struct SeqEffectHandle seq_effect_get_sequence_blend(struct Sequence *seq); struct SeqEffectHandle seq_effect_get_sequence_blend(struct Sequence *seq);
void seq_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq, bool force); void seq_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq);
float seq_speed_effect_target_frame_get(const struct SeqRenderData *context, float seq_speed_effect_target_frame_get(struct Scene *scene,
struct Sequence *seq, struct Sequence *seq,
float timeline_frame, float timeline_frame,
int input); int input);

View File

@@ -840,7 +840,7 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context,
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
/* Speed effect requires time remapping of `timeline_frame` for input(s). */ /* Speed effect requires time remapping of `timeline_frame` for input(s). */
if (input[0] && seq->type == SEQ_TYPE_SPEED) { if (input[0] && seq->type == SEQ_TYPE_SPEED) {
float target_frame = seq_speed_effect_target_frame_get(context, seq, timeline_frame, i); float target_frame = seq_speed_effect_target_frame_get(scene, seq, timeline_frame, i);
ibuf[i] = seq_render_strip(context, state, input[0], target_frame); ibuf[i] = seq_render_strip(context, state, input[0], target_frame);
} }
else { /* Other effects. */ else { /* Other effects. */

View File

@@ -118,7 +118,7 @@ static void sequence_invalidate_cache(Scene *scene,
} }
if (seq->effectdata && seq->type == SEQ_TYPE_SPEED) { if (seq->effectdata && seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true); seq_effect_speed_rebuild_map(scene, seq);
} }
sequence_do_invalidate_dependent(scene, seq, &ed->seqbase); sequence_do_invalidate_dependent(scene, seq, &ed->seqbase);
@@ -268,7 +268,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
SEQ_relations_sequence_free_anim(seq); SEQ_relations_sequence_free_anim(seq);
} }
if (seq->type == SEQ_TYPE_SPEED) { if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true); seq_effect_speed_rebuild_map(scene, seq);
} }
} }
if (seq->type == SEQ_TYPE_META) { if (seq->type == SEQ_TYPE_META) {
@@ -325,7 +325,7 @@ static bool update_changed_seq_recurs(
SEQ_relations_sequence_free_anim(seq); SEQ_relations_sequence_free_anim(seq);
} }
else if (seq->type == SEQ_TYPE_SPEED) { else if (seq->type == SEQ_TYPE_SPEED) {
seq_effect_speed_rebuild_map(scene, seq, true); seq_effect_speed_rebuild_map(scene, seq);
} }
} }