[Bf-blender-cvs] [7ca13eef7c3] master: Improve multi-user gpencil data performance with modifiers

Yann Lanthony noreply at git.blender.org
Mon Mar 7 11:40:39 CET 2022


Commit: 7ca13eef7c3349b346b178b8f4ca64838d2f4ccf
Author: Yann Lanthony
Date:   Mon Mar 7 11:37:50 2022 +0100
Branches: master
https://developer.blender.org/rB7ca13eef7c3349b346b178b8f4ca64838d2f4ccf

Improve multi-user gpencil data performance with modifiers

When a grease pencil data-block has multiple users and is subject
to modifiers, layer transforms or parenting, performance
(especially playback) is greatly affected.

This was caused by the grease pencil eval process which does per
instance full-copies of the original datablock in case those
kinds of transformations need to be applied.

This commit changes the behavior of the eval process to do shallow
copies (layers with empty frames) of the datablock instead
and duplicates only the visible strokes.

When we need to have a unique eval data
per instance, only copy the strokes of visible
frames to this copy.

Performance:
On a test file with 1350 frames 33k strokes and 480k points
in a single grease pencil object that was instanced 13 times:
 - master: 2.8 - 3.3 fps
 - patch: 42 - 52 fps

Co-authored by: @filedescriptor
This patch was contributed by The SPA Studios.

Reviewed By: #grease_pencil, pepeland

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

===================================================================

M	source/blender/blenkernel/intern/gpencil.c
M	source/blender/blenkernel/intern/gpencil_modifier.c

===================================================================

diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 16d43d40c50..92ed273cac8 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -2568,11 +2568,13 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer,
         layer_cb(gpl, gpf, NULL, thunk);
       }
 
-      LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
-        if (gps->totpoints == 0) {
-          continue;
+      if (stroke_cb) {
+        LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+          if (gps->totpoints == 0) {
+            continue;
+          }
+          stroke_cb(gpl, gpf, gps, thunk);
         }
-        stroke_cb(gpl, gpf, gps, thunk);
       }
     }
     /* Draw Active frame on top. */
@@ -2590,12 +2592,13 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer,
         gpl->opacity = prev_opacity;
         continue;
       }
-
-      LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) {
-        if (gps->totpoints == 0) {
-          continue;
+      if (stroke_cb) {
+        LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) {
+          if (gps->totpoints == 0) {
+            continue;
+          }
+          stroke_cb(gpl, act_gpf, gps, thunk);
         }
-        stroke_cb(gpl, act_gpf, gps, thunk);
       }
     }
 
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index bc3aa88d096..d432b18ff5f 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -618,48 +618,65 @@ static void gpencil_assign_object_eval(Object *object)
   }
 }
 
-/* Helper: Copy active frame from original datablock to evaluated datablock for modifiers. */
-static void gpencil_copy_activeframe_to_eval(
-    Depsgraph *depsgraph, Scene *scene, Object *ob, bGPdata *gpd_orig, bGPdata *gpd_eval)
+static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd)
 {
+  /* Create a temporary copy gpd. */
+  ID *newid = NULL;
+  BKE_libblock_copy_ex(NULL, &gpd->id, &newid, LIB_ID_COPY_LOCALIZE);
+  bGPdata *gpd_eval = (bGPdata *)newid;
+  BLI_listbase_clear(&gpd_eval->layers);
 
-  bGPDlayer *gpl_eval = gpd_eval->layers.first;
-  LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd_orig->layers) {
-
-    if (gpl_eval != NULL) {
-      bGPDframe *gpf_orig = gpl_orig->actframe;
-
-      int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_orig);
-      if ((gpf_orig == NULL) || (gpf_orig && gpf_orig->framenum != remap_cfra)) {
-        gpf_orig = BKE_gpencil_layer_frame_get(gpl_orig, remap_cfra, GP_GETFRAME_USE_PREV);
-      }
+  if (gpd->mat != NULL) {
+    gpd_eval->mat = MEM_dupallocN(gpd->mat);
+  }
 
-      if (gpf_orig != NULL) {
-        int gpf_index = BLI_findindex(&gpl_orig->frames, gpf_orig);
-        bGPDframe *gpf_eval = BLI_findlink(&gpl_eval->frames, gpf_index);
+  /* Duplicate structure: layers and frames without strokes. */
+  LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd->layers) {
+    bGPDlayer *gpl_eval = BKE_gpencil_layer_duplicate(gpl_orig, true, false);
+    BLI_addtail(&gpd_eval->layers, gpl_eval);
+    gpl_eval->runtime.gpl_orig = gpl_orig;
+    /* Update frames orig pointers (helps for faster lookup in copy_frame_to_eval_cb). */
+    BKE_gpencil_layer_original_pointers_update(gpl_orig, gpl_eval);
+  }
 
-        if (gpf_eval != NULL) {
-          /* Delete old strokes. */
-          BKE_gpencil_free_strokes(gpf_eval);
-          /* Copy again strokes. */
-          BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval);
+  return gpd_eval;
+}
 
