Alembic: integrate cache file into the dependency graph
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Thu, 4 Apr 2019 13:07:37 +0000 (15:07 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 1 May 2019 14:02:27 +0000 (16:02 +0200)
* The cache file datablock is now evaluated as part of the dependency graph,
  creating/freeing the Alembic file handle matching the current frame.
  Modifiers and constraints depend on this evaluation.
* Cache file handles and readers now only exist on COW datablocks, never the
  original ones.
* Object data paths are flushed back to the original for the user interface.
* The cache file keeps a list of all readers associated with its handle, and
  automatically frees them when the handle is freed. This kind of sharing of
  data across datablocks is weak but we have no better mechanism for it.

Fix T62720: Alembic sequences not working and crashing

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

18 files changed:
source/blender/alembic/intern/abc_object.cc
source/blender/alembic/intern/alembic_capi.cc
source/blender/blenkernel/BKE_cachefile.h
source/blender/blenkernel/intern/cachefile.c
source/blender/blenkernel/intern/constraint.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/editors/interface/interface_templates.c
source/blender/editors/io/io_cache.c
source/blender/makesdna/DNA_cachefile_types.h
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesrna/intern/rna_cachefile.c
source/blender/makesrna/intern/rna_constraint.c
source/blender/makesrna/intern/rna_modifier.c
source/blender/modifiers/intern/MOD_meshsequencecache.c

index 36daa50e095379f14fa8ea2d828eca9762c3f091..e437273c1bebbb5547dbccb767f3ae681ce8a0c3 100644 (file)
@@ -364,9 +364,6 @@ void AbcObjectReader::addCacheModifier()
   id_us_plus(&mcmd->cache_file->id);
 
   BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
-
-  mcmd->reader = reinterpret_cast<CacheReader *>(this);
-  this->incref();
 }
 
 chrono_t AbcObjectReader::minTime() const
index 3ff3fbe2001306914f451bdfdbf856a3f6febc9c..b502692d0e5459cb5d724c25ec3425c43b0511a2 100644 (file)
@@ -627,6 +627,7 @@ struct ImportJobData {
   char filename[1024];
   ImportSettings settings;
 
+  ArchiveReader *archive;
   std::vector<AbcObjectReader *> readers;
 
   short *stop;
@@ -672,9 +673,9 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
 
   cache_file->is_sequence = data->settings.is_sequence;
   cache_file->scale = data->settings.scale;
-  cache_file->handle = handle_from_archive(archive);
-  BLI_strncpy(cache_file->filepath, data->filename, 1024);
+  STRNCPY(cache_file->filepath, data->filename);
 
+  data->archive = archive;
   data->settings.cache_file = cache_file;
 
   *data->do_update = true;
@@ -855,6 +856,7 @@ static void import_endjob(void *user_data)
 static void import_freejob(void *user_data)
 {
   ImportJobData *data = static_cast<ImportJobData *>(user_data);
+  delete data->archive;
   delete data;
 }
 
@@ -886,6 +888,7 @@ bool ABC_import(bContext *C,
   job->settings.validate_meshes = validate_meshes;
   job->error_code = ABC_NO_ERROR;
   job->was_cancelled = false;
+  job->archive = NULL;
 
   G.is_break = false;
 
index b991ac242843f04a33bd07e3d91eaede88b7befe..257975e3c17870767fe230e564d25352f300016b 100644 (file)
@@ -29,8 +29,10 @@ extern "C" {
 #endif
 
 struct CacheFile;
+struct CacheReader;
 struct Depsgraph;
 struct Main;
+struct Object;
 struct Scene;
 
 void BKE_cachefiles_init(void);
@@ -52,24 +54,27 @@ void BKE_cachefile_make_local(struct Main *bmain,
                               struct CacheFile *cache_file,
                               const bool lib_local);
 
-void BKE_cachefile_reload(const struct Main *bmain, struct CacheFile *cache_file);
+void BKE_cachefile_reload(struct Depsgraph *depsgraph, struct CacheFile *cache_file);
 
-void BKE_cachefile_ensure_handle(const struct Main *bmain, struct CacheFile *cache_file);
-
-void BKE_cachefile_update_frame(struct Main *bmain,
-                                struct Depsgraph *depsgraph,
-                                struct Scene *scene,
-                                const float ctime,
-                                const float fps);
+void BKE_cachefile_eval(struct Main *bmain,
+                        struct Depsgraph *depsgraph,
+                        struct CacheFile *cache_file);
 
 bool BKE_cachefile_filepath_get(const struct Main *bmain,
+                                const struct Depsgraph *depsgrah,
                                 const struct CacheFile *cache_file,
-                                float frame,
                                 char r_filename[1024]);
 
-float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps);
+float BKE_cachefile_time_offset(const struct CacheFile *cache_file,
+                                const float time,
+                                const float fps);
 
-void BKE_cachefile_clean(struct Main *bmain, struct CacheFile *cache_file);
+/* Modifiers and constraints open and free readers through these. */
+void BKE_cachefile_reader_open(struct CacheFile *cache_file,
+                               struct CacheReader **reader,
+                               struct Object *object,
+                               const char *object_path);
+void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader);
 
 #ifdef __cplusplus
 }
