VSE: speedup timeline drawing, and improve waveform display #115311

Merged
Aras Pranckevicius merged 17 commits from aras_p/blender:vse-draw-opt into main 2023-11-29 20:25:30 +01:00
10 changed files with 701 additions and 575 deletions

View File

@ -32,6 +32,7 @@ set(SRC
sequencer_preview.cc
sequencer_preview_draw.cc
sequencer_proxy.cc
sequencer_quads_batch.cc
sequencer_retiming.cc
sequencer_retiming_draw.cc
sequencer_scopes.cc
@ -42,6 +43,7 @@ set(SRC
space_sequencer.cc
sequencer_intern.hh
sequencer_quads_batch.hh
)
set(LIB

View File

@ -15,6 +15,7 @@
/* Internal exports only. */
class SeqQuadsBatch;
struct ARegion;
struct ARegionType;
struct Depsgraph;
@ -317,7 +318,7 @@ int sequencer_retiming_select_all_exec(bContext *C, wmOperator *op);
int sequencer_retiming_box_select_exec(bContext *C, wmOperator *op);
/* `sequencer_retiming_draw.cc` */
void sequencer_draw_retiming(const bContext *C);
void sequencer_draw_retiming(const bContext *C, SeqQuadsBatch *quads);
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C);
SeqRetimingKey *try_to_realize_virtual_key(const bContext *C, Sequence *seq, const int mval[2]);
SeqRetimingKey *retiming_mousover_key_get(const bContext *C, const int mval[2], Sequence **r_seq);

View File

@ -0,0 +1,167 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spseq
*/
#include "sequencer_quads_batch.hh"
#include "BLI_color.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_batch.h"
#include "GPU_index_buffer.h"
#include "GPU_vertex_buffer.h"
struct ColorVertex {
blender::float2 pos;
blender::ColorTheme4b color;
};
static_assert(sizeof(ColorVertex) == 12);
static GPUIndexBuf *create_quads_index_buffer(int quads_count)
{
GPUIndexBufBuilder elb;
GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, quads_count * 2, quads_count * 4);
for (int i = 0; i < quads_count; i++) {
const uint i0 = i * 4 + 0;
const uint i1 = i * 4 + 1;
const uint i2 = i * 4 + 2;
const uint i3 = i * 4 + 3;
GPU_indexbuf_add_tri_verts(&elb, i0, i1, i2);
GPU_indexbuf_add_tri_verts(&elb, i2, i1, i3);
}
return GPU_indexbuf_build(&elb);
}
SeqQuadsBatch::SeqQuadsBatch()
{
ibo_quads = create_quads_index_buffer(MAX_QUADS);
GPUVertFormat format;
GPU_vertformat_clear(&format);
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
vbo_quads = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STREAM);
GPU_vertbuf_data_alloc(vbo_quads, MAX_QUADS * 4);
vbo_lines = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STREAM);
GPU_vertbuf_data_alloc(vbo_lines, MAX_LINES * 2);
batch_quads = GPU_batch_create_ex(
GPU_PRIM_TRIS, vbo_quads, ibo_quads, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
GPU_batch_program_set_builtin(batch_quads, GPU_SHADER_3D_FLAT_COLOR);
batch_lines = GPU_batch_create_ex(GPU_PRIM_LINES, vbo_lines, nullptr, GPU_BATCH_OWNS_VBO);
GPU_batch_program_set_builtin(batch_lines, GPU_SHADER_3D_FLAT_COLOR);
}
SeqQuadsBatch::~SeqQuadsBatch()
{
BLI_assert_msg(quads_num == 0 && lines_num == 0,
"SeqQuadsBatch is being destroyed without drawing quads/lines it contains");
GPU_batch_discard(batch_quads);
GPU_batch_discard(batch_lines);
}
void SeqQuadsBatch::draw()
{
if (quads_num > 0) {
GPU_vertbuf_tag_dirty(vbo_quads);
GPU_vertbuf_use(vbo_quads);
GPU_batch_draw_range(batch_quads, 0, quads_num * 6);
quads_num = 0;
verts_quads = nullptr;
}
if (lines_num > 0) {
GPU_vertbuf_tag_dirty(vbo_lines);
GPU_vertbuf_use(vbo_lines);
GPU_batch_draw_range(batch_lines, 0, lines_num * 2);
lines_num = 0;
verts_lines = nullptr;
}
}
void SeqQuadsBatch::add_quad(float x1,
float y1,
float x2,
float y2,
float x3,
float y3,
float x4,
float y4,
const uchar color[4])
{
if (quads_num >= MAX_QUADS) {
draw();
}
if (quads_num == 0) {
verts_quads = static_cast<ColorVertex *>(GPU_vertbuf_get_data(vbo_quads));
BLI_assert(verts_quads != nullptr);
}
aras_p marked this conversation as resolved
Review

Do you need to clear the flag, because you are reusing the buffer? I would expect that there is some function to handle this.

Here it seems to work when I remove call to GPU_vertbuf_attr_get_raw_data(). Would have to look how exactly this flag is used.

Do you need to clear the flag, because you are reusing the buffer? I would expect that there is some function to handle this. Here it seems to work when I remove call to `GPU_vertbuf_attr_get_raw_data()`. Would have to look how exactly this flag is used.
ColorVertex v0 = {blender::float2(x1, y1), color};
ColorVertex v1 = {blender::float2(x2, y2), color};
ColorVertex v2 = {blender::float2(x3, y3), color};
aras_p marked this conversation as resolved Outdated
Use `static_cast` for casting types, see https://wiki.blender.org/wiki/Style_Guide/C_Cpp#C.2B.2B_Type_Cast
ColorVertex v3 = {blender::float2(x4, y4), color};
*verts_quads++ = v0;
*verts_quads++ = v1;
*verts_quads++ = v2;
*verts_quads++ = v3;
quads_num++;
}
void SeqQuadsBatch::add_wire_quad(float x1, float y1, float x2, float y2, const uchar color[4])
{
if (lines_num + 4 > MAX_LINES) {
draw();
}
if (lines_num == 0) {
verts_lines = static_cast<ColorVertex *>(GPU_vertbuf_get_data(vbo_lines));
BLI_assert(verts_lines != nullptr);
}
ColorVertex v0 = {blender::float2(x1, y1), color};
ColorVertex v1 = {blender::float2(x1, y2), color};
ColorVertex v2 = {blender::float2(x2, y1), color};
ColorVertex v3 = {blender::float2(x2, y2), color};
/* Left */
*verts_lines++ = v0;
*verts_lines++ = v1;
/* Right */
*verts_lines++ = v2;
*verts_lines++ = v3;
/* Bottom */
*verts_lines++ = v0;
*verts_lines++ = v2;
/* Top */
*verts_lines++ = v1;
*verts_lines++ = v3;
lines_num += 4;
}
void SeqQuadsBatch::add_line(float x1, float y1, float x2, float y2, const uchar color[4])
{
if (lines_num + 1 > MAX_LINES) {
draw();
}
if (lines_num == 0) {
verts_lines = static_cast<ColorVertex *>(GPU_vertbuf_get_data(vbo_lines));
BLI_assert(verts_lines != nullptr);
}
ColorVertex v0 = {blender::float2(x1, y1), color};
ColorVertex v1 = {blender::float2(x2, y2), color};
*verts_lines++ = v0;
*verts_lines++ = v1;
lines_num++;
}

