VSE: prefetching

When enabled prefetching(preview panel>view settings), a pernament running job
is created, that will render frames in the background until the cache is full.
If the cache is not filled fast enough, prefetch job suspends itself
at the last moment and will wait until it has chance to "catch up".
Effectively this will decouple rendering to separate thread, so rendering
itself is a bit faster.
Cache recycling behavior will be changed to "free furthest frame to the left
of playhead if possible, otherwise rightmost frame".

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D5386
This commit is contained in:
Richard Antalik 2019-09-13 17:21:54 -07:00
parent 0547a77536
commit ab3a9dc1ed
Notes: blender-bot 2023-02-14 02:27:51 +01:00
Referenced by issue #70194, VSE: 'Prefetch frames' causes excessive CPU usage whenever there is animation on a strip (freezes when tweaking animation [Graph Editor, Dopesheet, typing in new values])
Referenced by issue #69924, Video Editor: Prefetch stops when moving playhead and makes Blender hang on new file.
10 changed files with 612 additions and 26 deletions

View File

@ -1894,6 +1894,7 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
layout.use_property_decorate = False
st = context.space_data
ed = context.scene.sequence_editor
col = layout.column()
col.prop(st, "display_channel", text="Channel")
@ -1905,6 +1906,7 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
col.prop(st, "show_separate_color")
col.prop(st, "proxy_render_size")
col.prop(ed, "use_prefetch")
class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel):

View File