index e4fc4706e669afd26125d0df82d46b24e6c30bac..a60b840344c5a12dd865024d6135943619624a1e 100644 (file)
  * \ingroup bke
  */
 
+#include <string.h>
+
 #include "DNA_anim_types.h"
 #include "DNA_cachefile_types.h"
 #include "DNA_constraint_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
+#include "BLI_utildefines.h"
 #include "BLI_fileops.h"
+#include "BLI_ghash.h"
 #include "BLI_listbase.h"
 #include "BLI_path_util.h"
 #include "BLI_string.h"
 #include "BLI_threads.h"
-#include "BLI_utildefines.h"
 
 #include "BKE_animsys.h"
 #include "BKE_cachefile.h"
 #include "BKE_modifier.h"
 #include "BKE_scene.h"
 
+#include "DEG_depsgraph_query.h"
+
 #ifdef WITH_ALEMBIC
 #  include "ABC_alembic.h"
 #endif
 
+/* TODO: make this per cache file to avoid global locks. */
 static SpinLock spin;
 
 void BKE_cachefiles_init(void)
@@ -57,6 +63,94 @@ void BKE_cachefiles_exit(void)
   BLI_spin_end(&spin);
 }
 
+void BKE_cachefile_reader_open(CacheFile *cache_file,
+                               struct CacheReader **reader,
+                               Object *object,
+                               const char *object_path)
+{
+#ifdef WITH_ALEMBIC
+  BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE);
+
+  if (cache_file->handle == NULL) {
+    return;
+  }
+
+  /* Open Alembic cache reader. */
+  *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path);
+
+  /* Multiple modifiers and constraints can call this function concurrently. */
+  BLI_spin_lock(&spin);
+  if (*reader) {
+    /* Register in set so we can free it when the cache file changes. */
+    if (cache_file->handle_readers == NULL) {
+      cache_file->handle_readers = BLI_gset_ptr_new("CacheFile.handle_readers");
+    }
+    BLI_gset_reinsert(cache_file->handle_readers, reader, NULL);
+  }
+  else if (cache_file->handle_readers) {
+    /* Remove in case CacheReader_open_alembic_object free the existing reader. */
+    BLI_gset_remove(cache_file->handle_readers, reader, NULL);
+  }
+  BLI_spin_unlock(&spin);
+#else
+  UNUSED_VARS(reader, object, object_path);
+#endif
+}
+
+void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reader)
+{
+#ifdef WITH_ALEMBIC
+  BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE);
+
+  if (*reader != NULL) {
+    CacheReader_free(*reader);
+    *reader = NULL;
+
+    /* Multiple modifiers and constraints can call this function concurrently. */
+    BLI_spin_lock(&spin);
+    if (cache_file->handle_readers) {
+      BLI_gset_remove(cache_file->handle_readers, reader, NULL);
+    }
+    BLI_spin_unlock(&spin);
+  }
+#else
+  UNUSED_VARS(cache_file, reader);
+#endif
+}
+
+static void cachefile_handle_free(CacheFile *cache_file)
+{
+#ifdef WITH_ALEMBIC
+  /* Free readers in all modifiers and constraints that use the handle, before
+   * we free the handle itself. */
+  BLI_spin_lock(&spin);
+  if (cache_file->handle_readers) {
+    GSetIterator gs_iter;
+    GSET_ITER (gs_iter, cache_file->handle_readers) {
+      struct CacheReader **reader = BLI_gsetIterator_getKey(&gs_iter);
+      if (*reader != NULL) {
+        CacheReader_free(*reader);
+        *reader = NULL;
+      }
+    }
+
+    BLI_gset_free(cache_file->handle_readers, NULL);
+    cache_file->handle_readers = NULL;
+  }
+  BLI_spin_unlock(&spin);
+
+  /* Free handle. */
+  if (cache_file->handle) {
+    ABC_free_handle(cache_file->handle);
+    cache_file->handle = NULL;
+  }
+
+  cache_file->handle_filepath[0] = '\0';
+#else
+  UNUSED_VARS(cache_file);
+#endif
+}
+
 void *BKE_cachefile_add(Main *bmain, const char *name)
 {
   CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, name, 0);
