Blender V5.0
strip_relations.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2003-2009 Blender Authors
3 * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later */
6
10
11#include "DNA_scene_types.h"
12#include "DNA_sequence_types.h"
13
14#include "BLI_ghash.h"
15#include "BLI_listbase.h"
16#include "BLI_math_base.h"
17#include "BLI_session_uid.h"
18
19#include "BKE_main.hh"
20#include "BKE_report.hh"
21
22#include "DEG_depsgraph.hh"
23
24#include "MOV_read.hh"
25
26#include "SEQ_iterator.hh"
27#include "SEQ_prefetch.hh"
28#include "SEQ_preview_cache.hh"
29#include "SEQ_relations.hh"
30#include "SEQ_sequencer.hh"
32#include "SEQ_time.hh"
33#include "SEQ_utils.hh"
34
38#include "effects/effects.hh"
39#include "sequencer.hh"
40#include "utils.hh"
41
42namespace blender::seq {
43
44bool relation_is_effect_of_strip(const Strip *effect, const Strip *input)
45{
46 return ELEM(input, effect->input1, effect->input2);
47}
48
57
62
64{
66}
67
69{
70 if (!(scene->ed->cache_flag & SEQ_CACHE_STORE_RAW)) {
71 /* RAW caches has been disabled, clear them out. */
73 }
74 if (!(scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT)) {
75 /* Final caches has been disabled, clear them out. */
77 }
78}
79
80bool is_cache_full(const Scene *scene)
81{
82 size_t cache_limit = size_t(U.memcachelimit) * 1024 * 1024;
84 cache_limit;
85}
86
88{
89 if (!is_cache_full(scene)) {
90 /* Cache is not full, we don't have to evict anything. */
91 return false;
92 }
93
94 /* Cache is full, so we want to remove some images. We always try to remove one final image,
95 * and some amount of source images for each final image, so that ratio of cached images
96 * stays the same. Depending on the frame composition complexity, there can be lots of
97 * source images cached for a single final frame; if we only removed one source image
98 * we'd eventually have the cache still filled only with source images. */
99 bool evicted_final = false;
100 bool evicted_source = false;
101 do {
102 const size_t count_final = final_image_cache_get_image_count(scene);
103 const size_t count_source = source_image_cache_get_image_count(scene);
104 evicted_final = false;
105 evicted_source = false;
106 const bool final_active = scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT;
107 /* Evict one final item, and as much from source as needed to maintain ratio. */
108 if (count_final != 0) {
109 evicted_final = final_image_cache_evict(scene);
110 }
111 /* Only remove source images if there's more of them than final ones. */
112 if (count_source != 0 && (!final_active || count_source > count_final)) {
113 evicted_source = source_image_cache_evict(scene);
114 /* Only try to enforce the ratio when the final cache is active. */
115 if (evicted_source && final_active) {
116 const size_t items = divide_ceil_ul(count_source, std::max<size_t>(count_final, 1));
117 /* Start at "1" to make sure we only try to evict more frames if the ratio is above 1:1. */
118 for (size_t i = 1; i < items; i++) {
119 if (!source_image_cache_evict(scene)) {
120 /* Can't evict any more frames, stop. */
121 break;
122 }
123 }
124 }
125 }
126
127 } while (is_cache_full(scene) && (evicted_final || evicted_source));
128
129 /* Did we evict anything to free up the cache? */
130 return !(evicted_final || evicted_source);
131}
132
133static void invalidate_final_cache_strip_range(Scene *scene, const Strip *strip)
134{
135 const int strip_left = time_left_handle_frame_get(scene, strip);
136 const int strip_right = time_right_handle_frame_get(scene, strip);
137 final_image_cache_invalidate_frame_range(scene, strip_left, strip_right);
138}
139
141{
142 Strip *meta = lookup_meta_by_strip(editing_get(scene), strip);
143 if (meta == nullptr) {
144 return;
145 }
146
148}
149
151{
153 relations_invalidate_cache(scene, strip);
154}
155
157{
158 if (strip->effectdata && strip->type == STRIP_TYPE_SPEED) {
159 strip_effect_speed_rebuild_map(scene, strip);
160 }
161
163
165 intra_frame_cache_invalidate(scene, strip);
168
169 /* Needed to update VSE sound. */
171 prefetch_stop(scene);
172}
173
174void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_target)
175{
176 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
177 if (scene->ed != nullptr) {
178 for (Strip *strip : lookup_strips_by_scene(editing_get(scene), scene_target)) {
179 relations_invalidate_cache_raw(scene, strip);
180 }
181 }
182 }
183}
184
185void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree)
186{
187 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
188 if (scene->ed != nullptr) {
189 for (Strip *strip : lookup_strips_by_compositor_node_group(editing_get(scene), node_tree)) {
190 relations_invalidate_cache(scene, strip);
191 }
192 }
193 }
194}
195
196static void invalidate_movieclip_strips(Scene *scene, MovieClip *clip_target, ListBase *seqbase)
197{
198 for (Strip *strip = static_cast<Strip *>(seqbase->first); strip != nullptr; strip = strip->next)
199 {
200 if (strip->clip == clip_target) {
201 relations_invalidate_cache_raw(scene, strip);
202 }
203
204 if (strip->seqbase.first != nullptr) {
205 invalidate_movieclip_strips(scene, clip_target, &strip->seqbase);
206 }
207 }
208}
209
211{
212 for (Scene *scene = static_cast<Scene *>(bmain->scenes.first); scene != nullptr;
213 scene = static_cast<Scene *>(scene->id.next))
214 {
215 if (scene->ed != nullptr) {
216 invalidate_movieclip_strips(scene, clip_target, &scene->ed->seqbase);
217 }
218 }
219}
220
221void relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
222{
223 if (scene->ed == nullptr) {
224 return;
225 }
226
227 cache_cleanup(scene);
228 prefetch_stop(scene);
229
230 LISTBASE_FOREACH (Strip *, strip, seqbase) {
231 if (for_render && time_strip_intersects_frame(scene, strip, scene->r.cfra)) {
232 continue;
233 }
234
235 if (strip->data) {
236 if (strip->type == STRIP_TYPE_MOVIE) {
238 }
239 if (strip->type == STRIP_TYPE_SPEED) {
240 strip_effect_speed_rebuild_map(scene, strip);
241 }
242 }
243 if (strip->type == STRIP_TYPE_META) {
244 relations_free_imbuf(scene, &strip->seqbase, for_render);
245 }
246 if (strip->type == STRIP_TYPE_SCENE) {
247 /* FIXME: recurse downwards,
248 * but do recurse protection somehow! */
249 }
250 }
251}
252
253static void sequencer_all_free_anim_ibufs(const Scene *scene,
254 ListBase *seqbase,
255 int timeline_frame,
256 const int frame_range[2])
257{
258 Editing *ed = editing_get(scene);
259 for (Strip *strip = static_cast<Strip *>(seqbase->first); strip != nullptr; strip = strip->next)
260 {
261 if (!time_strip_intersects_frame(scene, strip, timeline_frame) ||
262 !((frame_range[0] <= timeline_frame) && (frame_range[1] > timeline_frame)))
263 {
265 }
266 if (strip->type == STRIP_TYPE_META) {
267 int meta_range[2];
268
270 if (ms != nullptr && ms->parent_strip == strip) {
271 meta_range[0] = -MAXFRAME;
272 meta_range[1] = MAXFRAME;
273 }
274 else {
275 /* Limit frame range to meta strip. */
276 meta_range[0] = max_ii(frame_range[0], time_left_handle_frame_get(scene, strip));
277 meta_range[1] = min_ii(frame_range[1], time_right_handle_frame_get(scene, strip));
278 }
279
280 sequencer_all_free_anim_ibufs(scene, &strip->seqbase, timeline_frame, meta_range);
281 }
282 }
283}
284
285void relations_free_all_anim_ibufs(Scene *scene, int timeline_frame)
286{
287 Editing *ed = editing_get(scene);
288 if (ed == nullptr) {
289 return;
290 }
291
292 const int frame_range[2] = {-MAXFRAME, MAXFRAME};
293 sequencer_all_free_anim_ibufs(scene, &ed->seqbase, timeline_frame, frame_range);
294}
295
297{
298 LISTBASE_FOREACH (Strip *, strip, seqbase) {
299 if (strip->type == STRIP_TYPE_SCENE && strip->scene == scene) {
300 return strip;
301 }
302
303 if (strip->type == STRIP_TYPE_SCENE && (strip->flag & SEQ_SCENE_STRIPS)) {
304 if (strip->scene && strip->scene->ed &&
305 sequencer_check_scene_recursion(scene, &strip->scene->ed->seqbase))
306 {
307 return strip;
308 }
309 }
310
311 if (strip->type == STRIP_TYPE_META && sequencer_check_scene_recursion(scene, &strip->seqbase))
312 {
313 return strip;
314 }
315 }
316
317 return nullptr;
318}
319
321{
322 Editing *ed = editing_get(scene);
323 if (ed == nullptr) {
324 return false;
325 }
326
327 Strip *recursive_seq = sequencer_check_scene_recursion(scene, &ed->seqbase);
328
329 if (recursive_seq != nullptr) {
330 BKE_reportf(reports,
332 "Recursion detected in video sequencer. Strip %s at frame %d will not be rendered",
333 recursive_seq->name + 2,
334 time_left_handle_frame_get(scene, recursive_seq));
335
336 LISTBASE_FOREACH (Strip *, strip, &ed->seqbase) {
337 if (strip->type != STRIP_TYPE_SCENE && sequencer_strip_generates_image(strip)) {
338 /* There are other strips to render, so render them. */
339 return false;
340 }
341 }
342 /* No other strips to render - cancel operator. */
343 return true;
344 }
345
346 return false;
347}
348
349bool relations_render_loop_check(Strip *strip_main, Strip *strip)
350{
351 if (strip_main == nullptr || strip == nullptr) {
352 return false;
353 }
354
355 if (strip_main == strip) {
356 return true;
357 }
358
359 if ((strip_main->input1 && relations_render_loop_check(strip_main->input1, strip)) ||
360 (strip_main->input2 && relations_render_loop_check(strip_main->input2, strip)))
361 {
362 return true;
363 }
364
365 LISTBASE_FOREACH (StripModifierData *, smd, &strip_main->modifiers) {
366 if (smd->mask_strip && relations_render_loop_check(smd->mask_strip, strip)) {
367 return true;
368 }
369 }
370
371 return false;
372}
373
375{
376 while (strip->anims.last) {
377 StripAnim *sanim = static_cast<StripAnim *>(strip->anims.last);
378
379 if (sanim->anim) {
380 MOV_close(sanim->anim);
381 sanim->anim = nullptr;
382 }
383
384 BLI_freelinkN(&strip->anims, sanim);
385 }
386 BLI_listbase_clear(&strip->anims);
387}
388
393
394static bool get_uids_cb(Strip *strip, void *user_data)
395{
396 GSet *used_uids = (GSet *)user_data;
397 const SessionUID *session_uid = &strip->runtime.session_uid;
398 if (!BLI_session_uid_is_generated(session_uid)) {
399 printf("Sequence %s does not have UID generated.\n", strip->name);
400 return true;
401 }
402
403 if (BLI_gset_lookup(used_uids, session_uid) != nullptr) {
404 printf("Sequence %s has duplicate UID generated.\n", strip->name);
405 return true;
406 }
407
408 BLI_gset_insert(used_uids, (void *)session_uid);
409 return true;
410}
411
413{
414 if (scene->ed == nullptr) {
415 return;
416 }
417
418 GSet *used_uids = BLI_gset_new(
420
421 foreach_strip(&scene->ed->seqbase, get_uids_cb, used_uids);
422
423 BLI_gset_free(used_uids, nullptr);
424}
425
426bool exists_in_seqbase(const Strip *strip, const ListBase *seqbase)
427{
428 LISTBASE_FOREACH (Strip *, strip_test, seqbase) {
429 if (strip_test->type == STRIP_TYPE_META && exists_in_seqbase(strip, &strip_test->seqbase)) {
430 return true;
431 }
432 if (strip_test == strip) {
433 return true;
434 }
435 }
436 return false;
437}
438
439} // namespace blender::seq
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
struct GSet GSet
Definition BLI_ghash.h:337
void * BLI_gset_lookup(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.cc:959
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:944
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE uint64_t divide_ceil_ul(uint64_t a, uint64_t b)
bool BLI_session_uid_is_generated(const SessionUID *uid)
uint BLI_session_uid_ghash_hash(const void *uid_v)
bool BLI_session_uid_ghash_compare(const void *lhs_v, const void *rhs_v)
SessionUID BLI_session_uid_generate(void)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SEQUENCER_STRIPS
Definition DNA_ID.h:1122
#define MAXFRAME
@ SEQ_CACHE_STORE_RAW
@ SEQ_CACHE_STORE_FINAL_OUT
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_SPEED
@ STRIP_TYPE_META
@ SEQ_SCENE_STRIPS
#define U
#define input
#define printf(...)
void MOV_close(MovieReader *anim)
Definition movie_read.cc:66
void relations_strip_free_anim(Strip *strip)
void prefetch_stop(Scene *scene)
Definition prefetch.cc:310
Strip * lookup_meta_by_strip(Editing *ed, const Strip *key)
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
void foreach_strip(ListBase *seqbase, ForEachFunc callback, void *user_data)
Definition iterator.cc:59
void cache_cleanup_final(Scene *scene)
void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree)
bool sequencer_strip_generates_image(Strip *strip)
size_t final_image_cache_calc_memory_size(const Scene *scene)
static void invalidate_movieclip_strips(Scene *scene, MovieClip *clip_target, ListBase *seqbase)
void relations_invalidate_cache(Scene *scene, Strip *strip)
void intra_frame_cache_invalidate(Scene *scene)
static Strip * sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase)
void cache_settings_changed(Scene *scene)
bool relations_render_loop_check(Strip *strip_main, Strip *strip)
void final_image_cache_invalidate_frame_range(Scene *scene, const float timeline_frame_start, const float timeline_frame_end)
void relations_free_all_anim_ibufs(Scene *scene, int timeline_frame)
Span< Strip * > lookup_strips_by_scene(Editing *ed, const Scene *key)
void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_target)
bool source_image_cache_evict(Scene *scene)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
static void invalidate_final_cache_strip_range(Scene *scene, const Strip *strip)
static void invalidate_raw_cache_of_parent_meta(Scene *scene, Strip *strip)
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void source_image_cache_clear(Scene *scene)
static bool get_uids_cb(Strip *strip, void *user_data)
Span< Strip * > lookup_strips_by_compositor_node_group(Editing *ed, const bNodeTree *key)
void relations_invalidate_cache_raw(Scene *scene, Strip *strip)
void thumbnail_cache_clear(Scene *scene)
void source_image_cache_invalidate_strip(Scene *scene, const Strip *strip)
size_t source_image_cache_get_image_count(const Scene *scene)
void relations_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_target)
MetaStack * meta_stack_active_get(const Editing *ed)
Definition sequencer.cc:454
bool time_strip_intersects_frame(const Scene *scene, const Strip *strip, const int timeline_frame)
void final_image_cache_clear(Scene *scene)
void strip_effect_speed_rebuild_map(Scene *scene, Strip *strip)
void cache_cleanup(Scene *scene)
static void sequencer_all_free_anim_ibufs(const Scene *scene, ListBase *seqbase, int timeline_frame, const int frame_range[2])
bool final_image_cache_evict(Scene *scene)
bool is_cache_full(const Scene *scene)
void preview_cache_invalidate(Scene *scene)
void relations_session_uid_generate(Strip *strip)
bool relation_is_effect_of_strip(const Strip *effect, const Strip *input)
bool evict_caches_if_full(Scene *scene)
void media_presence_invalidate_strip(Scene *scene, const Strip *strip)
size_t final_image_cache_get_image_count(const Scene *scene)
size_t source_image_cache_calc_memory_size(const Scene *scene)
void relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
void cache_cleanup_intra(Scene *scene)
void relations_check_uids_unique_and_report(const Scene *scene)
bool relations_check_scene_recursion(Scene *scene, ReportList *reports)
bool exists_in_seqbase(const Strip *strip, const ListBase *seqbase)
ListBase seqbase
void * last
void * first
ListBase scenes
Definition BKE_main.hh:278
struct Editing * ed
struct RenderData r
struct MovieReader * anim
SessionUID session_uid
struct Strip * input1
void * effectdata
ListBase seqbase
StripRuntime runtime
struct Strip * next
char name[64]
ListBase modifiers
struct Strip * input2
ListBase anims
i
Definition text_draw.cc:230