@ -82,6 +82,11 @@ void BKE_sequence_iterator_end(SeqIterator *iter);
} \
((void)0)
typedef enum eSeqTaskId {
SEQ_TASK_MAIN_RENDER,
SEQ_TASK_PREFETCH_RENDER,
} eSeqTaskId;
typedef struct SeqRenderData {
struct Main *bmain;
struct Depsgraph *depsgraph;
@ -94,7 +99,10 @@ typedef struct SeqRenderData {
float motion_blur_shutter;
bool skip_cache;
bool is_proxy_render;
bool is_prefetch_render;
int view_id;
/* ID of task for asigning temp cache entries to particular task(thread, etc.) */
eSeqTaskId task_id;
/* special case for OpenGL render */
struct GPUOffScreen *gpu_offscreen;
@ -290,7 +298,7 @@ void BKE_sequencer_proxy_rebuild_context(struct Main *bmain,
void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context,
short *stop,
short *do_update,
float *progress);
float *num_frames_prefetched);
void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop);
void BKE_sequencer_proxy_set(struct Sequence *seq, bool value);
@ -318,6 +326,7 @@ bool BKE_sequencer_cache_put_if_possible(const SeqRenderData *context,
int type,
struct ImBuf *nval,
float cost);
bool BKE_sequencer_cache_recycle_item(struct Scene *scene);
void BKE_sequencer_cache_free_temp_cache(struct Scene *scene, short id, int cfra);
void BKE_sequencer_cache_destruct(struct Scene *scene);
void BKE_sequencer_cache_cleanup_all(struct Main *bmain);
@ -330,6 +339,22 @@ void BKE_sequencer_cache_iterate(
struct Scene *scene,
void *userdata,
bool callback(void *userdata, struct Sequence *seq, int cfra, int cache_type, float cost));
bool BKE_sequencer_cache_is_full(struct Scene *scene);
/* **********************************************************************
* seqprefetch.c
*
* Sequencer frame prefetching
* ********************************************************************** */
void BKE_sequencer_prefetch_start(const SeqRenderData *context, float cfra, float cost);
void BKE_sequencer_prefetch_stop(struct Scene *scene);
void BKE_sequencer_prefetch_free(struct Scene *scene);
bool BKE_sequencer_prefetch_need_redraw(struct Main *bmain, struct Scene *scene);
void BKE_sequencer_prefetch_get_time_range(struct Scene *scene, int *start, int *end);
SeqRenderData *BKE_sequencer_prefetch_get_original_context(const SeqRenderData *context);
struct Sequence *BKE_sequencer_prefetch_get_original_sequence(struct Sequence *seq,
struct Scene *scene);
/* **********************************************************************
* seqeffects.c

View File

@ -188,6 +188,7 @@ set(SRC
intern/scene.c
intern/screen.c
intern/seqcache.c
intern/seqprefetch.c
intern/seqeffects.c
intern/seqmodifier.c
intern/sequencer.c

View File

@ -44,17 +44,6 @@
* Sequencer Cache Design Notes
* ============================
*
* Cache key members:
* is_temp_cache - this cache entry will be freed before rendering next frame
* creator_id - ID of thread that created entry
* cost - In short: render time divided by playback frame rate
* link_prev/next - link to another entry created during rendering of the frame
*
* Linking: We use links to reduce number of iterations needed to manage cache.
* Entries are linked in order as they are put into cache.
* Only permanent (is_temp_cache = 0) cache entries are linked.
* Putting #SEQ_CACHE_STORE_FINAL_OUT will reset linking
*
* Function:
* All images created during rendering are added to cache, even if the cache is already full.
* This is because:
@ -64,6 +53,11 @@
* "holes" in the cache, which can be annoying
* If the cache is full all entries for pending frame will have is_temp_cache set.
*
* Linking: We use links to reduce number of iterations over entries needed to manage cache.
* Entries are linked in order as they are put into cache.
* Only permanent (is_temp_cache = 0) cache entries are linked.
* Putting #SEQ_CACHE_STORE_FINAL_OUT will reset linking
*
* Only entire frame can be freed to release resources for new entries (recycling).
* Once again, this is to reduce number of iterations, but also more controllable than removing
* entries one by one in reverse order to their creation.
@ -93,9 +87,10 @@ typedef struct SeqCacheKey {
struct Sequence *seq;
SeqRenderData context;
float nfra;
float cost;
bool is_temp_cache;
short creator_id;
float cost; /* In short: render time(s) divided by playback frame duration(s) */
bool is_temp_cache; /* this cache entry will be freed before rendering next frame */
/* ID of task for asigning temp cache entries to particular task(thread, etc.) */
eSeqTaskId task_id;
int type;
} SeqCacheKey;
@ -172,6 +167,11 @@ static void seq_cache_unlock(Scene *scene)
}
}
static size_t seq_cache_get_mem_total(void)
{
return ((size_t)U.memcachelimit) * 1024 * 1024;
}
static void seq_cache_keyfree(void *val)
{
SeqCacheKey *key = val;
@ -228,10 +228,43 @@ static void seq_cache_relink_keys(SeqCacheKey *link_next, SeqCacheKey *link_prev
}
}
/* Choose a key out of 2 candidates(leftmost and rightmost items)
* to recycle based on currently used strategy */
static SeqCacheKey *seq_cache_choose_key(Scene *scene, SeqCacheKey *lkey, SeqCacheKey *rkey)
{
SeqCacheKey *finalkey = NULL;
/* Ideally, cache would not need to check the state of prefetching task
* that is tricky to do however, because prefetch would need to know,
* if a key, that is about to be created would be removed by itself.
*
* This can happen because only FINAL_OUT item insertion will trigger recycling
* but that is also the point, where prefetch can be suspended.
*
* We could use temp cache as a shield and later untemp entry,
* but it is not worth of increasing system complexity.
*/
if (scene->ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) {
int pfjob_start, pfjob_end;
BKE_sequencer_prefetch_get_time_range(scene, &pfjob_start, &pfjob_end);
if (lkey) {
int lkey_cfra = lkey->seq->start + lkey->nfra;
if (lkey_cfra < pfjob_start || lkey_cfra > pfjob_end) {
return lkey;
}
}
if (rkey) {
int rkey_cfra = rkey->seq->start + rkey->nfra;
if (rkey_cfra < pfjob_start || rkey_cfra > pfjob_end) {
return rkey;
}
}
return NULL;
}
if (rkey && lkey) {
int lkey_cfra = lkey->seq->start + lkey->nfra;
int rkey_cfra = rkey->seq->start + rkey->nfra;
@ -286,7 +319,7 @@ static void seq_cache_recycle_linked(Scene *scene, SeqCacheKey *base)
}
}
static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
{
SeqCache *cache = seq_cache_get_from_scene(scene);
SeqCacheKey *finalkey = NULL;
@ -342,15 +375,16 @@ static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene)
}
finalkey = seq_cache_choose_key(scene, lkey, rkey);
return finalkey;
}
/* Find only "base" keys
* Sources(other types) for a frame must be freed all at once
*/
static bool seq_cache_recycle_item(Scene *scene)
bool BKE_sequencer_cache_recycle_item(Scene *scene)
{
size_t memory_total = ((size_t)U.memcachelimit) * 1024 * 1024;
size_t memory_total = seq_cache_get_mem_total();
SeqCache *cache = seq_cache_get_from_scene(scene);
if (!cache) {
return false;
@ -429,7 +463,7 @@ void BKE_sequencer_cache_free_temp_cache(Scene *scene, short id, int cfra)
SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter);
BLI_ghashIterator_step(&gh_iter);
if (key->is_temp_cache && key->creator_id == id && key->seq->start + key->nfra != cfra) {
if (key->is_temp_cache && key->task_id == id && key->seq->start + key->nfra != cfra) {
BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree);
}
}
@ -459,6 +493,8 @@ void BKE_sequencer_cache_cleanup_all(Main *bmain)
}
void BKE_sequencer_cache_cleanup(Scene *scene)
{
BKE_sequencer_prefetch_stop(scene);
SeqCache *cache = seq_cache_get_from_scene(scene);
if (!cache) {
return;
@ -542,6 +578,12 @@ struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context,
{
Scene *scene = context->scene;
if (context->is_prefetch_render) {
context = BKE_sequencer_prefetch_get_original_context(context);
scene = context->scene;
seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene);
}
if (!scene->ed->cache) {
BKE_sequencer_cache_create(scene);
return NULL;
@ -571,7 +613,13 @@ bool BKE_sequencer_cache_put_if_possible(
{
Scene *scene = context->scene;
if (seq_cache_recycle_item(scene)) {
if (context->is_prefetch_render) {
context = BKE_sequencer_prefetch_get_original_context(context);
scene = context->scene;
seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene);
}
if (BKE_sequencer_cache_recycle_item(scene)) {
BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, cost);
return true;
}
@ -586,7 +634,12 @@ void BKE_sequencer_cache_put(
const SeqRenderData *context, Sequence *seq, float cfra, int type, ImBuf *i, float cost)
{
Scene *scene = context->scene;
short creator_id = 0;
if (context->is_prefetch_render) {
context = BKE_sequencer_prefetch_get_original_context(context);
scene = context->scene;
seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene);
}
if (i == NULL || context->skip_cache || context->is_proxy_render || !seq) {
return;
@ -632,7 +685,7 @@ void BKE_sequencer_cache_put(
key->link_prev = NULL;
key->link_next = NULL;
key->is_temp_cache = true;
key->creator_id = creator_id;
key->task_id = context->task_id;
/* Item stored for later use */
if (flag & type) {
@ -688,3 +741,14 @@ void BKE_sequencer_cache_iterate(
cache->last_key = NULL;
seq_cache_unlock(scene);
}
bool BKE_sequencer_cache_is_full(Scene *scene)
{
size_t memory_total = seq_cache_get_mem_total();
SeqCache *cache = seq_cache_get_from_scene(scene);
if (!cache) {
return false;
}
return memory_total < cache->memory_used;
}

View File

@ -0,0 +1,453 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup bke
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "BLI_listbase.h"
#include "BLI_threads.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "BKE_animsys.h"
#include "BKE_library.h"
#include "BKE_scene.h"
#include "BKE_main.h"
#include "BKE_context.h"
#include "BKE_sequencer.h"
#include "BKE_layer.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_debug.h"
#include "DEG_depsgraph_query.h"
typedef struct PrefetchJob {
struct PrefetchJob *next, *prev;
struct Main *bmain;
struct Scene *scene;
struct Scene *scene_eval;
struct Depsgraph *depsgraph;
ThreadMutex prefetch_suspend_mutex;
ThreadCondition prefetch_suspend_cond;
ListBase threads;
/* context */
struct SeqRenderData context;
struct SeqRenderData context_cpy;
struct ListBase *seqbasep;
struct ListBase *seqbasep_cpy;
/* prefetch area */
float cfra;
int num_frames_prefetched;
/* control */
bool running;
bool waiting;
bool stop;
} PrefetchJob;
static bool seq_prefetch_is_playing(Main *bmain)
{
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
if (screen->animtimer) {
return true;
}
}
return false;
}
static bool seq_prefetch_is_scrubbing(Main *bmain)
{
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
if (screen->scrubbing) {
return true;
}
}
return false;
}
static PrefetchJob *seq_prefetch_job_get(Scene *scene)
{
if (scene && scene->ed) {
return scene->ed->prefetch_job;
}
return NULL;
}
static bool seq_prefetch_job_is_running(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (!pfjob) {
return false;
}
return pfjob->running;
}
static bool seq_prefetch_job_is_waiting(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (!pfjob) {
return false;
}
return pfjob->waiting;
}
/* for cache context swapping */
Sequence *BKE_sequencer_prefetch_get_original_sequence(Sequence *seq, Scene *scene)
{
Editing *ed = scene->ed;
ListBase *seqbase = &ed->seqbase;
Sequence *seq_orig = NULL;
for (seq_orig = (Sequence *)seqbase->first; seq_orig; seq_orig = seq_orig->next) {
if (strcmp(seq->name, seq_orig->name) == 0) {
break;
}
}
return seq_orig;
}
/* for cache context swapping */
SeqRenderData *BKE_sequencer_prefetch_get_original_context(const SeqRenderData *context)
{
PrefetchJob *pfjob = seq_prefetch_job_get(context->scene);
return &pfjob->context;
}
static bool seq_prefetch_is_cache_full(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (!BKE_sequencer_cache_is_full(pfjob->scene)) {
return false;
}
return BKE_sequencer_cache_recycle_item(pfjob->scene) == false;
}
void BKE_sequencer_prefetch_get_time_range(Scene *scene, int *start, int *end)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
*start = pfjob->cfra;
*end = pfjob->cfra + pfjob->num_frames_prefetched;
}
static void seq_prefetch_free_depsgraph(PrefetchJob *pfjob)
{
if (pfjob->depsgraph != NULL) {
DEG_graph_free(pfjob->depsgraph);
}
pfjob->depsgraph = NULL;
pfjob->scene_eval = NULL;
}
static void seq_prefetch_update_depsgraph(PrefetchJob *pfjob)
{
DEG_evaluate_on_framechange(
pfjob->bmain, pfjob->depsgraph, pfjob->cfra + pfjob->num_frames_prefetched);
}
static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob)
{
Main *bmain = pfjob->bmain;
Scene *scene = pfjob->scene;
ViewLayer *view_layer = BKE_view_layer_default_render(scene);
pfjob->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
DEG_debug_name_set(pfjob->depsgraph, "SEQUENCER PREFETCH");
/* Make sure there is a correct evaluated scene pointer. */
DEG_graph_build_for_render_pipeline(pfjob->depsgraph, pfjob->bmain, scene, view_layer);
/* Update immediately so we have proper evaluated scene. */
seq_prefetch_update_depsgraph(pfjob);
pfjob->scene_eval = DEG_get_evaluated_scene(pfjob->depsgraph);
pfjob->scene_eval->ed->cache_flag = 0;
}
static void seq_prefetch_update_area(PrefetchJob *pfjob)
{
int cfra = pfjob->scene->r.cfra;
/* rebase */
if (cfra > pfjob->cfra) {
int delta = cfra - pfjob->cfra;
pfjob->cfra = cfra;
pfjob->num_frames_prefetched -= delta;
if (pfjob->num_frames_prefetched <= 1) {
pfjob->num_frames_prefetched = 1;
}
}
/* reset */
if (cfra < pfjob->cfra) {
pfjob->cfra = cfra;
pfjob->num_frames_prefetched = 1;
}
}
/* Use also to update scene and context changes */
void BKE_sequencer_prefetch_stop(Scene *scene)
{
PrefetchJob *pfjob;
pfjob = seq_prefetch_job_get(scene);
if (!pfjob) {
return;
}
pfjob->stop = true;
while (pfjob->running) {
BLI_condition_notify_one(&pfjob->prefetch_suspend_cond);
}
}
static void seq_prefetch_update_context(const SeqRenderData *context)
{
PrefetchJob *pfjob;
pfjob = seq_prefetch_job_get(context->scene);
BKE_sequencer_new_render_data(pfjob->bmain,
pfjob->depsgraph,
pfjob->scene_eval,
context->rectx,
context->recty,
context->preview_render_size,
false,
&pfjob->context_cpy);
pfjob->context_cpy.is_prefetch_render = true;
pfjob->context_cpy.task_id = SEQ_TASK_PREFETCH_RENDER;
BKE_sequencer_new_render_data(pfjob->bmain,
pfjob->depsgraph,
pfjob->scene,
context->rectx,
context->recty,
context->preview_render_size,
false,
&pfjob->context);
pfjob->context.is_prefetch_render = false;
/* Same ID as prefetch context, because context will be swapped, but we still
* want to assign this ID to cache entries created in this thread.
* This is to allow "temp cache" work correctly for both threads.
*/
pfjob->context.task_id = SEQ_TASK_PREFETCH_RENDER;
}
static void seq_prefetch_update_scene(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (!pfjob) {
return;
}
seq_prefetch_free_depsgraph(pfjob);
seq_prefetch_init_depsgraph(pfjob);
}
static void seq_prefetch_resume(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (pfjob && pfjob->waiting) {
BLI_condition_notify_one(&pfjob->prefetch_suspend_cond);
}
}
void BKE_sequencer_prefetch_free(Scene *scene)
{
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
if (!pfjob) {
return;
}
BKE_sequencer_prefetch_stop(scene);
BLI_threadpool_remove(&pfjob->threads, pfjob);
BLI_threadpool_end(&pfjob->threads);
BLI_mutex_end(&pfjob->prefetch_suspend_mutex);
BLI_condition_end(&pfjob->prefetch_suspend_cond);
seq_prefetch_free_depsgraph(pfjob);
MEM_freeN(pfjob);
scene->ed->prefetch_job = NULL;
}
static void *seq_prefetch_frames(void *job)
{
PrefetchJob *pfjob = (PrefetchJob *)job;
/* set to NULL before return! */
pfjob->scene_eval->ed->prefetch_job = pfjob;
while (pfjob->cfra + pfjob->num_frames_prefetched < pfjob->scene->r.efra) {
BKE_animsys_evaluate_all_animation(pfjob->context_cpy.bmain,
pfjob->context_cpy.depsgraph,
pfjob->context_cpy.scene,
pfjob->cfra + pfjob->num_frames_prefetched);
seq_prefetch_update_depsgraph(pfjob);
ImBuf *ibuf = BKE_sequencer_give_ibuf(
&pfjob->context_cpy, pfjob->cfra + pfjob->num_frames_prefetched, 0);
BKE_sequencer_cache_free_temp_cache(
pfjob->scene, pfjob->context.task_id, pfjob->cfra + pfjob->num_frames_prefetched);
IMB_freeImBuf(ibuf);
/* suspend thread */
BLI_mutex_lock(&pfjob->prefetch_suspend_mutex);
while ((seq_prefetch_is_cache_full(pfjob->scene) || seq_prefetch_is_scrubbing(pfjob->bmain)) &&
pfjob->scene->ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE && !pfjob->stop) {
pfjob->waiting = true;
BLI_condition_wait(&pfjob->prefetch_suspend_cond, &pfjob->prefetch_suspend_mutex);
seq_prefetch_update_area(pfjob);
}
pfjob->waiting = false;
BLI_mutex_unlock(&pfjob->prefetch_suspend_mutex);
/* Avoid "collision" with main thread, but make sure to fetch at least few frames */
if (pfjob->num_frames_prefetched > 5 &&
(pfjob->cfra + pfjob->num_frames_prefetched - pfjob->scene->r.cfra) < 2) {
break;
}
if (!(pfjob->scene->ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) || pfjob->stop) {
break;
}
seq_prefetch_update_area(pfjob);
pfjob->num_frames_prefetched++;
}
BKE_sequencer_cache_free_temp_cache(
pfjob->scene, pfjob->context.task_id, pfjob->cfra + pfjob->num_frames_prefetched);
pfjob->running = false;
pfjob->scene_eval->ed->prefetch_job = NULL;
return 0;
}
PrefetchJob *seq_prefetch_start(const SeqRenderData *context, float cfra)
{
PrefetchJob *pfjob;
pfjob = seq_prefetch_job_get(context->scene);
if (!pfjob) {
if (context->scene->ed) {
pfjob = (PrefetchJob *)MEM_callocN(sizeof(PrefetchJob), "PrefetchJob");
context->scene->ed->prefetch_job = pfjob;
BLI_threadpool_init(&pfjob->threads, seq_prefetch_frames, 1);
BLI_mutex_init(&pfjob->prefetch_suspend_mutex);
BLI_condition_init(&pfjob->prefetch_suspend_cond);
pfjob->bmain = context->bmain;
pfjob->scene = context->scene;
seq_prefetch_init_depsgraph(pfjob);
}
}
seq_prefetch_update_scene(context->scene);
seq_prefetch_update_context(context);
pfjob->cfra = cfra;
pfjob->num_frames_prefetched = 1;
pfjob->waiting = false;
pfjob->stop = false;
pfjob->running = true;
if (&pfjob->threads) {
BLI_threadpool_remove(&pfjob->threads, pfjob);
}
BLI_threadpool_insert(&pfjob->threads, pfjob);
return pfjob;
}
/* Start or resume prefetching*/
void BKE_sequencer_prefetch_start(const SeqRenderData *context, float cfra, float cost)
{
Scene *scene = context->scene;
Editing *ed = scene->ed;
bool has_strips = (bool)ed->seqbasep->first;
if (!context->is_prefetch_render && !context->is_proxy_render) {
bool playing = seq_prefetch_is_playing(context->bmain);
bool scrubbing = seq_prefetch_is_scrubbing(context->bmain);
bool running = seq_prefetch_job_is_running(scene);
seq_prefetch_resume(scene);
/* conditions to start:
* prefetch enabled, prefetch not running, not scrubbing,
* not playing and rendering-expensive footage, cache storage enabled, has strips to render
*/
if ((ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) && !running && !scrubbing &&
!(playing && cost > 0.9) && ed->cache_flag & SEQ_CACHE_ALL_TYPES && has_strips) {
seq_prefetch_start(context, cfra);
}
}
}
bool BKE_sequencer_prefetch_need_redraw(Main *bmain, Scene *scene)
{
bool playing = seq_prefetch_is_playing(bmain);
bool scrubbing = seq_prefetch_is_scrubbing(bmain);
bool running = seq_prefetch_job_is_running(scene);
bool suspended = seq_prefetch_job_is_waiting(scene);
/* force redraw, when prefetching and using cache view. */
if (running && !playing && !suspended && scene->ed->cache_flag & SEQ_CACHE_VIEW_ENABLE) {
return true;
}
/* Sometimes scrubbing flag is set when not scrubbing. In that case I want to catch "event" of
* stopping scrubbing */
if (scrubbing) {
return true;
}
return false;
}

View File

@ -112,6 +112,8 @@ static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr
static int seq_num_files(Scene *scene, char views_format, const bool is_multiview);
static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const int view_id);
static ThreadMutex seq_render_mutex = BLI_MUTEX_INITIALIZER;
/* **** XXX ******** */
#define SELECT 1
ListBase seqbase_clipboard;
@ -483,6 +485,7 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user)
return;
}
BKE_sequencer_prefetch_free(scene);
BKE_sequencer_cache_destruct(scene);
SEQ_BEGIN (ed, seq) {
@ -492,7 +495,6 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user)
SEQ_END;
BLI_freelistN(&ed->metastack);
MEM_freeN(ed);
scene->ed = NULL;
@ -635,6 +637,8 @@ void BKE_sequencer_new_render_data(Main *bmain,
r_context->is_proxy_render = false;
r_context->view_id = 0;
r_context->gpu_offscreen = NULL;
r_context->task_id = SEQ_TASK_MAIN_RENDER;
r_context->is_prefetch_render = false;
}
/* ************************* iterator ************************** */
@ -4092,18 +4096,29 @@ ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int cha
out = BKE_sequencer_cache_get(context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT);
}
BKE_sequencer_cache_free_temp_cache(context->scene, 0, cfra);
BKE_sequencer_cache_free_temp_cache(context->scene, context->task_id, cfra);
clock_t begin = seq_estimate_render_cost_begin();
float cost = 0;
if (count && !out) {
BLI_mutex_lock(&seq_render_mutex);
out = seq_render_strip_stack(context, &state, seqbasep, cfra, chanshown);
cost = seq_estimate_render_cost_end(context->scene, begin);
BKE_sequencer_cache_put_if_possible(
context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost);
if (context->is_prefetch_render) {
BKE_sequencer_cache_put(
context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost);
}
else {
BKE_sequencer_cache_put_if_possible(
context, seq_arr[count - 1], cfra, SEQ_CACHE_STORE_FINAL_OUT, out, cost);
}
BLI_mutex_unlock(&seq_render_mutex);
}
BKE_sequencer_prefetch_start(context, cfra, cost);
return out;
}
@ -4334,6 +4349,7 @@ static void sequence_invalidate_cache(Scene *scene,
}
sequence_do_invalidate_dependent(scene, seq, &ed->seqbase);
BKE_sequencer_prefetch_stop(scene);
}
void BKE_sequence_invalidate_cache_raw(Scene *scene, Sequence *seq)
@ -4419,6 +4435,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
Sequence *seq;
BKE_sequencer_cache_cleanup(scene);
BKE_sequencer_prefetch_stop(scene);
for (seq = seqbase->first; seq; seq = seq->next) {
if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) {

View File

@ -6758,6 +6758,7 @@ static void direct_link_scene(FileData *fd, Scene *sce)
ed->act_seq = newdataadr(fd, ed->act_seq);
ed->cache = NULL;
ed->prefetch_job = NULL;
/* recursive link sequences, lb will be correctly initialized */
link_recurs_seq(fd, &ed->seqbase);

View File

@ -72,6 +72,7 @@
#include "UI_view2d.h"
#include "WM_api.h"
#include "WM_types.h"
#include "MEM_guardedalloc.h"
@ -1223,6 +1224,14 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *ar, Space
}
#endif
/* Force redraw, when prefetching and using cache view. */
static void seq_prefetch_wm_notify(const bContext *C, Scene *scene)
{
if (BKE_sequencer_prefetch_need_redraw(CTX_data_main(C), scene)) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
}
}
static void *sequencer_OCIO_transform_ibuf(
const bContext *C, ImBuf *ibuf, bool *glsl_used, int *format, int *type)
{
@ -1612,6 +1621,7 @@ void sequencer_draw_preview(const bContext *C,
}
UI_view2d_view_restore(C);
seq_prefetch_wm_notify(C, scene);
}
#if 0
@ -2004,6 +2014,8 @@ void draw_timeline_seq(const bContext *C, ARegion *ar)
short cfra_flag = 0;
float col[3];
seq_prefetch_wm_notify(C, scene);
/* clear and setup matrix */
UI_GetThemeColor3fv(TH_BACK, col);
if (ed && ed->metastack.first) {

View File

@ -270,6 +270,8 @@ typedef struct Editing {
/* Cache control */
float recycle_max_cost;
int cache_flag;
struct PrefetchJob *prefetch_job;
} Editing;
/* ************* Effect Variable Structs ********* */
@ -674,6 +676,8 @@ enum {
SEQ_CACHE_VIEW_PREPROCESSED = (1 << 7),
SEQ_CACHE_VIEW_COMPOSITE = (1 << 8),
SEQ_CACHE_VIEW_FINAL_OUT = (1 << 9),
SEQ_CACHE_PREFETCH_ENABLE = (1 << 10),
};
#endif /* __DNA_SEQUENCE_TYPES_H__ */

View File

@ -1932,6 +1932,13 @@ static void rna_def_editor(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_FINAL_OUT);
RNA_def_property_ui_text(prop, "Cache Final", "Cache final image for each frame");
prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_PREFETCH_ENABLE);
RNA_def_property_ui_text(prop,
"Prefetch frames",
"Render frames ahead of playhead in background for faster playback");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
prop = RNA_def_property(srna, "recycle_max_cost", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0f, SEQ_CACHE_COST_MAX);
RNA_def_property_ui_range(prop, 0.0f, SEQ_CACHE_COST_MAX, 0.1f, 1);