@@ -68,37 +162,23 @@ void *BKE_cachefile_add(Main *bmain, const char *name)
 
 void BKE_cachefile_init(CacheFile *cache_file)
 {
-  cache_file->handle = NULL;
   cache_file->filepath[0] = '\0';
   cache_file->override_frame = false;
   cache_file->frame = 0.0f;
   cache_file->is_sequence = false;
   cache_file->scale = 1.0f;
-  cache_file->handle_mutex = BLI_mutex_alloc();
   BLI_listbase_clear(&cache_file->object_paths);
+
+  cache_file->handle = NULL;
+  cache_file->handle_filepath[0] = '\0';
+  cache_file->handle_readers = NULL;
 }
 
 /** Free (or release) any data used by this cachefile (does not free the cachefile itself). */
 void BKE_cachefile_free(CacheFile *cache_file)
 {
   BKE_animdata_free((ID *)cache_file, false);
-
-  if (cache_file->id.tag & LIB_TAG_NO_MAIN) {
-    /* CoW/no-main copies reuse the existing ArchiveReader and mutex */
-    return;
-  }
-
-  if (cache_file->handle) {
-#ifdef WITH_ALEMBIC
-    ABC_free_handle(cache_file->handle);
-#endif
-    cache_file->handle = NULL;
-  }
-  if (cache_file->handle_mutex) {
-    BLI_mutex_free(cache_file->handle_mutex);
-    cache_file->handle_mutex = NULL;
-  }
-
+  cachefile_handle_free(cache_file);
   BLI_freelistN(&cache_file->object_paths);
 }
 
@@ -117,13 +197,8 @@ void BKE_cachefile_copy_data(Main *UNUSED(bmain),
                              const CacheFile *UNUSED(cache_file_src),
                              const int UNUSED(flag))
 {
-  if (cache_file_dst->id.tag & LIB_TAG_NO_MAIN) {
-    /* CoW/no-main copies reuse the existing ArchiveReader and mutex */
-    return;
-  }
-
   cache_file_dst->handle = NULL;
-  cache_file_dst->handle_mutex = NULL;
+  cache_file_dst->handle_readers = NULL;
   BLI_duplicatelist(&cache_file_dst->object_paths, &cache_file_dst->object_paths);
 }
 
@@ -139,72 +214,51 @@ void BKE_cachefile_make_local(Main *bmain, CacheFile *cache_file, const bool lib
   BKE_id_make_local_generic(bmain, &cache_file->id, true, lib_local);
 }
 
-void BKE_cachefile_reload(const Main *bmain, CacheFile *cache_file)
+void BKE_cachefile_reload(Depsgraph *depsgraph, CacheFile *cache_file)
 {
-  char filepath[FILE_MAX];
-
-  BLI_strncpy(filepath, cache_file->filepath, sizeof(filepath));
-  BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &cache_file->id));
-
-#ifdef WITH_ALEMBIC
-  if (cache_file->handle) {
-    ABC_free_handle(cache_file->handle);
+  /* To force reload, free the handle and tag depsgraph to load it again. */
+  CacheFile *cache_file_eval = (CacheFile *)DEG_get_evaluated_id(depsgraph, &cache_file->id);
+  if (cache_file_eval) {
+    cachefile_handle_free(cache_file_eval);
   }
 
-  cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths);
-#endif
+  DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE);
 }
 
-void BKE_cachefile_ensure_handle(const Main *bmain, CacheFile *cache_file)
+void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file)
 {
-  BLI_spin_lock(&spin);
-  if (cache_file->handle_mutex == NULL) {
-    cache_file->handle_mutex = BLI_mutex_alloc();
-  }
-  BLI_spin_unlock(&spin);
-
-  BLI_mutex_lock(cache_file->handle_mutex);
+  BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE);
 
