Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py2
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h27
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/seqcache.c106
-rw-r--r--source/blender/blenkernel/intern/seqprefetch.c453
-rw-r--r--source/blender/blenkernel/intern/sequencer.c25
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c12
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c7
10 files changed, 612 insertions, 26 deletions
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 8cc789eaf1d..66ce7496869 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -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):
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index e1bc16702d5..16f766ae8bb 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -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
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 053e5435925..16074cd3e37 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -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
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index ff54327e406..eb4fc6994a9 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -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;
+}
diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c
new file mode 100644
index 00000000000..172db70823a
--- /dev/null
+++ b/source/blender/blenkernel/intern/seqprefetch.c
@@ -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;
+}
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 14096335626..2e36982f7d8 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -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) {
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 9a4a12729a0..b0ea7ce2aef 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -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);
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index b15acb12d00..bf555e8fe09 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -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) {
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 087d30ce312..8d9dc77c49b 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -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__ */
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 1da29217db8..31bc9927026 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -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);