VSE: Cache rewrite
authorRichard Antalik <richardantalik@gmail.com>
Sun, 28 Apr 2019 21:13:41 +0000 (14:13 -0700)
committerRichard Antalik <richardantalik@gmail.com>
Sun, 28 Apr 2019 21:50:48 +0000 (14:50 -0700)
This patch implements new cache system.
Aim is to give user more control over cache, so it can be maximally
utilized. This is done through sequencer timeline side panel
in category proxy & cache.
Cached images are also visualized in timeline, controled by
sequencer timeline view->cache menu

Functional changes:
 - NOT use IMB_moviecache API
 - refactor names of cached image types
 - each scene owns 1 sequencer cache
 - merge preprocess cache into per-sequencer cache
 - cache links images rendered per frame in order as they are created
 - add cache content visualization tool
 - add RNA properties to control the cache

More info can be found in design notes in blenkernel/intern/seqcache.c
and in https://developer.blender.org/D4443

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D4443

20 files changed:
release/scripts/startup/bl_ui/space_sequencer.py
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenkernel/BKE_sequencer.h
source/blender/blenkernel/intern/blender.c
source/blender/blenkernel/intern/seqcache.c
source/blender/blenkernel/intern/sequencer.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_280.c
source/blender/editors/render/render_internal.c
source/blender/editors/space_sequencer/sequencer_draw.c
source/blender/editors/space_sequencer/space_sequencer.c
source/blender/editors/transform/transform_generics.c
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/allocimbuf.c
source/blender/imbuf/intern/moviecache.c
source/blender/makesdna/DNA_sequence_types.h
source/blender/makesrna/intern/rna_camera.c
source/blender/makesrna/intern/rna_color.c
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_sequencer.c

index 87ebf365a1eefa35422cf9166eab0c49ecd1441c..bd3eb2bc3040d3c8dbada82fb6c2cd89c606fea3 100644 (file)
@@ -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,
index 16655d9b0609e2652fe0db22f26db3d96a9676ac..e01f6a6b7516bd018aef3f8187b02761368fd500 100644 (file)
@@ -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
index 820f28fb3636f47b3020f46a695ee2e950df5570..5485b2d3619d57bdb01fe43d7f107e6b9ee1cca3 100644 (file)
@@ -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
index d6599498a6517f8b00e60802992107dc21bfa5e0..48b271cf277add521c77f4eaa77ce6204a8f021b 100644 (file)
@@ -97,7 +97,6 @@ void BKE_blender_free(void)
 
   BLI_callback_global_finalize();
 
-  BKE_sequencer_cache_destruct();
   IMB_moviecache_destruct();
 
   free_nodesystem();
index b022819ca8c856349308c7f18bc1c4afb2f453a0..c50f3f3f141bf21e6050a91897517c6200fca598 100644 (file)
  */
 
 #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);
 }
index 68c2869b5d2d327ae981217ef0ffb5a88c19892a..2887f30532e3995f24ffadabbc944c86acdbdc3a 100644 (file)
@@ -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);
   }
 }
index 6646a46be5657c195e0961efc9c0347905888fe7..bb766d7c6dbbd43afa407c4236f723dc3b1967db 100644 (file)
@@ -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);
index 73109cd9a6b5302bf31f900a420933def5b80eec..1359684e2210ee63c65740f72a5e088d73cdf87f 100644 (file)
@@ -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. */
   }
 }
index 8403f178284292af2fe0ab7f4145956b3d09b67f..bc3a18313d6477ddcaf6bb110eaf97f3d8741c98 100644 (file)
@@ -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
index 550aa9d7f8d9ac315220dada17c680b4ae2a6fbb..2fce8c42a24adec68cb7dd8f132040186a42c0d8 100644 (file)
@@ -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);
index b2c231d649e5c3917699f151d7a7aa62b7f270a3..2b0c29a02ad7da9c9459f89b0b7bb81d8b0cd339 100644 (file)
@@ -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;
       }
index 5f4311f68eb4e89bf941bcd13952a849cafb6489..7f049f480d7b6227c0058401ff41390bc1c682d8 100644 (file)
@@ -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);
 }
 
index bdc1abf31323b1da45b741a078a535625d8e352a..f8df14cf317fb975fbc9f9c4d7e6ed9819c3cf60 100644 (file)
@@ -172,6 +172,13 @@ struct ImBuf *IMB_dupImBuf(const struct ImBuf *ibuf1);
 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
index 76304b33a6f51f898cd3c6d9125854f17f181e4c..fe24ccc99a9416232c345ea900a170ec81ab94e5 100644 (file)
@@ -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 */
 
index 4424e5548ca68ce31b336f34b87e60440adb5c5f..3cb976a6d9fbd80f3d3645c80693635aaf1c098d 100644 (file)
@@ -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);
index ba3dee405ad62edebd9d3fa418966f6210ae78f0..2a4d1b5d9d3b99ce5f251b6a4095ebe782bd8298 100644 (file)
@@ -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__ */
index ddea98a135ae92ccf765a5ad51c505dca49cf927..1f356624f3f43f21a4e1325e01c554f8056fb6aa 100644 (file)
@@ -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);
 }
 
index 934847fed5014802e68ba89546fda6b5b531cfdf..40ee069657c5ccfa8d4c83cb6075edaef805b0dc 100644 (file)
@@ -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))
index 53cfc0bc3c089c95e9479df21603a2a1d7824ccd..8802eb07149bf9260ecb16fc2bbc2802a889da8a 100644 (file)
@@ -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");
index 6f524be7473463d3eae784260afca7642ac93352..7daf4474eb64b68b87059017dbae30953970f5cc 100644 (file)
@@ -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)