-  if (cache_file->handle == NULL) {
-    /* Assigning to a CoW copy is a bad idea; assign to the original instead. */
-    BLI_assert((cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0);
-    BKE_cachefile_reload(bmain, cache_file);
+  /* Compute filepath. */
+  char filepath[FILE_MAX];
+  if (!BKE_cachefile_filepath_get(bmain, depsgraph, cache_file, filepath)) {
+    return;
   }
 
-  BLI_mutex_unlock(cache_file->handle_mutex);
-}
-
-void BKE_cachefile_update_frame(
-    Main *bmain, struct Depsgraph *depsgraph, Scene *scene, const float ctime, const float fps)
-{
-  CacheFile *cache_file;
-  char filename[FILE_MAX];
-
-  for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
-    /* TODO: dependency graph should be updated to do drivers on cachefile.
-     * Execute drivers only, as animation has already been done. */
-    BKE_animsys_evaluate_animdata(
-        depsgraph, scene, &cache_file->id, cache_file->adt, ctime, ADT_RECALC_DRIVERS);
-
-    if (!cache_file->is_sequence) {
-      continue;
-    }
+  /* Test if filepath change or if we can keep the existing handle. */
+  if (STREQ(cache_file->handle_filepath, filepath)) {
+    return;
+  }
 
-    const float time = BKE_cachefile_time_offset(cache_file, ctime, fps);
+  cachefile_handle_free(cache_file);
+  BLI_freelistN(&cache_file->object_paths);
 
-    if (BKE_cachefile_filepath_get(bmain, cache_file, time, filename)) {
-      BKE_cachefile_clean(bmain, cache_file);
 #ifdef WITH_ALEMBIC
-      ABC_free_handle(cache_file->handle);
-      cache_file->handle = ABC_create_handle(filename, NULL);
+  cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths);
+  BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX);
 #endif
-    }
+
+  if (DEG_is_active(depsgraph)) {
+    /* Flush object paths back to original datablock for UI. */
+    CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id);
+    BLI_freelistN(&cache_file_orig->object_paths);
+    BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths);
   }
 }
 
 bool BKE_cachefile_filepath_get(const Main *bmain,
+                                const Depsgraph *depsgraph,
                                 const CacheFile *cache_file,
-                                float frame,
                                 char r_filepath[FILE_MAX])
 {
   BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX);
@@ -214,6 +268,11 @@ bool BKE_cachefile_filepath_get(const Main *bmain,
   int frame_len;
 
   if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) {
+    Scene *scene = DEG_get_evaluated_scene(depsgraph);
+    const float ctime = BKE_scene_frame_get(scene);
+    const float fps = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base);
+    const float frame = BKE_cachefile_time_offset(cache_file, ctime, fps);
+
     char ext[32];
     BLI_path_frame_strip(r_filepath, ext);
     BLI_path_frame(r_filepath, frame, frame_len);
@@ -226,47 +285,9 @@ bool BKE_cachefile_filepath_get(const Main *bmain,
   return true;
 }
 
-float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const float fps)
+float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, const float fps)
 {
   const float time_offset = cache_file->frame_offset / fps;
   const float frame = (cache_file->override_frame ? cache_file->frame : time);
   return cache_file->is_sequence ? frame : frame / fps - time_offset;
 }
-
-/* TODO(kevin): replace this with some depsgraph mechanism, or something similar. */
-void BKE_cachefile_clean(struct Main *bmain, CacheFile *cache_file)
-{
-  for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
-    ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
-
-    if (md) {
-      MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
-
-      if (cache_file == mcmd->cache_file) {
-#ifdef WITH_ALEMBIC
-        if (mcmd->reader != NULL) {
-          CacheReader_free(mcmd->reader);
-        }
-#endif
-        mcmd->reader = NULL;
-      }
-    }
-
-    for (bConstraint *con = ob->constraints.first; con; con = con->next) {
-      if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
-        continue;
-      }
-
-      bTransformCacheConstraint *data = con->data;
-
-      if (cache_file == data->cache_file) {
-#ifdef WITH_ALEMBIC
-        if (data->reader != NULL) {
-          CacheReader_free(data->reader);
-        }
-#endif
-        data->reader = NULL;
-      }
-    }
-  }
-}
index a475a16dd57c778195c84aca69fdd798af994bdf..0e29f165992671d799a1c96a44e2529d01d04888 100644 (file)
@@ -4835,13 +4835,9 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa
   const float frame = DEG_get_ctime(cob->depsgraph);
   const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
 
-  /* Must always load ABC handle on original. */
-  CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id);
-  BKE_cachefile_ensure_handle(G.main, cache_file_orig);
-
-  if (!data->reader) {
-    data->reader = CacheReader_open_alembic_object(
-        cache_file_orig->handle, data->reader, cob->ob, data->object_path);
+  if (!data->reader || !STREQ(data->reader_object_path, data->object_path)) {
+    STRNCPY(data->reader_object_path, data->object_path);
+    BKE_cachefile_reader_open(cache_file, &data->reader, cob->ob, data->object_path);
   }
 
   ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale);
@@ -4859,12 +4855,8 @@ static void transformcache_copy(bConstraint *con, bConstraint *srccon)
 
   BLI_strncpy(dst->object_path, src->object_path, sizeof(dst->object_path));
   dst->cache_file = src->cache_file;
