Patch implements better way to control playback speed than it is possible to do with speed effect. Speed factor property can be set in Time panel. There are 2 layers of control: Option to retime movie to match scene FPS rate. Custom speed factor to control playback rate. Since playback rate is strip property, it is now possible to manipulate strip as normal one even if it is retimed. To facilitate manipulation, some functions need to consider speed factor and apply necessary corrections to strip offset or strip start. These corrections may need to be float numbers, so start and offsets must be float as well. Sound strips now use speed factor instead of pitch. This means, that strips will change length to match usable length. In addition, it is possible to group movie and sound strip and change speed of meta strip.
603 lines
17 KiB
C
603 lines
17 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_anim_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_sequence_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_anim_data.h"
|
|
#include "BKE_animsys.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_build.h"
|
|
#include "DEG_depsgraph_debug.h"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "SEQ_channels.h"
|
|
#include "SEQ_iterator.h"
|
|
#include "SEQ_prefetch.h"
|
|
#include "SEQ_relations.h"
|
|
#include "SEQ_render.h"
|
|
#include "SEQ_sequencer.h"
|
|
|
|
#include "image_cache.h"
|
|
#include "prefetch.h"
|
|
#include "render.h"
|
|
|
|
typedef struct PrefetchJob {
|
|
struct PrefetchJob *next, *prev;
|
|
|
|
struct Main *bmain;
|
|
struct Main *bmain_eval;
|
|
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(const 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(const 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static Sequence *sequencer_prefetch_get_original_sequence(Sequence *seq, ListBase *seqbase)
|
|
{
|
|
LISTBASE_FOREACH (Sequence *, seq_orig, seqbase) {
|
|
if (STREQ(seq->name, seq_orig->name)) {
|
|
return seq_orig;
|
|
}
|
|
|
|
if (seq_orig->type == SEQ_TYPE_META) {
|
|
Sequence *match = sequencer_prefetch_get_original_sequence(seq, &seq_orig->seqbase);
|
|
if (match != NULL) {
|
|
return match;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Sequence *seq_prefetch_get_original_sequence(Sequence *seq, Scene *scene)
|
|
{
|
|
Editing *ed = scene->ed;
|
|
return sequencer_prefetch_get_original_sequence(seq, &ed->seqbase);
|
|
}
|
|
|
|
SeqRenderData *seq_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 (!seq_cache_is_full()) {
|
|
return false;
|
|
}
|
|
|
|
return seq_cache_recycle_item(pfjob->scene) == false;
|
|
}
|
|
|
|
static float seq_prefetch_cfra(PrefetchJob *pfjob)
|
|
{
|
|
return pfjob->cfra + pfjob->num_frames_prefetched;
|
|
}
|
|
static AnimationEvalContext seq_prefetch_anim_eval_context(PrefetchJob *pfjob)
|
|
{
|
|
return BKE_animsys_eval_context_construct(pfjob->depsgraph, seq_prefetch_cfra(pfjob));
|
|
}
|
|
|
|
void seq_prefetch_get_time_range(Scene *scene, int *start, int *end)
|
|
{
|
|
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
|
|
|
|
*start = pfjob->cfra;
|
|
*end = seq_prefetch_cfra(pfjob);
|
|
}
|
|
|
|
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->depsgraph, seq_prefetch_cfra(pfjob));
|
|
}
|
|
|
|
static void seq_prefetch_init_depsgraph(PrefetchJob *pfjob)
|
|
{
|
|
Main *bmain = pfjob->bmain_eval;
|
|
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);
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
void SEQ_prefetch_stop_all(void)
|
|
{
|
|
/* TODO(Richard): Use wm_jobs for prefetch, or pass main. */
|
|
for (Scene *scene = G.main->scenes.first; scene; scene = scene->id.next) {
|
|
SEQ_prefetch_stop(scene);
|
|
}
|
|
}
|
|
|
|
void SEQ_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);
|
|
|
|
SEQ_render_new_render_data(pfjob->bmain_eval,
|
|
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;
|
|
|
|
SEQ_render_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;
|
|
}
|
|
|
|
pfjob->scene = scene;
|
|
seq_prefetch_free_depsgraph(pfjob);
|
|
seq_prefetch_init_depsgraph(pfjob);
|
|
}
|
|
|
|
static void seq_prefetch_update_active_seqbase(PrefetchJob *pfjob)
|
|
{
|
|
MetaStack *ms_orig = SEQ_meta_stack_active_get(SEQ_editing_get(pfjob->scene));
|
|
Editing *ed_eval = SEQ_editing_get(pfjob->scene_eval);
|
|
|
|
if (ms_orig != NULL) {
|
|
Sequence *meta_eval = seq_prefetch_get_original_sequence(ms_orig->parseq, pfjob->scene_eval);
|
|
SEQ_seqbase_active_set(ed_eval, &meta_eval->seqbase);
|
|
}
|
|
else {
|
|
SEQ_seqbase_active_set(ed_eval, &ed_eval->seqbase);
|
|
}
|
|
}
|
|
|
|
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 seq_prefetch_free(Scene *scene)
|
|
{
|
|
PrefetchJob *pfjob = seq_prefetch_job_get(scene);
|
|
if (!pfjob) {
|
|
return;
|
|
}
|
|
|
|
SEQ_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);
|
|
BKE_main_free(pfjob->bmain_eval);
|
|
MEM_freeN(pfjob);
|
|
scene->ed->prefetch_job = NULL;
|
|
}
|
|
|
|
static bool seq_prefetch_seq_has_disk_cache(PrefetchJob *pfjob,
|
|
Sequence *seq,
|
|
bool can_have_final_image)
|
|
{
|
|
SeqRenderData *ctx = &pfjob->context_cpy;
|
|
float cfra = seq_prefetch_cfra(pfjob);
|
|
|
|
ImBuf *ibuf = seq_cache_get(ctx, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED);
|
|
if (ibuf != NULL) {
|
|
IMB_freeImBuf(ibuf);
|
|
return true;
|
|
}
|
|
|
|
ibuf = seq_cache_get(ctx, seq, cfra, SEQ_CACHE_STORE_RAW);
|
|
if (ibuf != NULL) {
|
|
IMB_freeImBuf(ibuf);
|
|
return true;
|
|
}
|
|
|
|
if (!can_have_final_image) {
|
|
return false;
|
|
}
|
|
|
|
ibuf = seq_cache_get(ctx, seq, cfra, SEQ_CACHE_STORE_FINAL_OUT);
|
|
if (ibuf != NULL) {
|
|
IMB_freeImBuf(ibuf);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool seq_prefetch_scene_strip_is_rendered(PrefetchJob *pfjob,
|
|
ListBase *channels,
|
|
ListBase *seqbase,
|
|
SeqCollection *scene_strips,
|
|
bool is_recursive_check)
|
|
{
|
|
float cfra = seq_prefetch_cfra(pfjob);
|
|
Sequence *seq_arr[MAXSEQ + 1];
|
|
int count = seq_get_shown_sequences(pfjob->scene_eval, channels, seqbase, cfra, 0, seq_arr);
|
|
|
|
/* Iterate over rendered strips. */
|
|
for (int i = 0; i < count; i++) {
|
|
Sequence *seq = seq_arr[i];
|
|
if (seq->type == SEQ_TYPE_META &&
|
|
seq_prefetch_scene_strip_is_rendered(pfjob, channels, &seq->seqbase, scene_strips, true)) {
|
|
return true;
|
|
}
|
|
|
|
/* Disable prefetching 3D scene strips, but check for disk cache. */
|
|
if (seq->type == SEQ_TYPE_SCENE && (seq->flag & SEQ_SCENE_STRIPS) == 0 &&
|
|
!seq_prefetch_seq_has_disk_cache(pfjob, seq, !is_recursive_check)) {
|
|
return true;
|
|
}
|
|
|
|
/* Check if strip is effect of scene strip or uses it as modifier. This is recursive check. */
|
|
Sequence *seq_scene;
|
|
SEQ_ITERATOR_FOREACH (seq_scene, scene_strips) {
|
|
if (SEQ_relations_render_loop_check(seq, seq_scene)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static SeqCollection *query_scene_strips(ListBase *seqbase)
|
|
{
|
|
SeqCollection *collection = SEQ_query_all_strips_recursive(seqbase);
|
|
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
|
|
if (seq->type != SEQ_TYPE_SCENE || (seq->flag & SEQ_SCENE_STRIPS) != 0) {
|
|
SEQ_collection_remove_strip(seq, collection);
|
|
}
|
|
}
|
|
return collection;
|
|
}
|
|
|
|
/* Prefetch must avoid rendering scene strips, because rendering in background locks UI and can
|
|
* make it unresponsive for long time periods. */
|
|
static bool seq_prefetch_must_skip_frame(PrefetchJob *pfjob, ListBase *channels, ListBase *seqbase)
|
|
{
|
|
SeqCollection *scene_strips = query_scene_strips(seqbase);
|
|
if (seq_prefetch_scene_strip_is_rendered(pfjob, channels, seqbase, scene_strips, false)) {
|
|
SEQ_collection_free(scene_strips);
|
|
return true;
|
|
}
|
|
SEQ_collection_free(scene_strips);
|
|
return false;
|
|
}
|
|
|
|
static bool seq_prefetch_need_suspend(PrefetchJob *pfjob)
|
|
{
|
|
return seq_prefetch_is_cache_full(pfjob->scene) || seq_prefetch_is_scrubbing(pfjob->bmain) ||
|
|
(seq_prefetch_cfra(pfjob) >= pfjob->scene->r.efra);
|
|
}
|
|
|
|
static void seq_prefetch_do_suspend(PrefetchJob *pfjob)
|
|
{
|
|
BLI_mutex_lock(&pfjob->prefetch_suspend_mutex);
|
|
while (seq_prefetch_need_suspend(pfjob) &&
|
|
(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);
|
|
}
|
|
|
|
static void *seq_prefetch_frames(void *job)
|
|
{
|
|
PrefetchJob *pfjob = (PrefetchJob *)job;
|
|
|
|
while (seq_prefetch_cfra(pfjob) <= pfjob->scene->r.efra) {
|
|
pfjob->scene_eval->ed->prefetch_job = NULL;
|
|
|
|
seq_prefetch_update_depsgraph(pfjob);
|
|
AnimData *adt = BKE_animdata_from_id(&pfjob->context_cpy.scene->id);
|
|
AnimationEvalContext anim_eval_context = seq_prefetch_anim_eval_context(pfjob);
|
|
BKE_animsys_evaluate_animdata(
|
|
&pfjob->context_cpy.scene->id, adt, &anim_eval_context, ADT_RECALC_ALL, false);
|
|
|
|
/* This is quite hacky solution:
|
|
* We need cross-reference original scene with copy for cache.
|
|
* However depsgraph must not have this data, because it will try to kill this job.
|
|
* Scene copy don't reference original scene. Perhaps, this could be done by depsgraph.
|
|
* Set to NULL before return!
|
|
*/
|
|
pfjob->scene_eval->ed->prefetch_job = pfjob;
|
|
|
|
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(pfjob->scene_eval));
|
|
ListBase *channels = SEQ_channels_displayed_get(SEQ_editing_get(pfjob->scene_eval));
|
|
if (seq_prefetch_must_skip_frame(pfjob, channels, seqbase)) {
|
|
pfjob->num_frames_prefetched++;
|
|
continue;
|
|
}
|
|
|
|
ImBuf *ibuf = SEQ_render_give_ibuf(&pfjob->context_cpy, seq_prefetch_cfra(pfjob), 0);
|
|
seq_cache_free_temp_cache(pfjob->scene, pfjob->context.task_id, seq_prefetch_cfra(pfjob));
|
|
IMB_freeImBuf(ibuf);
|
|
|
|
/* Suspend thread if there is nothing to be prefetched. */
|
|
seq_prefetch_do_suspend(pfjob);
|
|
|
|
/* Avoid "collision" with main thread, but make sure to fetch at least few frames */
|
|
if (pfjob->num_frames_prefetched > 5 &&
|
|
(seq_prefetch_cfra(pfjob) - 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++;
|
|
}
|
|
|
|
seq_cache_free_temp_cache(pfjob->scene, pfjob->context.task_id, seq_prefetch_cfra(pfjob));
|
|
pfjob->running = false;
|
|
pfjob->scene_eval->ed->prefetch_job = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PrefetchJob *seq_prefetch_start_ex(const SeqRenderData *context, float cfra)
|
|
{
|
|
PrefetchJob *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_eval = BKE_main_new();
|
|
pfjob->scene = context->scene;
|
|
seq_prefetch_init_depsgraph(pfjob);
|
|
}
|
|
}
|
|
pfjob->bmain = context->bmain;
|
|
|
|
pfjob->cfra = cfra;
|
|
pfjob->num_frames_prefetched = 1;
|
|
|
|
pfjob->waiting = false;
|
|
pfjob->stop = false;
|
|
pfjob->running = true;
|
|
|
|
seq_prefetch_update_scene(context->scene);
|
|
seq_prefetch_update_context(context);
|
|
seq_prefetch_update_active_seqbase(pfjob);
|
|
|
|
BLI_threadpool_remove(&pfjob->threads, pfjob);
|
|
BLI_threadpool_insert(&pfjob->threads, pfjob);
|
|
|
|
return pfjob;
|
|
}
|
|
|
|
void seq_prefetch_start(const SeqRenderData *context, float timeline_frame)
|
|
{
|
|
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,
|
|
* cache storage enabled, has strips to render, not rendering, not doing modal transform -
|
|
* important, see D7820.
|
|
*/
|
|
if ((ed->cache_flag & SEQ_CACHE_PREFETCH_ENABLE) && !running && !scrubbing && !playing &&
|
|
ed->cache_flag & SEQ_CACHE_ALL_TYPES && has_strips && !G.is_rendering && !G.moving) {
|
|
|
|
seq_prefetch_start_ex(context, timeline_frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SEQ_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;
|
|
}
|