diff options
20 files changed, 1068 insertions, 426 deletions
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 87ebf365a1e..bd3eb2bc304 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -159,6 +159,25 @@ class SEQUENCER_MT_view_toggle(Menu): layout.operator("sequencer.view_toggle").type = 'SEQUENCER_PREVIEW' +class SEQUENCER_MT_view_cache(Menu): + bl_label = "Cache" + + def draw(self, context): + layout = self.layout + + ed = context.scene.sequence_editor + layout.prop(ed, "show_cache") + layout.separator() + + col = layout.column() + col.enabled = ed.show_cache + + col.prop(ed, "show_cache_final_out") + col.prop(ed, "show_cache_raw") + col.prop(ed, "show_cache_preprocessed") + col.prop(ed, "show_cache_composite") + + class SEQUENCER_MT_view(Menu): bl_label = "View" @@ -212,6 +231,7 @@ class SEQUENCER_MT_view(Menu): layout.prop(st, "show_frame_indicator") layout.prop(st, "show_strip_offset") layout.prop(st, "show_marker_lines") + layout.menu("SEQUENCER_MT_view_cache") layout.prop_menu_enum(st, "waveform_display_type") @@ -1145,9 +1165,29 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel): layout.prop(strip, "use_float", text="Convert to Float") +class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel): + bl_label = "Cache Settings" + bl_category = "Proxy & Cache" + + @classmethod + def poll(cls, context): + return cls.has_sequencer(context) + + def draw(self, context): + layout = self.layout + ed = context.scene.sequence_editor + + layout.prop(ed, "cache_raw") + layout.prop(ed, "cache_preprocessed") + layout.prop(ed, "cache_composite") + layout.prop(ed, "cache_final") + layout.separator() + layout.prop(ed, "recycle_max_cost") + + class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel): - bl_label = "Proxy settings" - bl_category = "Proxy" + bl_label = "Proxy Settings" + bl_category = "Proxy & Cache" @classmethod def poll(cls, context): return cls.has_sequencer(context) @@ -1168,8 +1208,8 @@ class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel): class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): - bl_label = "Strip Proxy/Timecode" - bl_category = "Proxy" + bl_label = "Strip Proxy & Timecode" + bl_category = "Proxy & Cache" @classmethod def poll(cls, context): @@ -1225,8 +1265,33 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): col.prop(proxy, "timecode") +class SEQUENCER_PT_strip_cache(SequencerButtonsPanel, Panel): + bl_label = "Strip Cache" + bl_category = "Proxy & Cache" + + @classmethod + def poll(cls, context): + if not cls.has_sequencer(context): + return False + if act_strip(context) is not None: + return True + + def draw_header(self, context): + strip = act_strip(context) + self.layout.prop(strip, "override_cache_settings", text="") + + def draw(self, context): + layout = self.layout + strip = act_strip(context) + layout.active = strip.override_cache_settings + + layout.prop(strip, "cache_raw") + layout.prop(strip, "cache_preprocessed") + layout.prop(strip, "cache_composite") + + class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel): - bl_label = "Scene Preview/Render" + bl_label = "Scene Preview & Render" bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' bl_category = "Strip" @@ -1411,6 +1476,7 @@ classes = ( SEQUENCER_HT_header, SEQUENCER_MT_editor_menus, SEQUENCER_MT_view, + SEQUENCER_MT_view_cache, SEQUENCER_MT_view_toggle, SEQUENCER_MT_select, SEQUENCER_MT_marker, @@ -1430,8 +1496,10 @@ classes = ( SEQUENCER_PT_scene, SEQUENCER_PT_mask, SEQUENCER_PT_filter, + SEQUENCER_PT_cache_settings, SEQUENCER_PT_proxy_settings, SEQUENCER_PT_strip_proxy, + SEQUENCER_PT_strip_cache, SEQUENCER_PT_preview, SEQUENCER_PT_view, SEQUENCER_PT_view_safe_areas, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 16655d9b060..e01f6a6b751 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ * \note Use #STRINGIFY() rather than defining with quotes. */ #define BLENDER_VERSION 280 -#define BLENDER_SUBVERSION 59 +#define BLENDER_SUBVERSION 60 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 820f28fb363..5485b2d3619 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -302,46 +302,33 @@ void BKE_sequencer_proxy_set(struct Sequence *seq, bool value); * Sequencer memory cache management functions * ********************************************************************** */ -typedef enum { - SEQ_STRIPELEM_IBUF, - SEQ_STRIPELEM_IBUF_COMP, - SEQ_STRIPELEM_IBUF_STARTSTILL, - SEQ_STRIPELEM_IBUF_ENDSTILL, -} eSeqStripElemIBuf; +#define SEQ_CACHE_COST_MAX 10.0f -void BKE_sequencer_cache_destruct(void); -void BKE_sequencer_cache_cleanup(void); - -/* returned ImBuf is properly refed and has to be freed */ struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context, struct Sequence *seq, float cfra, - eSeqStripElemIBuf type); - -/* passed ImBuf is properly refed, so ownership is *not* - * transferred to the cache. - * you can pass the same ImBuf multiple times to the cache without problems. - */ - + int type); void BKE_sequencer_cache_put(const SeqRenderData *context, struct Sequence *seq, float cfra, - eSeqStripElemIBuf type, - struct ImBuf *nval); - -void BKE_sequencer_cache_cleanup_sequence(struct Sequence *seq); - -struct ImBuf *BKE_sequencer_preprocessed_cache_get(const SeqRenderData *context, - struct Sequence *seq, - float cfra, - eSeqStripElemIBuf type); -void BKE_sequencer_preprocessed_cache_put(const SeqRenderData *context, - struct Sequence *seq, - float cfra, - eSeqStripElemIBuf type, - struct ImBuf *ibuf); -void BKE_sequencer_preprocessed_cache_cleanup(void); -void BKE_sequencer_preprocessed_cache_cleanup_sequence(struct Sequence *seq); + int type, + struct ImBuf *nval, + float cost); +bool BKE_sequencer_cache_put_if_possible(const SeqRenderData *context, + struct Sequence *seq, + float cfra, + int type, + struct ImBuf *nval, + float cost); +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); +void BKE_sequencer_cache_cleanup(struct Scene *scene); +void BKE_sequencer_cache_cleanup_sequence(struct Scene *scene, struct Sequence *seq); +void BKE_sequencer_cache_iterate( + struct Scene *scene, + void *userdata, + bool callback(void *userdata, struct Sequence *seq, int cfra, int cache_type, float cost)); /* ********************************************************************** * seqeffects.c diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index d6599498a65..48b271cf277 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -97,7 +97,6 @@ void BKE_blender_free(void) BLI_callback_global_finalize(); - BKE_sequencer_cache_destruct(); IMB_moviecache_destruct(); free_nodesystem(); diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index b022819ca8c..c50f3f3f141 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -21,49 +21,84 @@ */ #include <stddef.h> - -#include "BLI_sys_types.h" /* for intptr_t */ +#include <memory.h> #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" -#include "IMB_moviecache.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "BLI_mempool.h" +#include "BLI_threads.h" #include "BLI_listbase.h" +#include "BLI_ghash.h" #include "BKE_sequencer.h" #include "BKE_scene.h" +#include "BKE_main.h" + +/* ***************************** 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 pernament (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: + * - one image may be needed multiple times during rendering. + * - keeping the last rendered frame allows us for faster re-render when user edits strip in stack + * - we can decide if we keep frame only when it's completely rendered. Otherwise we risk having + * "holes" in the cache, which can be annoying + * If the cache is full all entries for pending frame will have is_temp_cache set. + * + * 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. + * + * User can exclude caching of some images. Such entries will have is_temp_cache set. + */ + +typedef struct SeqCache { + struct GHash *hash; + ThreadMutex iterator_mutex; + struct BLI_mempool *keys_pool; + struct BLI_mempool *items_pool; + struct SeqCacheKey *last_key; + size_t memory_used; +} SeqCache; + +typedef struct SeqCacheItem { + struct SeqCache *cache_owner; + struct ImBuf *ibuf; +} SeqCacheItem; typedef struct SeqCacheKey { + struct SeqCache *cache_owner; + void *userkey; + struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame */ + struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame */ struct Sequence *seq; SeqRenderData context; float cfra; - eSeqStripElemIBuf type; + float nfra; + float cost; + bool is_temp_cache; + short creator_id; + int type; } SeqCacheKey; -typedef struct SeqPreprocessCacheElem { - struct SeqPreprocessCacheElem *next, *prev; - - struct Sequence *seq; - SeqRenderData context; - eSeqStripElemIBuf type; - - ImBuf *ibuf; -} SeqPreprocessCacheElem; - -typedef struct SeqPreprocessCache { - int cfra; - ListBase elems; -} SeqPreprocessCache; - -static struct MovieCache *moviecache = NULL; -static struct SeqPreprocessCache *preprocess_cache = NULL; - -static void preprocessed_cache_destruct(void); +static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) { @@ -88,209 +123,536 @@ static unsigned int seq_hash_render_data(const SeqRenderData *a) return rval; } -static unsigned int seqcache_hashhash(const void *key_) +static unsigned int seq_cache_hashhash(const void *key_) { const SeqCacheKey *key = key_; unsigned int rval = seq_hash_render_data(&key->context); - rval ^= *(const unsigned int *)&key->cfra; + rval ^= *(const unsigned int *)&key->nfra; rval += key->type; rval ^= ((intptr_t)key->seq) << 6; return rval; } -static bool seqcache_hashcmp(const void *a_, const void *b_) +static bool seq_cache_hashcmp(const void *a_, const void *b_) { const SeqCacheKey *a = a_; const SeqCacheKey *b = b_; - return ((a->seq != b->seq) || (a->cfra != b->cfra) || (a->type != b->type) || + return ((a->seq != b->seq) || (a->nfra != b->nfra) || (a->type != b->type) || seq_cmp_render_data(&a->context, &b->context)); } -void BKE_sequencer_cache_destruct(void) +static SeqCache *seq_cache_get_from_scene(Scene *scene) { - if (moviecache) { - IMB_moviecache_free(moviecache); + if (scene && scene->ed && scene->ed->cache) { + return scene->ed->cache; } - preprocessed_cache_destruct(); + return NULL; +} + +static void seq_cache_lock(Scene *scene) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + + if (cache) { + BLI_mutex_lock(&cache->iterator_mutex); + } } -void BKE_sequencer_cache_cleanup(void) +static void seq_cache_unlock(Scene *scene) { - if (moviecache) { - IMB_moviecache_free(moviecache); - moviecache = IMB_moviecache_create( - "seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); + SeqCache *cache = seq_cache_get_from_scene(scene); + + if (cache) { + BLI_mutex_unlock(&cache->iterator_mutex); } +} - BKE_sequencer_preprocessed_cache_cleanup(); +static void seq_cache_keyfree(void *val) +{ + SeqCacheKey *key = val; + BLI_mempool_free(key->cache_owner->keys_pool, key); } -static bool seqcache_key_check_seq(ImBuf *UNUSED(ibuf), void *userkey, void *userdata) +static void seq_cache_valfree(void *val) { - SeqCacheKey *key = (SeqCacheKey *)userkey; - Sequence *seq = (Sequence *)userdata; + SeqCacheItem *item = (SeqCacheItem *)val; + SeqCache *cache = item->cache_owner; + + if (item->ibuf) { + cache->memory_used -= IMB_get_size_in_memory(item->ibuf); + IMB_freeImBuf(item->ibuf); + } - return key->seq == seq; + BLI_mempool_free(item->cache_owner->items_pool, item); } -void BKE_sequencer_cache_cleanup_sequence(Sequence *seq) +static void seq_cache_put(SeqCache *cache, SeqCacheKey *key, ImBuf *ibuf) { - if (moviecache) { - IMB_moviecache_cleanup(moviecache, seqcache_key_check_seq, seq); + SeqCacheItem *item; + item = BLI_mempool_alloc(cache->items_pool); + item->cache_owner = cache; + item->ibuf = ibuf; + + if (BLI_ghash_reinsert(cache->hash, key, item, seq_cache_keyfree, seq_cache_valfree)) { + IMB_refImBuf(ibuf); + cache->last_key = key; + cache->memory_used += IMB_get_size_in_memory(ibuf); } } -struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context, - Sequence *seq, - float cfra, - eSeqStripElemIBuf type) +static ImBuf *seq_cache_get(SeqCache *cache, void *key) { - if (moviecache && seq) { - SeqCacheKey key; + SeqCacheItem *item = BLI_ghash_lookup(cache->hash, key); - key.seq = seq; - key.context = *context; - key.cfra = cfra - seq->start; - key.type = type; + if (item && item->ibuf) { + IMB_refImBuf(item->ibuf); - return IMB_moviecache_get(moviecache, &key); + return item->ibuf; } return NULL; } -void BKE_sequencer_cache_put( - const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type, ImBuf *i) +static void seq_cache_relink_keys(SeqCacheKey *link_next, SeqCacheKey *link_prev) +{ + if (link_next) { + link_next->link_prev = link_prev; + } + if (link_prev) { + link_prev->link_next = link_next; + } +} + +static SeqCacheKey *seq_cache_choose_key(Scene *scene, SeqCacheKey *lkey, SeqCacheKey *rkey) { - SeqCacheKey key; + SeqCacheKey *finalkey = NULL; + + if (rkey && lkey) { + if (lkey->cfra > rkey->cfra) { + SeqCacheKey *swapkey = lkey; + lkey = rkey; + rkey = swapkey; + } + + int l_diff = scene->r.cfra - lkey->cfra; + int r_diff = rkey->cfra - scene->r.cfra; + + if (l_diff > r_diff) { + finalkey = lkey; + } + else { + finalkey = rkey; + } + } + else { + if (lkey) { + finalkey = lkey; + } + else { + finalkey = rkey; + } + } + return finalkey; +} - if (i == NULL || context->skip_cache) { +static void seq_cache_recycle_linked(Scene *scene, SeqCacheKey *base) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { return; } - if (!moviecache) { - moviecache = IMB_moviecache_create( - "seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); + SeqCacheKey *next = base->link_next; + + while (base) { + SeqCacheKey *prev = base->link_prev; + BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree); + base = prev; + } + + base = next; + while (base) { + next = base->link_next; + BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree); + base = next; + } +} + +static SeqCacheKey *seq_cache_get_item_for_removal(Scene *scene) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + SeqCacheKey *finalkey = NULL; + /*leftmost key*/ + SeqCacheKey *lkey = NULL; + /*rightmost key*/ + SeqCacheKey *rkey = NULL; + SeqCacheKey *key = NULL; + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + int total_count = 0; + int cheap_count = 0; + + while (!BLI_ghashIterator_done(&gh_iter)) { + key = BLI_ghashIterator_getKey(&gh_iter); + SeqCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); + BLI_ghashIterator_step(&gh_iter); + + /* this shouldn't happen, but better be safe than sorry */ + if (!item->ibuf) { + seq_cache_recycle_linked(scene, key); + /* can not continue iterating after linked remove */ + BLI_ghashIterator_init(&gh_iter, cache->hash); + continue; + } + + if (key->is_temp_cache || key->link_next != NULL) { + continue; + } + + total_count++; + + if (key->cost <= scene->ed->recycle_max_cost) { + cheap_count++; + if (lkey) { + if (key->cfra < lkey->cfra) { + lkey = key; + } + } + else { + lkey = key; + } + if (rkey) { + if (key->cfra > rkey->cfra) { + rkey = key; + } + } + else { + rkey = key; + } + } + } + + 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) +{ + size_t memory_total = ((size_t)U.memcachelimit) * 1024 * 1024; + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return false; } - key.seq = seq; - key.context = *context; - key.cfra = cfra - seq->start; - key.type = type; + seq_cache_lock(scene); + + while (cache->memory_used > memory_total) { + SeqCacheKey *finalkey = seq_cache_get_item_for_removal(scene); - IMB_moviecache_put(moviecache, &key, i); + if (finalkey) { + seq_cache_recycle_linked(scene, finalkey); + } + else { + seq_cache_unlock(scene); + return false; + } + } + seq_cache_unlock(scene); + return true; } -void BKE_sequencer_preprocessed_cache_cleanup(void) +static void seq_cache_set_temp_cache_linked(Scene *scene, SeqCacheKey *base) { - SeqPreprocessCacheElem *elem; + SeqCache *cache = seq_cache_get_from_scene(scene); - if (!preprocess_cache) { + if (!cache || !base) { return; } - for (elem = preprocess_cache->elems.first; elem; elem = elem->next) { - IMB_freeImBuf(elem->ibuf); + SeqCacheKey *next = base->link_next; + + while (base) { + SeqCacheKey *prev = base->link_prev; + base->is_temp_cache = true; + base = prev; } - BLI_freelistN(&preprocess_cache->elems); - BLI_listbase_clear(&preprocess_cache->elems); + base = next; + while (base) { + next = base->link_next; + base->is_temp_cache = true; + base = next; + } } -static void preprocessed_cache_destruct(void) +static void BKE_sequencer_cache_create(Scene *scene) { - if (!preprocess_cache) { + BLI_mutex_lock(&cache_create_lock); + if (scene->ed->cache == NULL) { + SeqCache *cache = MEM_callocN(sizeof(SeqCache), "SeqCache"); + cache->keys_pool = BLI_mempool_create(sizeof(SeqCacheKey), 0, 64, BLI_MEMPOOL_NOP); + cache->items_pool = BLI_mempool_create(sizeof(SeqCacheItem), 0, 64, BLI_MEMPOOL_NOP); + cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash"); + cache->last_key = NULL; + BLI_mutex_init(&cache->iterator_mutex); + scene->ed->cache = cache; + } + BLI_mutex_unlock(&cache_create_lock); +} + +/* ***************************** API ****************************** */ + +void BKE_sequencer_cache_free_temp_cache(Scene *scene, short id, int cfra) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { return; } - BKE_sequencer_preprocessed_cache_cleanup(); + seq_cache_lock(scene); - MEM_freeN(preprocess_cache); - preprocess_cache = NULL; + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + while (!BLI_ghashIterator_done(&gh_iter)) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + BLI_ghashIterator_step(&gh_iter); + + if (key->is_temp_cache && key->creator_id == id && key->cfra != cfra) { + BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); + } + } + seq_cache_unlock(scene); } -ImBuf *BKE_sequencer_preprocessed_cache_get(const SeqRenderData *context, - Sequence *seq, - float cfra, - eSeqStripElemIBuf type) +void BKE_sequencer_cache_destruct(Scene *scene) { - SeqPreprocessCacheElem *elem; + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return; + } - if (!preprocess_cache) { - return NULL; + BLI_ghash_free(cache->hash, seq_cache_keyfree, seq_cache_valfree); + BLI_mempool_destroy(cache->keys_pool); + BLI_mempool_destroy(cache->items_pool); + BLI_mutex_end(&cache->iterator_mutex); + MEM_freeN(cache); + scene->ed->cache = NULL; +} + +void BKE_sequencer_cache_cleanup_all(Main *bmain) +{ + for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { + BKE_sequencer_cache_cleanup(scene); + } +} +void BKE_sequencer_cache_cleanup(Scene *scene) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return; } - if (preprocess_cache->cfra != cfra) { - return NULL; + seq_cache_lock(scene); + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + while (!BLI_ghashIterator_done(&gh_iter)) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + + BLI_ghashIterator_step(&gh_iter); + BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); } + cache->last_key = NULL; + seq_cache_unlock(scene); +} - for (elem = preprocess_cache->elems.first; elem; elem = elem->next) { - if (elem->seq != seq) { - continue; - } +void BKE_sequencer_cache_cleanup_sequence(Scene *scene, Sequence *seq) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return; + } - if (elem->type != type) { - continue; - } + seq_cache_lock(scene); - if (seq_cmp_render_data(&elem->context, context) != 0) { - continue; + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + while (!BLI_ghashIterator_done(&gh_iter)) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + BLI_ghashIterator_step(&gh_iter); + + if (key->seq == seq) { + /* Relink keys, so we don't end up with orphaned keys */ + if (key->link_next || key->link_prev) { + seq_cache_relink_keys(key->link_next, key->link_prev); + } + + BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); } + } + cache->last_key = NULL; + seq_cache_unlock(scene); +} + +struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context, + Sequence *seq, + float cfra, + int type) +{ + Scene *scene = context->scene; - IMB_refImBuf(elem->ibuf); - return elem->ibuf; + if (!scene->ed->cache) { + BKE_sequencer_cache_create(scene); + return NULL; } - return NULL; + seq_cache_lock(scene); + SeqCache *cache = seq_cache_get_from_scene(scene); + ImBuf *ibuf = NULL; + + if (cache && seq) { + SeqCacheKey key; + + key.seq = seq; + key.context = *context; + key.nfra = cfra - seq->start; + key.type = type; + + ibuf = seq_cache_get(cache, &key); + } + seq_cache_unlock(scene); + + return ibuf; } -void BKE_sequencer_preprocessed_cache_put( - const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type, ImBuf *ibuf) +bool BKE_sequencer_cache_put_if_possible( + const SeqRenderData *context, Sequence *seq, float cfra, int type, ImBuf *ibuf, float cost) { - SeqPreprocessCacheElem *elem; + Scene *scene = context->scene; - if (!preprocess_cache) { - preprocess_cache = MEM_callocN(sizeof(SeqPreprocessCache), "sequencer preprocessed cache"); + if (seq_cache_recycle_item(scene)) { + BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, cost); + return true; } else { - if (preprocess_cache->cfra != cfra) { - BKE_sequencer_preprocessed_cache_cleanup(); - } + seq_cache_set_temp_cache_linked(scene, scene->ed->cache->last_key); + scene->ed->cache->last_key = NULL; + return false; } +} + +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; - elem = MEM_callocN(sizeof(SeqPreprocessCacheElem), "sequencer preprocessed cache element"); + if (i == NULL || context->skip_cache || context->is_proxy_render || !seq) { + return; + } - elem->seq = seq; - elem->type = type; - elem->context = *context; - elem->ibuf = ibuf; + /* Prevent reinserting, it breaks cache key linking */ + ImBuf *test = BKE_sequencer_cache_get(context, seq, cfra, type); + if (test) { + IMB_freeImBuf(test); + return; + } - preprocess_cache->cfra = cfra; + if (!scene->ed->cache) { + BKE_sequencer_cache_create(scene); + } - IMB_refImBuf(ibuf); + seq_cache_lock(scene); - BLI_addtail(&preprocess_cache->elems, elem); + SeqCache *cache = seq_cache_get_from_scene(scene); + int flag; + + if (seq->cache_flag & SEQ_CACHE_OVERRIDE) { + flag = seq->cache_flag; + flag |= scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT; + } + else { + flag = scene->ed->cache_flag; + } + + if (cost > SEQ_CACHE_COST_MAX) { + cost = SEQ_CACHE_COST_MAX; + } + + SeqCacheKey *key; + key = BLI_mempool_alloc(cache->keys_pool); + key->cache_owner = cache; + key->seq = seq; + key->context = *context; + key->cfra = cfra; + key->nfra = cfra - seq->start; + key->type = type; + key->cost = cost; + key->cache_owner = cache; + key->link_prev = NULL; + key->link_next = NULL; + key->is_temp_cache = true; + key->creator_id = creator_id; + + /* Item stored for later use */ + if (flag & type) { + key->is_temp_cache = false; + key->link_prev = cache->last_key; + } + + SeqCacheKey *temp_last_key = cache->last_key; + seq_cache_put(cache, key, i); + + /* Restore pointer to previous item as this one will be freed when stack is rendered */ + if (key->is_temp_cache) { + cache->last_key = temp_last_key; + } + + /* Set last_key's reference to this key so we can look up chain backwards + * Item is already put in cache, so cache->last_key points to current key; + */ + if (flag & type && temp_last_key) { + temp_last_key->link_next = cache->last_key; + } + + /* Reset linking */ + if (key->type == SEQ_CACHE_STORE_FINAL_OUT) { + cache->last_key = NULL; + } + + seq_cache_unlock(scene); } -void BKE_sequencer_preprocessed_cache_cleanup_sequence(Sequence *seq) +void BKE_sequencer_cache_iterate( + struct Scene *scene, + void *userdata, + bool callback(void *userdata, struct Sequence *seq, int cfra, int cache_type, float cost)) { - SeqPreprocessCacheElem *elem, *elem_next; - - if (!preprocess_cache) { + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { return; } - for (elem = preprocess_cache->elems.first; elem; elem = elem_next) { - elem_next = elem->next; + seq_cache_lock(scene); + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + bool interrupt = false; - if (elem->seq == seq) { - IMB_freeImBuf(elem->ibuf); + while (!BLI_ghashIterator_done(&gh_iter) && !interrupt) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + BLI_ghashIterator_step(&gh_iter); - BLI_freelinkN(&preprocess_cache->elems, elem); - } + interrupt = callback(userdata, key->seq, key->cfra, key->type, key->cost); } + + cache->last_key = NULL; + seq_cache_unlock(scene); } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 68c2869b5d2..2887f30532e 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -27,7 +27,7 @@ #include <stddef.h> #include <stdlib.h> #include <string.h> -#include <math.h> +#include <time.h> #include "MEM_guardedalloc.h" @@ -464,6 +464,11 @@ Editing *BKE_sequencer_editing_ensure(Scene *scene) ed = scene->ed = MEM_callocN(sizeof(Editing), "addseq"); ed->seqbasep = &ed->seqbase; + ed->cache = NULL; + ed->cache_flag = SEQ_CACHE_STORE_FINAL_OUT; + ed->cache_flag |= SEQ_CACHE_VIEW_FINAL_OUT; + ed->cache_flag |= SEQ_CACHE_VIEW_ENABLE; + ed->recycle_max_cost = 10.0f; } return scene->ed; @@ -478,8 +483,7 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user) return; } - /* this may not be the active scene!, could be smarter about this */ - BKE_sequencer_cache_cleanup(); + BKE_sequencer_cache_destruct(scene); SEQ_BEGIN (ed, seq) { /* handle cache freeing above */ @@ -2639,7 +2643,7 @@ bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, { float mul; - if (context->is_proxy_render) { + if (context && context->is_proxy_render) { return false; } @@ -2809,54 +2813,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, return ibuf; } -static ImBuf *copy_from_ibuf_still(const SeqRenderData *context, Sequence *seq, float nr) -{ - ImBuf *rval = NULL; - ImBuf *ibuf = NULL; - - if (nr == 0) { - ibuf = BKE_sequencer_cache_get(context, seq, seq->start, SEQ_STRIPELEM_IBUF_STARTSTILL); - } - else if (nr == seq->len - 1) { - ibuf = BKE_sequencer_cache_get(context, seq, seq->start, SEQ_STRIPELEM_IBUF_ENDSTILL); - } - - if (ibuf) { - rval = IMB_dupImBuf(ibuf); - IMB_metadata_copy(rval, ibuf); - IMB_freeImBuf(ibuf); - } - - return rval; -} - -static void copy_to_ibuf_still(const SeqRenderData *context, Sequence *seq, float nr, ImBuf *ibuf) -{ - /* warning: ibuf may be NULL if the video fails to load */ - if (nr == 0 || nr == seq->len - 1) { - /* we have to store a copy, since the passed ibuf - * could be preprocessed afterwards (thereby silently - * changing the cached image... */ - ImBuf *oibuf = ibuf; - ibuf = IMB_dupImBuf(oibuf); - - if (ibuf) { - IMB_metadata_copy(ibuf, oibuf); - sequencer_imbuf_assign_spaces(context->scene, ibuf); - } - - if (nr == 0) { - BKE_sequencer_cache_put(context, seq, seq->start, SEQ_STRIPELEM_IBUF_STARTSTILL, ibuf); - } - - if (nr == seq->len - 1) { - BKE_sequencer_cache_put(context, seq, seq->start, SEQ_STRIPELEM_IBUF_ENDSTILL, ibuf); - } - - IMB_freeImBuf(ibuf); - } -} - /*********************** strip rendering functions *************************/ typedef struct RenderEffectInitData { @@ -3062,7 +3018,7 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, static ImBuf *seq_render_image_strip(const SeqRenderData *context, Sequence *seq, - float nr, + float UNUSED(nr), float cfra) { ImBuf *ibuf = NULL; @@ -3138,8 +3094,8 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); if (i != context->view_id) { - copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); - BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + BKE_sequencer_cache_put( + &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibufs_arr[i], 0); } } } @@ -3253,8 +3209,8 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false); } if (i != context->view_id) { - copy_to_ibuf_still(&localcontext, seq, nr, ibuf_arr[i]); - BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf_arr[i]); + BKE_sequencer_cache_put( + &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[i], 0); } } @@ -3656,8 +3612,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, } if (i != context->view_id) { - copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); - BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[i], 0); } RE_ReleaseResultImage(re); @@ -3781,8 +3736,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, /* Scene strips update all animation, so we need to restore original state.*/ BKE_animsys_evaluate_all_animation( context->bmain, context->depsgraph, context->scene, cfra); - - copy_to_ibuf_still(context, seq, nr, ibuf); } break; } @@ -3820,13 +3773,11 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, case SEQ_TYPE_IMAGE: { ibuf = seq_render_image_strip(context, seq, nr, cfra); - copy_to_ibuf_still(context, seq, nr, ibuf); break; } case SEQ_TYPE_MOVIE: { ibuf = seq_render_movie_strip(context, seq, nr, cfra); - copy_to_ibuf_still(context, seq, nr, ibuf); break; } @@ -3842,8 +3793,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, if (ibuf->rect_float) { BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); } - - copy_to_ibuf_still(context, seq, nr, ibuf); } break; @@ -3852,8 +3801,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, case SEQ_TYPE_MASK: { /* ibuf is always new */ ibuf = seq_render_mask_strip(context, seq, nr); - - copy_to_ibuf_still(context, seq, nr, ibuf); break; } } @@ -3865,6 +3812,26 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, return ibuf; } +/* Estimate time spent by the program rendering the strip */ +static clock_t seq_estimate_render_cost_begin(void) +{ + return clock(); +} + +static float seq_estimate_render_cost_end(Scene *scene, clock_t begin) +{ + clock_t end = clock(); + float time_spent = (float)(end - begin); + float time_max = (1.0f / scene->r.frs_sec) * CLOCKS_PER_SEC; + + if (time_max != 0) { + return time_spent / time_max; + } + else { + return 1; + } +} + static ImBuf *seq_render_strip(const SeqRenderData *context, SeqRenderState *state, Sequence *seq, @@ -3873,37 +3840,32 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, ImBuf *ibuf = NULL; bool use_preprocess = false; bool is_proxy_image = false; - float nr = give_stripelem_index(seq, cfra); /* all effects are handled similarly with the exception of speed effect */ int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : seq->type; bool is_preprocessed = !ELEM( type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP); - ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF); + clock_t begin = seq_estimate_render_cost_begin(); - if (ibuf == NULL) { - ibuf = copy_from_ibuf_still(context, seq, nr); + ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED); + if (ibuf == NULL) { + ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_RAW); if (ibuf == NULL) { - ibuf = BKE_sequencer_preprocessed_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF); + /* MOVIECLIPs have their own proxy management */ + if (seq->type != SEQ_TYPE_MOVIECLIP) { + ibuf = seq_proxy_fetch(context, seq, cfra); + is_proxy_image = (ibuf != NULL); + } if (ibuf == NULL) { - /* MOVIECLIPs have their own proxy management */ - if (seq->type != SEQ_TYPE_MOVIECLIP) { - ibuf = seq_proxy_fetch(context, seq, cfra); - is_proxy_image = (ibuf != NULL); - } - - if (ibuf == NULL) { - ibuf = do_render_strip_uncached(context, state, seq, cfra); - } + ibuf = do_render_strip_uncached(context, state, seq, cfra); + } - if (ibuf) { - if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_MOVIECLIP)) { - is_proxy_image = (context->preview_render_size != 100); - } - BKE_sequencer_preprocessed_cache_put(context, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf); + if (ibuf) { + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_MOVIECLIP)) { + is_proxy_image = (context->preview_render_size != 100); } } } @@ -3911,30 +3873,29 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, if (ibuf) { use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra); } - } - else { - /* currently, we cache preprocessed images in SEQ_STRIPELEM_IBUF, - * but not(!) on SEQ_STRIPELEM_IBUF_ENDSTILL and ..._STARTSTILL - * so, no need in check for preprocess here - */ - } - if (ibuf == NULL) { - ibuf = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect); - sequencer_imbuf_assign_spaces(context->scene, ibuf); - } + if (ibuf == NULL) { + ibuf = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect); + sequencer_imbuf_assign_spaces(context->scene, ibuf); + } - if (context->is_proxy_render == false && - (ibuf->x != context->rectx || ibuf->y != context->recty)) { - use_preprocess = true; - } + if (context->is_proxy_render == false && + (ibuf->x != context->rectx || ibuf->y != context->recty)) { + use_preprocess = true; + } - if (use_preprocess) { - ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed); - } + if (use_preprocess) { + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost); - BKE_sequencer_cache_put(context, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf); + /* reset timer so we can get partial render time */ + begin = seq_estimate_render_cost_begin(); + ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed); + } + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost); + } return ibuf; } @@ -4015,6 +3976,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, int count; int i; ImBuf *out = NULL; + clock_t begin; count = get_shown_sequences(seqbasep, cfra, chanshown, (Sequence **)&seq_arr); @@ -4022,73 +3984,11 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, return NULL; } -#if 0 /* commentind since this breaks keyframing, since it resets the value on draw */ - if (scene->r.cfra != cfra) { - /* XXX for prefetch and overlay offset!..., very bad!!! */ - AnimData *adt = BKE_animdata_from_id(&scene->id); - BKE_animsys_evaluate_animdata(scene, &scene->id, adt, cfra, ADT_RECALC_ANIM); - } -#endif - - out = BKE_sequencer_cache_get(context, seq_arr[count - 1], cfra, SEQ_STRIPELEM_IBUF_COMP); - - if (out) { - return out; - } - - if (count == 1) { - Sequence *seq = seq_arr[0]; - - /* Some of the blend modes are unclear how to apply with only single input, - * or some of them will just produce an empty result.. - */ - if (ELEM(seq->blend_mode, SEQ_BLEND_REPLACE, SEQ_TYPE_CROSS, SEQ_TYPE_ALPHAOVER)) { - int early_out; - if (seq->blend_mode == SEQ_BLEND_REPLACE) { - early_out = EARLY_NO_INPUT; - } - else { - early_out = seq_get_early_out_for_blend_mode(seq); - } - - if (ELEM(early_out, EARLY_NO_INPUT, EARLY_USE_INPUT_2)) { - out = seq_render_strip(context, state, seq, cfra); - } - else if (early_out == EARLY_USE_INPUT_1) { - out = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect); - } - else { - out = seq_render_strip(context, state, seq, cfra); - - if (early_out == EARLY_DO_EFFECT) { - ImBuf *ibuf1 = IMB_allocImBuf( - context->rectx, context->recty, 32, out->rect_float ? IB_rectfloat : IB_rect); - ImBuf *ibuf2 = out; - - out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2); - if (out) { - IMB_metadata_copy(out, ibuf2); - } - - IMB_freeImBuf(ibuf1); - IMB_freeImBuf(ibuf2); - } - } - } - else { - out = seq_render_strip(context, state, seq, cfra); - } - - BKE_sequencer_cache_put(context, seq, cfra, SEQ_STRIPELEM_IBUF_COMP, out); - - return out; - } - for (i = count - 1; i >= 0; i--) { int early_out; Sequence *seq = seq_arr[i]; - out = BKE_sequencer_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF_COMP); + out = BKE_sequencer_cache_get(context, seq, cfra, SEQ_CACHE_STORE_COMPOSITE); if (out) { break; @@ -4112,15 +4012,19 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, break; case EARLY_DO_EFFECT: if (i == 0) { + begin = seq_estimate_render_cost_begin(); + ImBuf *ibuf1 = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect); ImBuf *ibuf2 = seq_render_strip(context, state, seq, cfra); out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2); + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost); + IMB_freeImBuf(ibuf1); IMB_freeImBuf(ibuf2); } - break; } if (out) { @@ -4128,11 +4032,9 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, } } - BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_STRIPELEM_IBUF_COMP, out); - i++; - for (; i < count; i++) { + begin = seq_estimate_render_cost_begin(); Sequence *seq = seq_arr[i]; if (seq_get_early_out_for_blend_mode(seq) == EARLY_DO_EFFECT) { @@ -4145,7 +4047,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, IMB_freeImBuf(ibuf2); } - BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_STRIPELEM_IBUF_COMP, out); + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, out, cost); } return out; @@ -4158,7 +4061,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int chanshown) { - Editing *ed = BKE_sequencer_editing_get(context->scene, false); + Scene *scene = context->scene; + Editing *ed = BKE_sequencer_editing_get(scene, false); ListBase *seqbasep; if (ed == NULL) { @@ -4176,8 +4080,29 @@ ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int cha SeqRenderState state; sequencer_state_init(&state); + ImBuf *out = NULL; + Sequence *seq_arr[MAXSEQ + 1]; + int count; - return seq_render_strip_stack(context, &state, seqbasep, cfra, chanshown); + count = get_shown_sequences(seqbasep, cfra, chanshown, seq_arr); + + if (count) { + 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); + + clock_t begin = seq_estimate_render_cost_begin(); + float cost = 0; + + if (count && !out) { + 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); + } + + return out; } ImBuf *BKE_sequencer_give_ibuf_seqbase(const SeqRenderData *context, @@ -4196,7 +4121,9 @@ ImBuf *BKE_sequencer_give_ibuf_direct(const SeqRenderData *context, float cfra, SeqRenderState state; sequencer_state_init(&state); - return seq_render_strip(context, &state, seq, cfra); + ImBuf *ibuf = seq_render_strip(context, &state, seq, cfra); + + return ibuf; } /* *********************** threading api ******************* */ @@ -4368,7 +4295,7 @@ bool BKE_sequence_check_depend(Sequence *seq, Sequence *cur) return true; } -static void sequence_do_invalidate_dependent(Sequence *seq, ListBase *seqbase) +static void sequence_do_invalidate_dependent(Scene *scene, Sequence *seq, ListBase *seqbase) { Sequence *cur; @@ -4378,12 +4305,11 @@ static void sequence_do_invalidate_dependent(Sequence *seq, ListBase *seqbase) } if (BKE_sequence_check_depend(seq, cur)) { - BKE_sequencer_cache_cleanup_sequence(cur); - BKE_sequencer_preprocessed_cache_cleanup_sequence(cur); + BKE_sequencer_cache_cleanup_sequence(scene, cur); } if (cur->seqbase.first) { - sequence_do_invalidate_dependent(seq, &cur->seqbase); + sequence_do_invalidate_dependent(scene, seq, &cur->seqbase); } } } @@ -4391,7 +4317,7 @@ static void sequence_do_invalidate_dependent(Sequence *seq, ListBase *seqbase) static void sequence_invalidate_cache(Scene *scene, Sequence *seq, bool invalidate_self, - bool invalidate_preprocess) + bool UNUSED(invalidate_preprocess)) { Editing *ed = scene->ed; @@ -4402,7 +4328,7 @@ static void sequence_invalidate_cache(Scene *scene, * re-open the animation. */ BKE_sequence_free_anim(seq); - BKE_sequencer_cache_cleanup_sequence(seq); + BKE_sequencer_cache_cleanup_sequence(scene, seq); } /* if invalidation is invoked from sequence free routine, effectdata would be NULL here */ @@ -4410,16 +4336,12 @@ static void sequence_invalidate_cache(Scene *scene, BKE_sequence_effect_speed_rebuild_map(scene, seq, true); } - if (invalidate_preprocess) { - BKE_sequencer_preprocessed_cache_cleanup_sequence(seq); - } - /* invalidate cache for all dependent sequences */ /* NOTE: can not use SEQ_BEGIN/SEQ_END here because that macro will change sequence's depth, * which makes transformation routines work incorrect */ - sequence_do_invalidate_dependent(seq, &ed->seqbase); + sequence_do_invalidate_dependent(scene, seq, &ed->seqbase); } void BKE_sequence_invalidate_cache(Scene *scene, Sequence *seq) @@ -4441,7 +4363,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) { Sequence *seq; - BKE_sequencer_cache_cleanup(); + BKE_sequencer_cache_cleanup(scene); for (seq = seqbase->first; seq; seq = seq->next) { if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) { @@ -5449,6 +5371,7 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine) seq->scene_sound = NULL; seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + seq->cache_flag = SEQ_CACHE_ALL_TYPES; return seq; } @@ -6080,7 +6003,6 @@ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int cfra) void BKE_sequencer_all_free_anim_ibufs(Main *bmain, int cfra) { - BKE_sequencer_cache_cleanup(); for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed == NULL) { @@ -6088,5 +6010,6 @@ void BKE_sequencer_all_free_anim_ibufs(Main *bmain, int cfra) continue; } sequencer_all_free_anim_ibufs(&ed->seqbase, cfra); + BKE_sequencer_cache_cleanup(scene); } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6646a46be56..bb766d7c6db 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6735,6 +6735,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) ed = sce->ed = newdataadr(fd, sce->ed); ed->act_seq = newdataadr(fd, ed->act_seq); + ed->cache = NULL; /* recursive link sequences, lb will be correctly initialized */ link_recurs_seq(fd, &ed->seqbase); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 73109cd9a6b..1359684e221 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1077,6 +1077,14 @@ static void do_versions_seq_unique_name_all_strips(Scene *sce, ListBase *seqbase } } +static void do_versions_seq_set_cache_defaults(Editing *ed) +{ + ed->cache_flag = SEQ_CACHE_STORE_FINAL_OUT; + ed->cache_flag |= SEQ_CACHE_VIEW_FINAL_OUT; + ed->cache_flag |= SEQ_CACHE_VIEW_ENABLE; + ed->recycle_max_cost = 10.0f; +} + void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) { bool use_collection_compat_28 = true; @@ -3242,7 +3250,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - { + if (!MAIN_VERSION_ATLEAST(bmain, 280, 60)) { if (!DNA_struct_elem_find(fd->filesdna, "bSplineIKConstraint", "short", "yScaleMode")) { for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { if (ob->pose) { @@ -3300,6 +3308,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_animdata_main_cb(bmain, do_version_bbone_scale_animdata_cb, NULL); } + for (Scene *sce = bmain->scenes.first; sce != NULL; sce = sce->id.next) { + if (sce->ed != NULL) { + do_versions_seq_set_cache_defaults(sce->ed); + } + } + } + + { /* Versioning code until next subversion bump goes here. */ } } diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 8403f178284..bc3a18313d6 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -358,7 +358,7 @@ static int screen_render_exec(bContext *C, wmOperator *op) * otherwise, invalidated cache entries can make their way into * the output rendering. We can't put that into RE_BlenderFrame, * since sequence rendering can call that recursively... (peter) */ - BKE_sequencer_cache_cleanup(); + BKE_sequencer_cache_cleanup(scene); RE_SetReports(re, op->reports); @@ -978,7 +978,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even * otherwise, invalidated cache entries can make their way into * the output rendering. We can't put that into RE_BlenderFrame, * since sequence rendering can call that recursively... (peter) */ - BKE_sequencer_cache_cleanup(); + BKE_sequencer_cache_cleanup(scene); // store spare // get view3d layer, local layer, make this nice api call to render diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 550aa9d7f8d..2fce8c42a24 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1812,6 +1812,182 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) GPU_blend(false); } +typedef struct CacheDrawData { + const bContext *C; + uint pos; + float stripe_offs; + float stripe_ht; +} CacheDrawData; + +/* Called as a callback */ +static bool draw_cache_view_cb( + void *userdata, struct Sequence *seq, int cfra, int cache_type, float UNUSED(cost)) +{ + CacheDrawData *drawdata = userdata; + const bContext *C = drawdata->C; + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + struct View2D *v2d = &ar->v2d; + Editing *ed = scene->ed; + uint pos = drawdata->pos; + + if ((ed->cache_flag & SEQ_CACHE_VIEW_FINAL_OUT) == 0) { + return true; + } + + float stripe_bot, stripe_top, stripe_offs, stripe_ht; + float color[4]; + color[3] = 0.4f; + + switch (cache_type) { + case SEQ_CACHE_STORE_FINAL_OUT: + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_FINAL_OUT) { + color[0] = 1.0f; + color[1] = 0.4f; + color[2] = 0.2f; + stripe_ht = UI_view2d_region_to_view_y(v2d, 4.0f * UI_DPI_FAC * U.pixelsize) - + v2d->cur.ymin; + ; + stripe_bot = UI_view2d_region_to_view_y(v2d, V2D_SCROLL_HEIGHT_TEXT); + stripe_top = stripe_bot + stripe_ht; + break; + } + else { + return false; + } + + case SEQ_CACHE_STORE_RAW: + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_RAW) { + color[0] = 1.0f; + color[1] = 0.1f; + color[2] = 0.02f; + stripe_offs = drawdata->stripe_offs; + stripe_ht = drawdata->stripe_ht; + stripe_bot = seq->machine + SEQ_STRIP_OFSBOTTOM + stripe_offs; + stripe_top = stripe_bot + stripe_ht; + break; + } + else { + return false; + } + + case SEQ_CACHE_STORE_PREPROCESSED: + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_PREPROCESSED) { + color[0] = 0.1f; + color[1] = 0.1f; + color[2] = 0.75f; + stripe_offs = drawdata->stripe_offs; + stripe_ht = drawdata->stripe_ht; + stripe_bot = seq->machine + SEQ_STRIP_OFSBOTTOM + (stripe_offs + stripe_ht) + stripe_offs; + stripe_top = stripe_bot + stripe_ht; + break; + } + else { + return false; + } + + case SEQ_CACHE_STORE_COMPOSITE: + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_COMPOSITE) { + color[0] = 1.0f; + color[1] = 0.6f; + color[2] = 0.0f; + stripe_offs = drawdata->stripe_offs; + stripe_ht = drawdata->stripe_ht; + stripe_top = seq->machine + SEQ_STRIP_OFSTOP - stripe_offs; + stripe_bot = stripe_top - stripe_ht; + break; + } + else { + return false; + } + } + + immUniformColor4f(color[0], color[1], color[2], color[3]); + immRectf(pos, cfra, stripe_bot, cfra + 1, stripe_top); + + return false; +} + +static void draw_cache_view(const bContext *C) +{ + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + struct View2D *v2d = &ar->v2d; + + if ((scene->ed->cache_flag & SEQ_CACHE_VIEW_ENABLE) == 0) { + return; + } + + GPU_blend(true); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + float stripe_bot, stripe_top, stripe_offs; + float stripe_ht = UI_view2d_region_to_view_y(v2d, 4.0f * UI_DPI_FAC * U.pixelsize) - + v2d->cur.ymin; + + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_FINAL_OUT) { + stripe_bot = UI_view2d_region_to_view_y(v2d, V2D_SCROLL_HEIGHT_TEXT); + stripe_top = stripe_bot + stripe_ht; + float bg_color[4] = {1.0f, 0.4f, 0.2f, 0.1f}; + + immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); + immRectf(pos, scene->r.sfra, stripe_bot, scene->r.efra, stripe_top); + } + + for (Sequence *seq = scene->ed->seqbasep->first; seq != NULL; seq = seq->next) { + if (seq->type == SEQ_TYPE_SOUND_RAM) { + continue; + } + + if (seq->startdisp > v2d->cur.xmax || seq->enddisp < v2d->cur.xmin) { + continue; + } + + CLAMP_MAX(stripe_ht, 0.2f); + stripe_offs = UI_view2d_region_to_view_y(v2d, 1.0f) - v2d->cur.ymin; + CLAMP_MIN(stripe_offs, stripe_ht / 2); + + stripe_bot = seq->machine + SEQ_STRIP_OFSBOTTOM + stripe_offs; + stripe_top = stripe_bot + stripe_ht; + + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_RAW) { + float bg_color[4] = {1.0f, 0.1f, 0.02f, 0.1f}; + immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); + immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + } + + stripe_bot += stripe_ht + stripe_offs; + stripe_top = stripe_bot + stripe_ht; + + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_PREPROCESSED) { + float bg_color[4] = {0.1f, 0.1f, 0.75f, 0.1f}; + immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); + immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + } + + stripe_top = seq->machine + SEQ_STRIP_OFSTOP - stripe_offs; + stripe_bot = stripe_top - stripe_ht; + + if (scene->ed->cache_flag & SEQ_CACHE_VIEW_COMPOSITE) { + float bg_color[4] = {1.0f, 0.6f, 0.0f, 0.1f}; + immUniformColor4f(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); + immRectf(pos, seq->startdisp, stripe_bot, seq->enddisp, stripe_top); + } + } + + CacheDrawData userdata; + userdata.C = C; + userdata.pos = pos; + userdata.stripe_offs = stripe_offs; + userdata.stripe_ht = stripe_ht; + + BKE_sequencer_cache_iterate(scene, &userdata, draw_cache_view_cb); + + immUnbindProgram(); + GPU_blend(false); +} + /* Draw Timeline/Strip Editor Mode for Sequencer */ void draw_timeline_seq(const bContext *C, ARegion *ar) { @@ -1860,6 +2036,7 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) if (ed) { /* draw the data */ draw_seq_strips(C, ed, ar); + draw_cache_view(C); /* text draw cached (for sequence names), in pixelspace now */ UI_view2d_text_cache_draw(ar); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index b2c231d649e..2b0c29a02ad 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -695,8 +695,6 @@ static void sequencer_preview_region_listener(wmWindow *UNUSED(win), case NC_ANIMATION: switch (wmn->data) { case ND_KEYFRAME: - /* Otherwise, often prevents seeing immediately effects of keyframe editing... */ - BKE_sequencer_cache_cleanup(); ED_region_tag_redraw(ar); break; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 5f4311f68eb..7f049f480d7 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1111,19 +1111,16 @@ static void recalcData_sequencer(TransInfo *t) if (seq != seq_prev) { if (BKE_sequence_tx_fullupdate_test(seq)) { - /* A few effect strip types need a complete recache on transform. */ BKE_sequence_invalidate_cache(t->scene, seq); } else { - BKE_sequence_invalidate_dependent(t->scene, seq); + BKE_sequence_invalidate_cache(t->scene, seq); } } seq_prev = seq; } - BKE_sequencer_preprocessed_cache_cleanup(); - flushTransSeq(t); } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index bdc1abf3132..f8df14cf317 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -173,6 +173,13 @@ bool addzbufImBuf(struct ImBuf *ibuf); bool addzbuffloatImBuf(struct ImBuf *ibuf); /** + * Approximate size of ImBuf in memory + * + * \attention Defined in allocimbuf.c + */ +size_t IMB_get_size_in_memory(struct ImBuf *ibuf); + +/** * * \attention Defined in rectop.c */ diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 76304b33a6f..fe24ccc99a9 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -618,6 +618,35 @@ ImBuf *IMB_dupImBuf(const ImBuf *ibuf1) return (ibuf2); } +size_t IMB_get_size_in_memory(ImBuf *ibuf) +{ + int a; + size_t size = 0, channel_size = 0; + + size += sizeof(ImBuf); + + if (ibuf->rect) + channel_size += sizeof(char); + + if (ibuf->rect_float) + channel_size += sizeof(float); + + size += channel_size * ibuf->x * ibuf->y * ibuf->channels; + + if (ibuf->miptot) { + for (a = 0; a < ibuf->miptot; a++) { + if (ibuf->mipmap[a]) + size += IMB_get_size_in_memory(ibuf->mipmap[a]); + } + } + + if (ibuf->tiles) { + size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles; + } + + return size; +} + #if 0 /* remove? - campbell */ /* support for cache limiting */ diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 4424e5548ca..3cb976a6d9f 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -186,58 +186,23 @@ static void IMB_moviecache_destructor(void *p) } } -/* approximate size of ImBuf in memory */ -static size_t IMB_get_size_in_memory(ImBuf *ibuf) +static size_t get_size_in_memory(ImBuf *ibuf) { - int a; - size_t size = 0, channel_size = 0; - - /* Persistent images should have no affect on how "normal" - * images are cached. - * - * This is a bit arbitrary, but would make it so only movies - * and sequences are memory limited, keeping textures in the - * memory in order to avoid constant file reload on viewport - * update. - */ + /* Keep textures in the memory to avoid constant file reload on viewport update. */ if (ibuf->userflags & IB_PERSISTENT) { return 0; } - - size += sizeof(ImBuf); - - if (ibuf->rect) { - channel_size += sizeof(char); - } - - if (ibuf->rect_float) { - channel_size += sizeof(float); - } - - size += channel_size * ibuf->x * ibuf->y * ibuf->channels; - - if (ibuf->miptot) { - for (a = 0; a < ibuf->miptot; a++) { - if (ibuf->mipmap[a]) { - size += IMB_get_size_in_memory(ibuf->mipmap[a]); - } - } - } - - if (ibuf->tiles) { - size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles; + else { + return IMB_get_size_in_memory(ibuf); } - - return size; } - static size_t get_item_size(void *p) { size_t size = sizeof(MovieCacheItem); MovieCacheItem *item = (MovieCacheItem *)p; if (item->ibuf) { - size += IMB_get_size_in_memory(item->ibuf); + size += get_size_in_memory(item->ibuf); } return size; @@ -407,7 +372,7 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu size_t mem_in_use, mem_limit, elem_size; bool result = false; - elem_size = IMB_get_size_in_memory(ibuf); + elem_size = get_size_in_memory(ibuf); mem_limit = MEM_CacheLimiter_get_maximum(); BLI_mutex_lock(&limitor_lock); diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index ba3dee405ad..2a4d1b5d9d3 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -229,6 +229,9 @@ typedef struct Sequence { /* modifiers */ ListBase modifiers; + + int cache_flag; + int _pad2[3]; } Sequence; typedef struct MetaStack { @@ -258,6 +261,12 @@ typedef struct Editing { int over_ofs, over_cfra; int over_flag, proxy_storage; rctf over_border; + + struct SeqCache *cache; + + /* Cache control */ + float recycle_max_cost; + int cache_flag; } Editing; /* ************* Effect Variable Structs ********* */ @@ -635,4 +644,33 @@ enum { SEQUENCE_MASK_TIME_ABSOLUTE = 1, }; +/* Sequence->cache_flag + * SEQ_CACHE_STORE_RAW + * SEQ_CACHE_STORE_PREPROCESSED + * SEQ_CACHE_STORE_COMPOSITE + * FINAL_OUT is ignored + * + * Editing->cache_flag + * all entries + */ +enum { + SEQ_CACHE_STORE_RAW = (1 << 0), + SEQ_CACHE_STORE_PREPROCESSED = (1 << 1), + SEQ_CACHE_STORE_COMPOSITE = (1 << 2), + SEQ_CACHE_STORE_FINAL_OUT = (1 << 3), + + /* For lookup purposes */ + SEQ_CACHE_ALL_TYPES = SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_PREPROCESSED | + SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT, + + SEQ_CACHE_OVERRIDE = (1 << 4), + + /* enable cache visualization overlay in timeline UI */ + SEQ_CACHE_VIEW_ENABLE = (1 << 5), + SEQ_CACHE_VIEW_RAW = (1 << 6), + SEQ_CACHE_VIEW_PREPROCESSED = (1 << 7), + SEQ_CACHE_VIEW_COMPOSITE = (1 << 8), + SEQ_CACHE_VIEW_FINAL_OUT = (1 << 9), +}; + #endif /* __DNA_SEQUENCE_TYPES_H__ */ diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index ddea98a135a..1f356624f3f 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -124,11 +124,10 @@ static void rna_Camera_background_images_clear(Camera *cam) WM_main_add_notifier(NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, cam); } -static void rna_Camera_dof_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Camera_dof_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { /* TODO(sergey): Can be more selective here. */ - BKE_sequencer_cache_cleanup(); - BKE_sequencer_preprocessed_cache_cleanup(); + BKE_sequencer_cache_cleanup_all(bmain); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 934847fed50..40ee069657c 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -577,10 +577,6 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, BKE_movieclip_reload(bmain, clip); - /* all sequencers for now, we don't know which scenes are using this clip as a strip */ - BKE_sequencer_cache_cleanup(); - BKE_sequencer_preprocessed_cache_cleanup(); - WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, &clip->id); WM_main_add_notifier(NC_MOVIECLIP | NA_EDITED, &clip->id); } @@ -612,21 +608,19 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, } BKE_sequence_invalidate_cache(scene, seq); - BKE_sequencer_preprocessed_cache_cleanup_sequence(seq); } else { SEQ_BEGIN (scene->ed, seq) { BKE_sequence_free_anim(seq); } SEQ_END; - - BKE_sequencer_cache_cleanup(); - BKE_sequencer_preprocessed_cache_cleanup(); } WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); } } + + BKE_sequencer_cache_cleanup_all(bmain); } static char *rna_ColorManagedSequencerColorspaceSettings_path(PointerRNA *UNUSED(ptr)) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 53cfc0bc3c0..8802eb07149 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1988,22 +1988,21 @@ static void rna_Scene_update_active_object_data(bContext *C, PointerRNA *UNUSED( } } -static void rna_SceneCamera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_SceneCamera_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->id.data; Object *camera = scene->camera; + BKE_sequencer_cache_cleanup_all(bmain); + if (camera && (camera->type == OB_CAMERA)) { DEG_id_tag_update(&camera->id, ID_RECALC_GEOMETRY); } } -static void rna_SceneSequencer_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *UNUSED(ptr)) +static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { - BKE_sequencer_cache_cleanup(); - BKE_sequencer_preprocessed_cache_cleanup(); + BKE_sequencer_cache_cleanup(scene); } static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr)) @@ -2157,11 +2156,10 @@ static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value) } } -static void rna_GPUDOFSettings_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_GPUDOFSettings_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { /* TODO(sergey): Can be more selective here. */ - BKE_sequencer_cache_cleanup(); - BKE_sequencer_preprocessed_cache_cleanup(); + BKE_sequencer_cache_cleanup_all(bmain); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } @@ -5608,7 +5606,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_range(prop, 1, SHRT_MAX); RNA_def_property_ui_range(prop, 1, 100, 10, 1); RNA_def_property_ui_text(prop, "Resolution %", "Percentage scale for render resolution"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneSequencer_update"); prop = RNA_def_property(srna, "tile_x", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "tilex"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 6f524be7473..7daf4474eb6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1726,6 +1726,31 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Modifiers", "Modifiers affecting this strip"); rna_def_sequence_modifiers(brna, prop); + prop = RNA_def_property(srna, "cache_raw", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_RAW); + RNA_def_property_ui_text(prop, + "Cache Raw", + "Cache raw images read from disk, for faster tweaking of strip " + "parameters at the cost of memory usage"); + + prop = RNA_def_property(srna, "cache_preprocessed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_PREPROCESSED); + RNA_def_property_ui_text( + prop, + "Cache Rreprocessed", + "Cache preprocessed images, for faster tweaking of effects at the cost of memory usage"); + + prop = RNA_def_property(srna, "cache_composite", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_COMPOSITE); + RNA_def_property_ui_text(prop, + "Cache Composite", + "Cache intermediate composited images, for faster tweaking of stacked " + "strips at the cost of memory usage"); + + prop = RNA_def_property(srna, "override_cache_settings", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_OVERRIDE); + RNA_def_property_ui_text(prop, "Override Cache Settings", "Override global cache settings"); + RNA_api_sequence_strip(srna); } @@ -1809,6 +1834,65 @@ static void rna_def_editor(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "proxy_dir"); RNA_def_property_ui_text(prop, "Proxy Directory", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); + + /* cache flags */ + + prop = RNA_def_property(srna, "show_cache", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_VIEW_ENABLE); + RNA_def_property_ui_text(prop, "Show Cache", "Visualize cached images on the timeline"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_cache_final_out", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_VIEW_FINAL_OUT); + RNA_def_property_ui_text(prop, "Final Images", "Visualize cached complete frames"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_cache_raw", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_VIEW_RAW); + RNA_def_property_ui_text(prop, "Raw Images", "Visualize cached raw images"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_cache_preprocessed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_VIEW_PREPROCESSED); + RNA_def_property_ui_text(prop, "Preprocessed Images", "Visualize cached preprocessed images"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_cache_composite", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_VIEW_COMPOSITE); + RNA_def_property_ui_text(prop, "Composite Images", "Visualize cached composite images"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "cache_raw", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_RAW); + RNA_def_property_ui_text(prop, + "Cache Raw", + "Cache raw images read from disk, for faster tweaking of strip " + "parameters at the cost of memory usage"); + + prop = RNA_def_property(srna, "cache_preprocessed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_PREPROCESSED); + RNA_def_property_ui_text( + prop, + "Cache Preprocessed", + "Cache preprocessed images, for faster tweaking of effects at the cost of memory usage"); + + prop = RNA_def_property(srna, "cache_composite", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_STORE_COMPOSITE); + RNA_def_property_ui_text(prop, + "Cache Composite", + "Cache intermediate composited images, for faster tweaking of stacked " + "strips at the cost of memory usage"); + + prop = RNA_def_property(srna, "cache_final", PROP_BOOLEAN, PROP_NONE); + 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, "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); + RNA_def_property_float_sdna(prop, NULL, "recycle_max_cost"); + RNA_def_property_ui_text( + prop, "Recycle Up To Cost", "Only frames with cost lower than this value will be recycled"); } static void rna_def_filter_video(StructRNA *srna) |