-
-#ifdef WITH_ALEMBIC
-  if (dst->reader) {
-    CacheReader_incref(dst->reader);
-  }
-#endif
+  dst->reader = NULL;
+  dst->reader_object_path[0] = '\0';
 }
 
 static void transformcache_free(bConstraint *con)
@@ -4872,10 +4864,8 @@ static void transformcache_free(bConstraint *con)
   bTransformCacheConstraint *data = con->data;
 
   if (data->reader) {
-#ifdef WITH_ALEMBIC
-    CacheReader_free(data->reader);
-#endif
-    data->reader = NULL;
+    BKE_cachefile_reader_free(data->cache_file, &data->reader);
+    data->reader_object_path[0] = '\0';
   }
 }
 
index e0bf62402d27f4a3b0a69a7874ef908ef2992678..58b36aed24f798f95166a91a1a185ffdc7adcba3 100644 (file)
@@ -1565,15 +1565,6 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain)
   BKE_image_editors_update_frame(bmain, scene->r.cfra);
   BKE_sound_set_cfra(scene->r.cfra);
   DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
-  /* Update animated cache files for modifiers.
-   *
-   * TODO(sergey): Make this a depsgraph node?
-   */
-  BKE_cachefile_update_frame(bmain,
-                             depsgraph,
-                             scene,
-                             ctime,
-                             (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base));
 #ifdef POSE_ANIMATION_WORKAROUND
   scene_armature_depsgraph_workaround(bmain, depsgraph);
 #endif
index 55f02b45854d1f0e8ba787d690bc5a41fc13afe2..bf96cc9fa8eaf5be8b3b40407716f43830dc8c72 100644 (file)
@@ -3305,7 +3305,8 @@ static void direct_link_cachefile(FileData *fd, CacheFile *cache_file)
 {
   BLI_listbase_clear(&cache_file->object_paths);
   cache_file->handle = NULL;
-  cache_file->handle_mutex = NULL;
+  cache_file->handle_filepath[0] = '\0';
+  cache_file->handle_readers = NULL;
 
   /* relink animdata */
   cache_file->adt = newdataadr(fd, cache_file->adt);
@@ -3739,6 +3740,7 @@ static void direct_link_constraints(FileData *fd, ListBase *lb)
       case CONSTRAINT_TYPE_TRANSFORM_CACHE: {
         bTransformCacheConstraint *data = con->data;
         data->reader = NULL;
+        data->reader_object_path[0] = '\0';
       }
     }
   }
@@ -5745,6 +5747,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
     else if (md->type == eModifierType_MeshSequenceCache) {
       MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
       msmcd->reader = NULL;
+      msmcd->reader_object_path[0] = '\0';
     }
     else if (md->type == eModifierType_SurfaceDeform) {
       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
index 87bbcef7f01a3d1e030a25b228e3114d2a07f3d6..b6822a89093fabbe00af2bde1b2b95f562e67145 100644 (file)
@@ -65,6 +65,7 @@ extern "C" {
 #include "BKE_action.h"
 #include "BKE_armature.h"
 #include "BKE_animsys.h"
+#include "BKE_cachefile.h"
 #include "BKE_collection.h"
 #include "BKE_constraint.h"
 #include "BKE_curve.h"
@@ -1469,11 +1470,16 @@ void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file)
     return;
   }
   ID *cache_file_id = &cache_file->id;
+  add_id_node(cache_file_id);
+  CacheFile *cache_file_cow = get_cow_datablock(cache_file);
   /* Animation, */
   build_animdata(cache_file_id);
   build_parameters(cache_file_id);
   /* Cache evaluation itself. */
-  add_operation_node(cache_file_id, NodeType::CACHE, OperationCode::FILE_CACHE_UPDATE);
+  add_operation_node(cache_file_id,
+                     NodeType::CACHE,
+                     OperationCode::FILE_CACHE_UPDATE,
+                     function_bind(BKE_cachefile_eval, bmain_, _1, cache_file_cow));
 }
 
 void DepsgraphNodeBuilder::build_mask(Mask *mask)
index 147d82c599984457fa58cb5c18e5b3d387081475..d3fd16d889f2a5dae6b928f70a0be785fc2fa238 100644 (file)
@@ -2222,6 +2222,14 @@ void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file)
     ComponentKey datablock_key(&cache_file->id, NodeType::CACHE);
     add_relation(animation_key, datablock_key, "Datablock Animation");
   }
