VSE: Make prefetch RAW cache aware

Before this change prefetching would not work correctly with only RAW
caches. The RAW caches wouldn't previously be emptied either if
switching them off while prefetching was turned on.
This commit is contained in:
Sebastian Parborg
2025-05-15 17:32:00 +02:00
committed by Sebastian Parborg
parent 7a291d6e5f
commit 38b178f7db
6 changed files with 72 additions and 37 deletions

View File

@@ -70,6 +70,7 @@ void relations_session_uid_generate(Strip *strip);
void cache_cleanup(Scene *scene);
bool is_cache_full(const Scene *scene);
bool evict_caches_if_full(Scene *scene);
void source_image_cache_iterate(Scene *scene,
void *userdata,

View File

@@ -196,7 +196,10 @@ bool final_image_cache_evict(Scene *scene)
* is full and no longer can evict anything. */
int cur_prefetch_start = std::numeric_limits<int>::min();
int cur_prefetch_end = std::numeric_limits<int>::min();
seq_prefetch_get_time_range(scene, &cur_prefetch_start, &cur_prefetch_end);
if (scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT) {
/* 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 int cur_frame = scene->r.cfra;

View File

@@ -256,9 +256,20 @@ bool source_image_cache_evict(Scene *scene)
if (cache == nullptr) {
return false;
}
/* Find which entry to remove -- we pick the one that is furthest from the current frame,
* biasing the ones that are behind the current frame. */
* biasing the ones that are behind the current frame.
*
* However, do not try to evict entries from the current prefetch job range -- we need to
* be able to fully fill the cache from prefetching, and then actually stop the job when it
* is full and no longer can evict anything. */
int cur_prefetch_start = std::numeric_limits<int>::min();
int cur_prefetch_end = std::numeric_limits<int>::min();
if (scene->ed->cache_flag & SEQ_CACHE_STORE_RAW) {
/* 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 int cur_frame = scene->r.cfra;
SourceImageCache::StripEntry *best_strip = nullptr;
std::pair<int, int> best_key = {};
@@ -266,6 +277,20 @@ bool source_image_cache_evict(Scene *scene)
for (const auto &strip : cache->map_.items()) {
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. */
}
if (item_frame >= cur_prefetch_start && item_frame <= timeline_end) {
continue; /* Within active prefetch range, do not try to remove it. */
}
}
else if (item_frame >= cur_prefetch_start && item_frame <= cur_prefetch_end) {
continue; /* Within active prefetch range, do not try to remove it. */
}
/* Score for removal is distance to current frame; 2x that if behind current frame. */
int score = 0;
if (item_frame < cur_frame) {

View File

@@ -181,12 +181,7 @@ Scene *prefetch_get_original_scene_and_strip(const RenderData *context, const St
static bool seq_prefetch_is_cache_full(Scene *scene)
{
bool full = is_cache_full(scene);
if (!full) {
return false;
}
bool evicted = final_image_cache_evict(scene);
return !evicted;
return evict_caches_if_full(scene);
}
static int seq_prefetch_cfra(PrefetchJob *pfjob)

View File

@@ -1964,34 +1964,6 @@ static ImBuf *seq_render_strip_stack(const RenderData *context,
return out;
}
static void evict_caches_if_full(Scene *scene)
{
if (!is_cache_full(scene)) {
return;
}
/* Cache is full, so we want to remove some images. We always try to remove one final image,
* and some amount of source images for each final image, so that ratio of cached images
* stays the same. Depending on the frame composition complexity, there can be lots of
* source images cached for a single final frame; if we only removed one source image
* we'd eventually have the cache still filled only with source images. */
const size_t count_final = final_image_cache_get_image_count(scene);
const size_t count_source = source_image_cache_get_image_count(scene);
const size_t source_per_final = std::max<size_t>(
divide_ceil_ul(count_source, std::max<size_t>(count_final, 1)), 1);
do {
bool evicted_final = final_image_cache_evict(scene);
bool evicted_source = false;
for (size_t i = 0; i < source_per_final; i++) {
evicted_source |= source_image_cache_evict(scene);
}
if (!evicted_final && !evicted_source) {
break; /* Can't evict no more. */
}
} while (is_cache_full(scene));
}
ImBuf *render_give_ibuf(const RenderData *context, float timeline_frame, int chanshown)
{
Scene *scene = context->scene;

View File

@@ -60,6 +60,45 @@ bool is_cache_full(const Scene *scene)
cache_limit;
}
bool evict_caches_if_full(Scene *scene)
{
if (!is_cache_full(scene)) {
/* Cache is not full, we don't have to evict anything. */
return false;
}
/* Cache is full, so we want to remove some images. We always try to remove one final image,
* and some amount of source images for each final image, so that ratio of cached images
* stays the same. Depending on the frame composition complexity, there can be lots of
* source images cached for a single final frame; if we only removed one source image
* we'd eventually have the cache still filled only with source images. */
const size_t count_final = final_image_cache_get_image_count(scene);
const size_t count_source = source_image_cache_get_image_count(scene);
bool evicted_final = false;
if (count_final != 0) {
evicted_final = final_image_cache_evict(scene);
}
bool evicted_source = false;
if (count_source != 0) {
evicted_source = source_image_cache_evict(scene);
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,
std::max<size_t>(count_final, 1));
for (size_t i = 0; i < source_per_final; i++) {
if (!source_image_cache_evict(scene)) {
/* Can't evict any more frames, stop. */
break;
}
}
}
}
/* Did we evict anything to free up the cache? */
return !(evicted_final || evicted_source);
}
static void invalidate_final_cache_strip_range(Scene *scene, const Strip *strip)
{
const int strip_left = time_left_handle_frame_get(scene, strip);