Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYann Lanthony <yann-lty>2022-03-07 13:37:50 +0300
committerFalk David <falkdavid@gmx.de>2022-03-07 13:38:56 +0300
commit7ca13eef7c3349b346b178b8f4ca64838d2f4ccf (patch)
tree1f2bd382ae4622e7fd5cba2a75bd535dbc7e7447 /source/blender/blenkernel/intern/gpencil_modifier.c
parent57c5f2a50329d6591eea41ac65ec94890c3c33df (diff)
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
Diffstat (limited to 'source/blender/blenkernel/intern/gpencil_modifier.c')
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c102
1 files changed, 60 insertions, 42 deletions
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)