+
+  /* Cache file updates */
+  if (cache_file->is_sequence) {
+    OperationKey cache_update_key(
+        &cache_file->id, NodeType::CACHE, OperationCode::FILE_CACHE_UPDATE);
+    TimeSourceKey time_src_key;
+    add_relation(time_src_key, cache_update_key, "TimeSrc -> Cache File Eval");
+  }
 }
 
 void DepsgraphRelationBuilder::build_mask(Mask *mask)
@@ -2329,7 +2337,8 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node)
       continue;
     }
     int rel_flag = (RELATION_FLAG_NO_FLUSH | RELATION_FLAG_GODMODE);
-    if (id_type == ID_ME && comp_node->type == NodeType::GEOMETRY) {
+    if ((id_type == ID_ME && comp_node->type == NodeType::GEOMETRY) ||
+        (id_type == ID_CF && comp_node->type == NodeType::CACHE)) {
       rel_flag &= ~RELATION_FLAG_NO_FLUSH;
     }
     /* Notes on exceptions:
index f656e917c44b2d4690476051a7f52586cc6cb3cf..e96141eaa05a6cafaa37efcfd6bed88315e5c6c5 100644 (file)
@@ -6711,17 +6711,18 @@ void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const c
   uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE);
 
   row = uiLayoutRow(layout, false);
-  uiLayoutSetEnabled(row, RNA_boolean_get(&fileptr, "override_frame"));
+  uiLayoutSetActive(row, RNA_boolean_get(&fileptr, "override_frame"));
   uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE);
 
   row = uiLayoutRow(layout, false);
   uiItemR(row, &fileptr, "frame_offset", 0, "Frame Offset", ICON_NONE);
+  uiLayoutSetActive(row, !RNA_boolean_get(&fileptr, "is_sequence"));
 
   row = uiLayoutRow(layout, false);
   uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
 
   row = uiLayoutRow(layout, false);
-  uiLayoutSetEnabled(row, (sbuts->mainb == BCONTEXT_CONSTRAINT));
+  uiLayoutSetActive(row, (sbuts->mainb == BCONTEXT_CONSTRAINT));
   uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE);
 
   /* TODO: unused for now, so no need to expose. */
index 6358c2c1370e72f13acb2cce3701e5324520364b..3dd3b20bda3eda99a841e9fbb4efc0f1e19bfe4f 100644 (file)
@@ -38,6 +38,8 @@
 
 #include "RNA_access.h"
 
+#include "DEG_depsgraph.h"
+
 #include "UI_interface.h"
 
 #include "WM_api.h"
@@ -93,7 +95,7 @@ static int cachefile_open_exec(bContext *C, wmOperator *op)
 
   CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename), 0);
   BLI_strncpy(cache_file->filepath, filename, FILE_MAX);
-  BKE_cachefile_reload(bmain, cache_file);
+  DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE);
 
   /* Will be set when running invoke, not exec directly. */
   if (op->customdata != NULL) {
@@ -137,7 +139,7 @@ void CACHEFILE_OT_open(wmOperatorType *ot)
 
 /* ***************************** Reload Operator **************************** */
 
-static int cachefile_reload_exec(bContext *C, wmOperator *op)
+static int cachefile_reload_exec(bContext *C, wmOperator *UNUSED(op))
 {
   CacheFile *cache_file = CTX_data_edit_cachefile(C);
 
@@ -145,14 +147,10 @@ static int cachefile_reload_exec(bContext *C, wmOperator *op)
     return OPERATOR_CANCELLED;
   }
 
-  Main *bmain = CTX_data_main(C);
-
-  BLI_freelistN(&cache_file->object_paths);
-  BKE_cachefile_reload(bmain, cache_file);
+  Depsgraph *depsgraph = CTX_data_depsgraph(C);
+  BKE_cachefile_reload(depsgraph, cache_file);
 
   return OPERATOR_FINISHED;
-
-  UNUSED_VARS(op);
 }
 
 void CACHEFILE_OT_reload(wmOperatorType *ot)
