From a75fcd5d4a4f7d83bc866663e11a1fbd1d3a059d Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Wed, 11 Jun 2025 15:15:46 +0200 Subject: [PATCH] VSE: Make cache eviction a bit smarter when wrapping. Also fix some minor cache and prefetch bugs that became more noticeable with these changes. Pull Request: https://projects.blender.org/blender/blender/pulls/139635 --- .../sequencer/intern/cache/final_image_cache.cc | 11 +++++++---- .../sequencer/intern/cache/source_image_cache.cc | 11 +++++++---- source/blender/sequencer/intern/prefetch.cc | 4 ++-- source/blender/sequencer/intern/render.cc | 6 ++++-- source/blender/sequencer/intern/strip_relations.cc | 7 ++++--- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/source/blender/sequencer/intern/cache/final_image_cache.cc b/source/blender/sequencer/intern/cache/final_image_cache.cc index 6f017657338..dd6fb18ab68 100644 --- a/source/blender/sequencer/intern/cache/final_image_cache.cc +++ b/source/blender/sequencer/intern/cache/final_image_cache.cc @@ -200,17 +200,20 @@ bool final_image_cache_evict(Scene *scene) /* Only activate the prefetch guards if the cache is active. */ seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end); } - bool prefetch_loops_around = cur_prefetch_start > cur_prefetch_end; + const bool prefetch_loops_around = cur_prefetch_start > cur_prefetch_end; + + const int timeline_start = PSFRA; + const int timeline_end = PEFRA; + /* If we wrap around, treat the timeline start as the playback head position. + * This is to try to mitigate un-needed cache evictions. */ + const int cur_frame = prefetch_loops_around ? timeline_start : scene->r.cfra; - const int cur_frame = scene->r.cfra; std::pair best_key = {}; ImBuf *best_item = nullptr; int best_score = 0; for (const auto &item : cache->map_.items()) { const int item_frame = item.key.first; if (prefetch_loops_around) { - int timeline_start = PSFRA; - int timeline_end = PEFRA; if (item_frame >= timeline_start && item_frame <= cur_prefetch_end) { continue; /* Within active prefetch range, do not try to remove it. */ } diff --git a/source/blender/sequencer/intern/cache/source_image_cache.cc b/source/blender/sequencer/intern/cache/source_image_cache.cc index f0e87269783..5b05dbc4b17 100644 --- a/source/blender/sequencer/intern/cache/source_image_cache.cc +++ b/source/blender/sequencer/intern/cache/source_image_cache.cc @@ -268,9 +268,14 @@ bool source_image_cache_evict(Scene *scene) /* Only activate the prefetch guards if the cache is active. */ seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end); } - bool prefetch_loops_around = cur_prefetch_start > cur_prefetch_end; + const bool prefetch_loops_around = cur_prefetch_start > cur_prefetch_end; + + const int timeline_start = PSFRA; + const int timeline_end = PEFRA; + /* If we wrap around, treat the timeline start as the playback head position. + * This is to try to mitigate un-needed cache evictions. */ + const int cur_frame = prefetch_loops_around ? timeline_start : scene->r.cfra; - const int cur_frame = scene->r.cfra; SourceImageCache::StripEntry *best_strip = nullptr; std::pair best_key = {}; int best_score = 0; @@ -278,8 +283,6 @@ bool source_image_cache_evict(Scene *scene) for (const auto &entry : strip.value.frames.items()) { const int item_frame = int(strip.key->start + entry.value.strip_frame); if (prefetch_loops_around) { - int timeline_start = PSFRA; - int timeline_end = PEFRA; if (item_frame >= timeline_start && item_frame <= cur_prefetch_end) { continue; /* Within active prefetch range, do not try to remove it. */ } diff --git a/source/blender/sequencer/intern/prefetch.cc b/source/blender/sequencer/intern/prefetch.cc index 2af3eb81b6e..359726e7558 100644 --- a/source/blender/sequencer/intern/prefetch.cc +++ b/source/blender/sequencer/intern/prefetch.cc @@ -76,7 +76,7 @@ struct PrefetchJob { int timeline_end = 0; int timeline_length = 0; int num_frames_prefetched = 0; - int cache_flags = 0; + int cache_flags = 0; /* Only used to detect cache flag changes. */ /* Control: */ /* Set by prefetch. */ @@ -544,6 +544,7 @@ static void *seq_prefetch_frames(void *job) } ImBuf *ibuf = render_give_ibuf(&pfjob->context_cpy, seq_prefetch_cfra(pfjob), 0); + pfjob->num_frames_prefetched++; IMB_freeImBuf(ibuf); /* Suspend thread if there is nothing to be prefetched. */ @@ -556,7 +557,6 @@ static void *seq_prefetch_frames(void *job) } seq_prefetch_update_area(pfjob); - pfjob->num_frames_prefetched++; } pfjob->running = false; diff --git a/source/blender/sequencer/intern/render.cc b/source/blender/sequencer/intern/render.cc index 24f5c10e081..354be318964 100644 --- a/source/blender/sequencer/intern/render.cc +++ b/source/blender/sequencer/intern/render.cc @@ -2004,10 +2004,12 @@ ImBuf *render_give_ibuf(const RenderData *context, float timeline_frame, int cha if (!strips.is_empty() && !out) { std::scoped_lock lock(seq_render_mutex); - out = seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chanshown); - + /* Try to make space before we add any new frames to the cache if it is full. + * If we do this after we have added the new cache, we risk removing what we just added.*/ evict_caches_if_full(orig_scene); + out = seq_render_strip_stack(context, &state, channels, seqbasep, timeline_frame, chanshown); + if (out && (orig_scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT) && !context->skip_cache && !context->is_proxy_render) { diff --git a/source/blender/sequencer/intern/strip_relations.cc b/source/blender/sequencer/intern/strip_relations.cc index 77caa846670..907dd278c83 100644 --- a/source/blender/sequencer/intern/strip_relations.cc +++ b/source/blender/sequencer/intern/strip_relations.cc @@ -94,11 +94,12 @@ bool evict_caches_if_full(Scene *scene) bool evicted_source = false; if (count_source != 0) { evicted_source = source_image_cache_evict(scene); + /* Only try to enforce the final frame and raw cache ratio when the final cache is active. */ if (evicted_source && scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT) { - /* Only try to enforce the final frame and raw cache ratio when the final cache is active. */ - const size_t source_per_final = divide_ceil_ul(count_source - 1, + const size_t source_per_final = divide_ceil_ul(count_source, std::max(count_final, 1)); - for (size_t i = 0; i < source_per_final; i++) { + /* Start at "1" to make sure we only try to evict more frames if the ratio is above 1:1. */ + for (size_t i = 1; i < source_per_final; i++) { if (!source_image_cache_evict(scene)) { /* Can't evict any more frames, stop. */ break;