View File

@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spseq
*/
#pragma once
#include "BLI_sys_types.h"
struct GPUVertBuf;
struct GPUIndexBuf;
struct GPUBatch;
struct ColorVertex;
/**
* Flat-colored 2D geometry draw batching utility.
*
* Internally uses #GPU_SHADER_3D_FLAT_COLOR to draw single-colored rectangles, quads
* or lines. After adding a number of primitives with #add_quad, #add_wire_quad, #add_line,
* draw them using #draw. Note that #draw can be called behind the scenes if number of primitives
* is larger than the internal batch buffer size.
*/
class SeqQuadsBatch {
public:
SeqQuadsBatch();
~SeqQuadsBatch();
/** Draw all the previously added primitives. */
void draw();
/** Add an axis-aligned quad. */
void add_quad(float x1, float y1, float x2, float y2, const uchar color[4])
{
add_quad(x1, y1, x1, y2, x2, y1, x2, y2, color);
}
/** Add a quad. */
void add_quad(float x1,
float y1,
float x2,
float y2,
float x3,
float y3,
float x4,
float y4,
const uchar color[4]);
/** Add four lines of an axis-aligned quad edges. */
void add_wire_quad(float x1, float y1, float x2, float y2, const uchar color[4]);
/** Add a line. */
void add_line(float x1, float y1, float x2, float y2, const uchar color[4]);
private:
static constexpr int MAX_QUADS = 1024;
static constexpr int MAX_LINES = 4096;
GPUVertBuf *vbo_quads = nullptr;
GPUIndexBuf *ibo_quads = nullptr;
GPUBatch *batch_quads = nullptr;
ColorVertex *verts_quads = nullptr;
int quads_num = 0;
GPUVertBuf *vbo_lines = nullptr;
GPUBatch *batch_lines = nullptr;
ColorVertex *verts_lines = nullptr;
int lines_num = 0;
};