index e5f77f70720d7322b515f340fe062a974d5d6a1c..bc57202367c9b49d598fbfbc0f30bcf6ab0cfdb9 100644 (file)
 extern "C" {
 #endif
 
+struct GSet;
+
 /* CacheFile::flag */
 enum {
   CACHEFILE_DS_EXPAND = (1 << 0),
-  CACHEFILE_DIRTY = (1 << 1),
+  CACHEFILE_UNUSED_0 = (1 << 1),
 };
 
 /* CacheFile::draw_flag */
@@ -53,9 +55,6 @@ typedef struct CacheFile {
   ID id;
   struct AnimData *adt;
 
-  struct AbcArchiveHandle *handle;
-  void *handle_mutex;
-
   /** Paths of the objects inside of the Alembic archive referenced by this CacheFile. */
   ListBase object_paths;
 
@@ -78,6 +77,11 @@ typedef struct CacheFile {
   short draw_flag;
 
   char _pad[4];
+
+  /* Runtime */
+  struct AbcArchiveHandle *handle;
+  char handle_filepath[1024];
+  struct GSet *handle_readers;
 } CacheFile;
 
 #ifdef __cplusplus
index 3ca61400e2e592c8436bd645db7c13d7fa9bca3e..f839f22ec9f6fbb6089aa4beec4430e4f7de38ee 100644 (file)
@@ -593,9 +593,12 @@ typedef struct bObjectSolverConstraint {
 /* Transform matrix cache constraint */
 typedef struct bTransformCacheConstraint {
   struct CacheFile *cache_file;
-  struct CacheReader *reader;
   /** FILE_MAX. */
   char object_path[1024];
+
+  /* Runtime. */
+  struct CacheReader *reader;
+  char reader_object_path[1024];
 } bTransformCacheConstraint;
 
 /* ------------------------------------------ */
index 1b85fb26c0e10b50ff0d2d40089e1fed7809546b..6a524e03b6e1161d9e1943fdee47597e0d6fbdbd 100644 (file)
@@ -1852,12 +1852,15 @@ typedef struct MeshSeqCacheModifierData {
   ModifierData modifier;
 
   struct CacheFile *cache_file;
-  struct CacheReader *reader;
   /** 1024 = FILE_MAX. */
   char object_path[1024];
 
   char read_flag;
   char _pad[7];
+
+  /* Runtime. */
+  struct CacheReader *reader;
+  char reader_object_path[1024];
 } MeshSeqCacheModifierData;
 
 /* MeshSeqCacheModifierData.read_flag */
index fb61be69ee6b276219af6e0b8395334639407729..189a4a7de86dfa0fb916dca6ac06f696c31db353 100644 (file)
 #    include "../../../alembic/ABC_alembic.h"
 #  endif
 
-static void rna_CacheFile_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+static void rna_CacheFile_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
 {
   CacheFile *cache_file = (CacheFile *)ptr->data;
 
-  DEG_id_tag_update(&cache_file->id, 0);
+  DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE);
   WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
-
-  UNUSED_VARS(bmain, scene);
-}
-
-static void rna_CacheFile_update_handle(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
-  CacheFile *cache_file = ptr->data;
-
-  if ((cache_file->flag & CACHEFILE_DIRTY) != 0) {
-    BKE_cachefile_clean(bmain, cache_file);
-    BLI_freelistN(&cache_file->object_paths);
-    cache_file->flag &= ~CACHEFILE_DIRTY;
-  }
-
-  BKE_cachefile_reload(bmain, cache_file);
-
-  rna_CacheFile_update(bmain, scene, ptr);
 }
 
 static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -76,20 +59,6 @@ static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, P
   rna_iterator_listbase_begin(iter, &cache_file->object_paths, NULL);
 }
 
-static void rna_CacheFile_filename_set(PointerRNA *ptr, const char *value)
-{
-  CacheFile *cache_file = ptr->data;
-
-  if (STREQ(cache_file->filepath, value)) {
-    return;
-  }
-
-  /* Different file is opened, close all readers. */
-  cache_file->flag |= CACHEFILE_DIRTY;
-
-  BLI_strncpy(cache_file->filepath, value, sizeof(cache_file->filepath));
-}
-
 #else
 
 /* cachefile.object_paths */
@@ -122,9 +91,8 @@ static void rna_def_cachefile(BlenderRNA *brna)
   RNA_def_struct_ui_icon(srna, ICON_FILE);
 
   PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
-  RNA_def_property_string_funcs(prop, NULL, NULL, "rna_CacheFile_filename_set");
   RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file");
-  RNA_def_property_update(prop, 0, "rna_CacheFile_update_handle");
+  RNA_def_property_update(prop, 0, "rna_CacheFile_update");
 
   prop = RNA_def_property(srna, "is_sequence", PROP_BOOLEAN, PROP_NONE);
   RNA_def_property_ui_text(
index 4444518140207fad9ff97a42684c79e935de9529..c1c235d497b2effa6403d55a16883e013e1ce469 100644 (file)
@@ -729,22 +729,6 @@ static void rna_Constraint_objectSolver_camera_set(PointerRNA *ptr, PointerRNA v
   }
 }
 
-static void rna_Constraint_transformCache_object_path_update(Main *bmain,
-                                                             Scene *scene,
-                                                             PointerRNA *ptr)
-{
-#  ifdef WITH_ALEMBIC
-  bConstraint *con = (bConstraint *)ptr->data;
-  bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data;
-  Object *ob = (Object *)ptr->id.data;
-
-  data->reader = CacheReader_open_alembic_object(
-      data->cache_file->handle, data->reader, ob, data->object_path);
-#  endif
-
-  rna_Constraint_update(bmain, scene, ptr);
-}
-
 #else
 
 static const EnumPropertyItem constraint_distance_items[] = {
@@ -2902,7 +2886,7 @@ static void rna_def_constraint_transform_cache(BlenderRNA *brna)
       prop,
       "Object Path",
       "Path to the object in the Alembic archive used to lookup the transform matrix");
-  RNA_def_property_update(prop, 0, "rna_Constraint_transformCache_object_path_update");
+  RNA_def_property_update(prop, 0, "rna_Constraint_update");
 }
 
 /* base struct for constraints */