-          gpf_eval->runtime.gpf_orig = (bGPDframe *)gpf_orig;
-          BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval);
-        }
-      }
+void copy_frame_to_eval_cb(bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, void *thunk)
+{
+  /* Early return when callback is not provided with a frame. */
+  if (gpf == NULL) {
+    return;
+  }
 
-      gpl_eval = gpl_eval->next;
-    }
+  /* Free any existing eval stroke data. This happens in case we have a single user on the data
+   * block and the strokes have not been deleted. */
+  if (!BLI_listbase_is_empty(&gpf->strokes)) {
+    BKE_gpencil_free_strokes(gpf);
   }
+
+  /* Get original frame. */
+  bGPDframe *gpf_orig = gpf->runtime.gpf_orig;
+  /* Copy strokes to eval frame and update internal orig pointers. */
+  BKE_gpencil_frame_copy_strokes(gpf_orig, gpf);
+  BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf);
 }
 
-static bGPdata *gpencil_copy_for_eval(bGPdata *gpd)
+void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob)
 {
-  const int flags = LIB_ID_COPY_LOCALIZE;
+  /* Remap layers' active frame with time modifiers applied. */
+  bGPdata *gpd_eval = ob->data;
+  LISTBASE_FOREACH (bGPDlayer *, gpl_eval, &gpd_eval->layers) {
+    bGPDframe *gpf_eval = gpl_eval->actframe;
+    int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_eval);
+    if (gpf_eval == NULL || gpf_eval->framenum != remap_cfra) {
+      gpl_eval->actframe = BKE_gpencil_layer_frame_get(gpl_eval, remap_cfra, GP_GETFRAME_USE_PREV);
+    }
+  }
 
-  bGPdata *result = (bGPdata *)BKE_id_copy_ex(NULL, &gpd->id, NULL, flags);
-  return result;
+  /* Copy only visible frames to evaluated version. */
+  BKE_gpencil_visible_stroke_advanced_iter(
+      NULL, ob, copy_frame_to_eval_cb, NULL, NULL, true, scene->r.cfra);
 }
 
 void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
@@ -688,7 +705,7 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
   if (ob->runtime.gpd_eval != NULL) {
     /* Make sure to clear the pointer in case the runtime eval data points to the same data block.
      * This can happen when the gpencil data block was not tagged for a depsgraph update after last
-     * call to this function. */
+     * call to this function (e.g. a frame change). */
     if (gpd_eval == ob->runtime.gpd_eval) {
       gpd_eval = NULL;
     }
@@ -707,18 +724,19 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
     return;
   }
 
-  /* If only one user, don't need a new copy, just update data of the frame. */
-  if (gpd_orig->id.us == 1) {
-    BLI_assert(ob->data != NULL);
-    gpencil_copy_activeframe_to_eval(depsgraph, scene, ob, ob_orig->data, gpd_eval);
-    return;
+  /* If datablock has only one user, we can update its eval data directly.
+   * Otherwise, we need to have distinct copies for each instance, since applied transformations
+   * may differ. */
+  if (gpd_orig->id.us > 1) {
+    /* Copy of the original datablock's structure (layers and empty frames). */
+    ob->runtime.gpd_eval = gpencil_copy_structure_for_eval(gpd_orig);
+    /* Overwrite ob->data with gpd_eval here. */
+    gpencil_assign_object_eval(ob);
   }
 
-  /* Copy full datablock to evaluated version. */
-  ob->runtime.gpd_eval = gpencil_copy_for_eval(gpd_orig);
-  /* Overwrite ob->data with gpd_eval here. */
-  gpencil_assign_object_eval(ob);
-  BKE_gpencil_update_orig_pointers(ob_orig, ob);
+  BLI_assert(ob->data != NULL);
+  /* Only copy strokes from visible frames to evaluated data.*/
+  gpencil_copy_visible_frames_to_eval(depsgraph, scene, ob);
 }
 
 void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)



More information about the Bf-blender-cvs mailing list