View File

@ -53,6 +53,7 @@
/* Own include. */
#include "sequencer_intern.hh"
#include "sequencer_quads_batch.hh"
#define KEY_SIZE (10 * U.pixelsize)
#define KEY_CENTER (UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 4 + KEY_SIZE / 2)
@ -261,7 +262,11 @@ SeqRetimingKey *retiming_mousover_key_get(const bContext *C, const int mval[2],
/** \name Retiming Key
* \{ */
static void retime_key_draw(const bContext *C, const Sequence *seq, const SeqRetimingKey *key)
static void retime_key_draw(const bContext *C,
const Sequence *seq,
const SeqRetimingKey *key,
const KeyframeShaderBindings &sh_bindings,
const blender::Map<SeqRetimingKey *, Sequence *> &selection)
{
const Scene *scene = CTX_data_scene(C);
const float key_x = key_x_get(scene, seq, key);
@ -272,24 +277,6 @@ static void retime_key_draw(const bContext *C, const Sequence *seq, const SeqRet
return; /* Key out of the strip bounds. */
}
GPUVertFormat *format = immVertexFormat();
KeyframeShaderBindings sh_bindings;
sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
sh_bindings.color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, 1);
eBezTriple_KeyframeType key_type = BEZT_KEYTYPE_KEYFRAME;
if (SEQ_retiming_key_is_freeze_frame(key)) {
key_type = BEZT_KEYTYPE_BREAKDOWN;
@ -298,7 +285,7 @@ static void retime_key_draw(const bContext *C, const Sequence *seq, const SeqRet
key_type = BEZT_KEYTYPE_MOVEHOLD;
}
const bool is_selected = SEQ_retiming_selection_contains(SEQ_editing_get(scene), key);
const bool is_selected = selection.contains(const_cast<SeqRetimingKey *>(key));
const int size = KEY_SIZE;
const float bottom = KEY_CENTER;
@ -323,17 +310,16 @@ static void retime_key_draw(const bContext *C, const Sequence *seq, const SeqRet
&sh_bindings,
0,
0);
immEnd();
GPU_program_point_size(false);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
static void draw_continuity(const bContext *C, const Sequence *seq, const SeqRetimingKey *key)
static void draw_continuity(const bContext *C,
const Sequence *seq,
const SeqRetimingKey *key,
const blender::Map<SeqRetimingKey *, Sequence *> &selection,
aras_p marked this conversation as resolved Outdated

Sorry, I have noticed this only now, you should avoid passing pointers to objects, pass reference instead.

So argument would be const blender::Set<const SeqRetimingKey *> &selection

Sorry, I have noticed this only now, you should avoid passing pointers to objects, pass reference instead. So argument would be `const blender::Set<const SeqRetimingKey *> &selection`
SeqQuadsBatch *quads)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
if (key_x_get(scene, seq, key) == SEQ_time_left_handle_frame_get(scene, seq) ||
key->strip_frame_index == 0)
@ -358,29 +344,34 @@ static void draw_continuity(const bContext *C, const Sequence *seq, const SeqRet
const float bottom = y_center - size * width_fac;
const float top = y_center + size * width_fac;
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
uchar color[4];
if (SEQ_retiming_data_is_editable(seq) &&
(SEQ_retiming_selection_contains(ed, key) || SEQ_retiming_selection_contains(ed, key - 1)))
(selection.contains(const_cast<SeqRetimingKey *>(key)) ||
selection.contains(const_cast<SeqRetimingKey *>(key - 1))))
{
immUniform4f("color", 0.65f, 0.5f, 0.2f, 1.0f);
color[0] = 166;
color[1] = 127;
color[2] = 51;
color[3] = 255;
}
else {
immUniform4f("color", 0.0f, 0.0f, 0.0f, 0.1f);
color[0] = 0;
color[1] = 0;
color[2] = 0;
color[3] = 25;
}
immRectf(pos, prev_key_position, bottom, key_position, top);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
quads->add_quad(prev_key_position, bottom, key_position, top, color);
}
/* If there are no keys, draw fake keys and create real key when they are selected. */
/* TODO: would be nice to draw continuity between fake keys. */
static void fake_keys_draw(const bContext *C, Sequence *seq)
static bool fake_keys_draw(const bContext *C,
Sequence *seq,
const KeyframeShaderBindings &sh_bindings,
const blender::Map<SeqRetimingKey *, Sequence *> &selection)
{
if (!SEQ_retiming_is_active(seq) && !SEQ_retiming_data_is_editable(seq)) {
return;
return false;
}
const Scene *scene = CTX_data_scene(C);
@ -391,18 +382,19 @@ static void fake_keys_draw(const bContext *C, Sequence *seq)
SeqRetimingKey fake_key;
fake_key.strip_frame_index = left_key_frame - SEQ_time_start_frame_get(seq);
fake_key.flag = 0;
retime_key_draw(C, seq, &fake_key);
retime_key_draw(C, seq, &fake_key, sh_bindings, selection);
}
if (SEQ_retiming_key_get_by_timeline_frame(scene, seq, right_key_frame) == nullptr) {
SeqRetimingKey fake_key;
fake_key.strip_frame_index = right_key_frame - SEQ_time_start_frame_get(seq);
fake_key.flag = 0;
retime_key_draw(C, seq, &fake_key);
retime_key_draw(C, seq, &fake_key, sh_bindings, selection);
}
return true;
}
static void retime_keys_draw(const bContext *C)
static void retime_keys_draw(const bContext *C, SeqQuadsBatch *quads)
{
const Scene *scene = CTX_data_scene(C);
if (scene->ed == nullptr) {
@ -413,22 +405,75 @@ static void retime_keys_draw(const bContext *C)
return;
}
/* Get the selection here once, for faster "is key selected?" lookups later. */
blender::Map<SeqRetimingKey *, Sequence *> selection = SEQ_retiming_selection_get(scene->ed);
wmOrtho2_region_pixelspace(CTX_wm_region(C));
for (Sequence *seq : sequencer_visible_strips_get(C)) {
blender::Vector<Sequence *> strips = sequencer_visible_strips_get(C);
/* Draw all continuity sections. */
GPU_blend(GPU_BLEND_ALPHA);
for (Sequence *seq : strips) {
if (!SEQ_retiming_is_allowed(seq)) {
aras_p marked this conversation as resolved Outdated

Can't you just pass map returnd by SEQ_retiming_selection_get() and use contains() method? This seems like unnecessary code duplication.

Can't you just pass map returnd by `SEQ_retiming_selection_get()` and use `contains()` method? This seems like unnecessary code duplication.

That's what I tried initially, but SEQ_retiming_selection_get returns non-const Sequence pointers as map keys, and my C++-fu is not strong enough to find the magic incantation, how to use contains() when I have a const-Sequence pointer. contains_as() with const pointer does not compile, at least on visual studio.

That's what I tried initially, but SEQ_retiming_selection_get returns non-const Sequence pointers as map keys, and my C++-fu is not strong enough to find the magic incantation, how to use `contains()` when I have a const-Sequence pointer. `contains_as()` with const pointer does not compile, at least on visual studio.

I am not great with C++ either, but you can use non-const SeqRetimingKey for iteration.

There is also const_cast which seems to work, but not sure if it is good idea to use it in this case.

I am not great with C++ either, but you can use non-const `SeqRetimingKey` for iteration. There is also `const_cast` which seems to work, but not sure if it is good idea to use it in this case.
continue;
}
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
draw_continuity(C, seq, &key);
}
fake_keys_draw(C, seq);
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
retime_key_draw(C, seq, &key);
draw_continuity(C, seq, &key, selection, quads);
}
}
quads->draw();
/* Draw all keys. */
const View2D *v2d = UI_view2d_fromcontext(C);
GPUVertFormat *format = immVertexFormat();
KeyframeShaderBindings sh_bindings;
sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
sh_bindings.color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
constexpr int MAX_KEYS_IN_BATCH = 1024;
int point_counter = 0;
immBeginAtMost(GPU_PRIM_POINTS, MAX_KEYS_IN_BATCH);
for (Sequence *seq : strips) {
if (!SEQ_retiming_is_allowed(seq)) {
continue;
}
if (fake_keys_draw(C, seq, sh_bindings, selection)) {
point_counter += 2;
}
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
retime_key_draw(C, seq, &key, sh_bindings, selection);
point_counter++;
aras_p marked this conversation as resolved Outdated

I could have specified this a bit more, you can have just 1 condition
if (point_counter + 3 > MAX_KEYS_IN_BATCH) { after retime_key_draw()

This way batch should never be overfilled, and from what I read, with immBeginAtMost() you can always draw less vertices.

I could have specified this a bit more, you can have just 1 condition `if (point_counter + 3 > MAX_KEYS_IN_BATCH) {` after `retime_key_draw()` This way batch should never be overfilled, and from what I read, with `immBeginAtMost()` you can always draw less vertices.
/* Next key plus possible two fake keys for next sequence would need at
* most 3 points, so restart the batch if we're close to that. */
if (point_counter + 3 >= MAX_KEYS_IN_BATCH) {
immEnd();
immBeginAtMost(GPU_PRIM_POINTS, MAX_KEYS_IN_BATCH);
point_counter = 0;
}
}
}
immEnd();
GPU_program_point_size(false);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
/** \} */
@ -550,8 +595,8 @@ static void retime_speed_draw(const bContext *C)
/** \} */
void sequencer_draw_retiming(const bContext *C)
void sequencer_draw_retiming(const bContext *C, SeqQuadsBatch *quads)
{
retime_keys_draw(C);
retime_keys_draw(C, quads);
retime_speed_draw(C);
}