index 8a63bf1a6190e8a65ae72678fa706ac4160ae2d9..4f304f97cac828a608228775553f6b0577fd885d 100644 (file)
@@ -1300,19 +1300,6 @@ static bool rna_SurfaceDeformModifier_is_bound_get(PointerRNA *ptr)
   return (((SurfaceDeformModifierData *)ptr->data)->verts != NULL);
 }
 
-static void rna_MeshSequenceCache_object_path_update(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
-#  ifdef WITH_ALEMBIC
-  MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data;
-  Object *ob = (Object *)ptr->id.data;
-
-  mcmd->reader = CacheReader_open_alembic_object(
-      mcmd->cache_file->handle, mcmd->reader, ob, mcmd->object_path);
-#  endif
-
-  rna_Modifier_update(bmain, scene, ptr);
-}
-
 static bool rna_ParticleInstanceModifier_particle_system_poll(PointerRNA *ptr,
                                                               const PointerRNA value)
 {
@@ -5107,7 +5094,7 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna)
       prop,
       "Object Path",
       "Path to the object in the Alembic archive used to lookup geometric data");
-  RNA_def_property_update(prop, 0, "rna_MeshSequenceCache_object_path_update");
+  RNA_def_property_update(prop, 0, "rna_Modifier_update");
 
   static const EnumPropertyItem read_flag_items[] = {
       {MOD_MESHSEQ_READ_VERT, "VERT", 0, "Vertex", ""},
index 7710082150b3bb73acd7bee48f9e40458bf79930..760830ffb24d97a3cd892a6f87ab115eb72b982b 100644 (file)
  * \ingroup modifiers
  */
 
+#include <string.h>
+
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+
 #include "DNA_cachefile_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
@@ -47,6 +52,9 @@ static void initData(ModifierData *md)
   mcmd->cache_file = NULL;
   mcmd->object_path[0] = '\0';
   mcmd->read_flag = MOD_MESHSEQ_READ_ALL;
+
+  mcmd->reader = NULL;
+  mcmd->reader_object_path[0] = '\0';
 }
 
 static void copyData(const ModifierData *md, ModifierData *target, const int flag)
@@ -59,6 +67,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
   modifier_copyData_generic(md, target, flag);
 
   tmcmd->reader = NULL;
+  tmcmd->reader_object_path[0] = '\0';
 }
 
 static void freeData(ModifierData *md)
@@ -66,10 +75,8 @@ static void freeData(ModifierData *md)
   MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
 
   if (mcmd->reader) {
-#ifdef WITH_ALEMBIC
-    CacheReader_free(mcmd->reader);
-#endif
-    mcmd->reader = NULL;
+    mcmd->reader_object_path[0] = '\0';
+    BKE_cachefile_reader_free(mcmd->cache_file, &mcmd->reader);
   }
 }
 
@@ -93,17 +100,14 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
   Mesh *org_mesh = mesh;
 
   Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
+  CacheFile *cache_file = mcmd->cache_file;
   const float frame = DEG_get_ctime(ctx->depsgraph);
-  const float time = BKE_cachefile_time_offset(mcmd->cache_file, frame, FPS);
+  const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
   const char *err_str = NULL;
 
-  CacheFile *cache_file = (CacheFile *)DEG_get_original_id(&mcmd->cache_file->id);
-
-  BKE_cachefile_ensure_handle(G.main, cache_file);
-
-  if (!mcmd->reader) {
-    mcmd->reader = CacheReader_open_alembic_object(
-        cache_file->handle, NULL, ctx->object, mcmd->object_path);
+  if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
+    STRNCPY(mcmd->reader_object_path, mcmd->object_path);
+    BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
     if (!mcmd->reader) {
       modifier_setError(md, "Could not create Alembic reader for file %s", cache_file->filepath);
       return mesh;