Motion paths: Use minimal possible dependency graph
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 20 Sep 2019 15:38:32 +0000 (17:38 +0200)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 25 Sep 2019 12:40:06 +0000 (14:40 +0200)
This change makes it so motion paths are using minimal possible
dependency graph which is sufficient to evaluate required motion
path targets.

Disclaimer: granularity is done on ID level, but it is possible
to go more granular if really needed.

Brings time down to 0.5 sec when updating motion path for the
Rain animation file used for benchmarks in the previous commits.

Reviewers: brecht

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

source/blender/editors/animation/anim_motion_paths.c
source/blender/editors/armature/pose_edit.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/object/object_edit.c

index aff532f4d548b797ccbbaa9bd9d270a13cda923b..30bf837f6c0ea5f4376456c0fe40504db22e49c1 100644 (file)
@@ -36,6 +36,7 @@
 #include "BKE_scene.h"
 
 #include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
 #include "DEG_depsgraph_query.h"
 
 #include "GPU_batch.h"
@@ -67,6 +68,37 @@ typedef struct MPathTarget {
 
 /* ........ */
 
+/* update scene for current frame */
+static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph)
+{
+  BKE_scene_graph_update_for_newframe(depsgraph, bmain);
+}
+
+Depsgraph *animviz_depsgraph_build(Main *bmain,
+                                   Scene *scene,
+                                   ViewLayer *view_layer,
+                                   ListBase *targets)
+{
+  /* Allocate dependency graph. */
+  Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
+
+  /* Make a flat array of IDs for the DEG API. */
+  const int num_ids = BLI_listbase_count(targets);
+  ID **ids = MEM_malloc_arrayN(sizeof(ID *), num_ids, "animviz IDS");
+  int current_id_index = 0;
+  for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) {
+    ids[current_id_index++] = &mpt->ob->id;
+  }
+
+  /* Build graph from all requested IDs. */
+  DEG_graph_build_from_ids(depsgraph, bmain, scene, view_layer, ids, num_ids);
+  MEM_freeN(ids);
+
+  /* Update once so we can access pointers of evaluated animation data. */
+  motionpaths_calc_update_scene(bmain, depsgraph);
+  return depsgraph;
+}
+
 /* get list of motion paths to be baked for the given object
  * - assumes the given list is ready to be used
  */
@@ -106,24 +138,6 @@ void animviz_get_object_motionpaths(Object *ob, ListBase *targets)
 
 /* ........ */
 
-/* update scene for current frame */
-static void motionpaths_calc_update_scene(Main *bmain, struct Depsgraph *depsgraph)
-{
-  /* Do all updates
-   *  - if this is too slow, resort to using a more efficient way
-   *    that doesn't force complete update, but for now, this is the
-   *    most accurate way!
-   *
-   * TODO(segey): Bring back partial updates, which became impossible
-   * with the new depsgraph due to unsorted nature of bases.
-   *
-   * TODO(sergey): Use evaluation context dedicated to motion paths.
-   */
-  BKE_scene_graph_update_for_newframe(depsgraph, bmain);
-}
-
-/* ........ */
-
 /* perform baking for the targets on the current frame */
 static void motionpaths_calc_bake_targets(ListBase *targets, int cframe)
 {
index e2c9828c20f918e39235fb02262fabfac0561c7b..ad115896a43ac5f9029f2c9d97f06ff56c83165d 100644 (file)
@@ -208,12 +208,12 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathC
   }
 
   Main *bmain = CTX_data_main(C);
-  /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some
-   * nested pointers, like animation data. */
-  Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-  ListBase targets = {NULL, NULL};
+  ViewLayer *view_layer = CTX_data_view_layer(C);
+
+  Depsgraph *depsgraph;
   bool free_depsgraph = false;
 
+  ListBase targets = {NULL, NULL};
   /* set flag to force recalc, then grab the relevant bones to target */
   ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS;
   animviz_get_object_motionpaths(ob, &targets);
@@ -223,6 +223,19 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathC
   TIMEIT_START(pose_path_calc);
 #endif
 
+  /* For a single frame update it's faster to re-use existing dependency graph and avoid overhead
+   * of building all the relations and so on for a temporary one.  */
+  if (range == POSE_PATH_CALC_RANGE_CURRENT_FRAME) {
+    /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some
+     * nested pointers, like animation data. */
+    depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+    free_depsgraph = false;
+  }
+  else {
+    depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, &targets);
+    free_depsgraph = true;
+  }
+
   animviz_calc_motionpaths(
       depsgraph, bmain, scene, &targets, pose_path_convert_range(range), !free_depsgraph);
 
@@ -238,7 +251,7 @@ void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathC
     DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
   }
 
-  /* Free temporary depsgraph instance */
+  /* Free temporary depsgraph. */
   if (free_depsgraph) {
     DEG_graph_free(depsgraph);
   }
index c262f390dc6c3d7ebe1b7e2f791ffccb87d4e122..bf9b69f12e167efd25397df40b6c0014e99b4604 100644 (file)
@@ -845,6 +845,11 @@ typedef enum eAnimvizCalcRange {
   ANIMVIZ_CALC_RANGE_FULL,
 } eAnimvizCalcRange;
 
+struct Depsgraph *animviz_depsgraph_build(struct Main *bmain,
+                                          struct Scene *scene,
+                                          struct ViewLayer *view_layer,
+                                          struct ListBase *targets);
+
 void animviz_calc_motionpaths(struct Depsgraph *depsgraph,
                               struct Main *bmain,
                               struct Scene *scene,
index 77897c909d97d49ac2859627b7dfbc54cbd15b9e..4759a3cb0db6b49c3bfc646eb670fa46d14dec6f 100644 (file)
@@ -936,11 +936,9 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang
   }
 
   Main *bmain = CTX_data_main(C);
-  /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some
-   * nested pointers, like animation data. */
-  Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-  ListBase targets = {NULL, NULL};
+  ViewLayer *view_layer = CTX_data_view_layer(C);
 
+  ListBase targets = {NULL, NULL};
   /* loop over objects in scene */
   CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
     /* set flag to force recalc, then grab path(s) from object */
@@ -949,6 +947,21 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang
   }
   CTX_DATA_END;
 
+  Depsgraph *depsgraph;
+  bool free_depsgraph = false;
+  /* For a single frame update it's faster to re-use existing dependency graph and avoid overhead
+   * of building all the relations and so on for a temporary one.  */
+  if (range == OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) {
+    /* NOTE: Dependency graph will be evaluated at all the frames, but we first need to access some
+     * nested pointers, like animation data. */
+    depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+    free_depsgraph = false;
+  }
+  else {
+    depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, &targets);
+    free_depsgraph = true;
+  }
+
   /* recalculate paths, then free */
   animviz_calc_motionpaths(
       depsgraph, bmain, scene, &targets, object_path_convert_range(range), true);
@@ -964,6 +977,11 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang
     }
     CTX_DATA_END;
   }
+
+  /* Free temporary depsgraph. */
+  if (free_depsgraph) {
+    DEG_graph_free(depsgraph);
+  }
 }
 
 /* show popup to determine settings */