View File

@ -1517,13 +1517,10 @@ static void rna_Sequence_separate(ID *id, Sequence *seqm, Main *bmain)
/* Find channel owner. If nullptr, owner is `Editing`, otherwise it's `Sequence`. */
static Sequence *rna_SeqTimelineChannel_owner_get(Editing *ed, SeqTimelineChannel *channel)
{
blender::VectorSet strips = SEQ_query_all_strips_recursive(&ed->seqbase);
blender::VectorSet strips = SEQ_query_all_meta_strips_recursive(&ed->seqbase);
Sequence *channel_owner = nullptr;
for (Sequence *seq : strips) {
if (seq->type != SEQ_TYPE_META) {
continue;
}
if (BLI_findindex(&seq->channels, channel) != -1) {
channel_owner = seq;
break;

View File

@ -87,6 +87,13 @@ blender::VectorSet<Sequence *> SEQ_query_all_strips(ListBase *seqbase);
* \return set of strips
*/
blender::VectorSet<Sequence *> SEQ_query_all_strips_recursive(ListBase *seqbase);
/**
* Query all meta strips in seqbase and nested meta strips.
*
* \param seqbase: ListBase in which strips are queried
* \return set of meta strips
*/
blender::VectorSet<Sequence *> SEQ_query_all_meta_strips_recursive(ListBase *seqbase);
/**
* Query all effect strips that are directly or indirectly connected to seq_reference.

View File

@ -92,6 +92,16 @@ static void query_all_strips_recursive(ListBase *seqbase, VectorSet<Sequence *>
}
}
static void query_all_meta_strips_recursive(ListBase *seqbase, VectorSet<Sequence *> &strips)
{
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (seq->type == SEQ_TYPE_META) {
query_all_meta_strips_recursive(&seq->seqbase, strips);
strips.add(seq);
}
}
}
VectorSet<Sequence *> SEQ_query_all_strips_recursive(ListBase *seqbase)
{
VectorSet<Sequence *> strips;
@ -99,6 +109,13 @@ VectorSet<Sequence *> SEQ_query_all_strips_recursive(ListBase *seqbase)
return strips;
}
VectorSet<Sequence *> SEQ_query_all_meta_strips_recursive(ListBase *seqbase)
{
VectorSet<Sequence *> strips;
query_all_meta_strips_recursive(seqbase, strips);
return strips;
}
VectorSet<Sequence *> SEQ_query_all_strips(ListBase *seqbase)
{
VectorSet<Sequence *> strips;

View File

@ -387,10 +387,10 @@ ListBase *SEQ_get_seqbase_by_seq(const Scene *scene, Sequence *seq)
Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase)
{
blender::VectorSet strips = SEQ_query_all_strips_recursive(seqbase_main);
blender::VectorSet strips = SEQ_query_all_meta_strips_recursive(seqbase_main);
for (Sequence *seq : strips) {
if (seq->type == SEQ_TYPE_META && &seq->seqbase == meta_seqbase) {
if (&seq->seqbase == meta_seqbase) {
return seq;
}
}