[Bf-blender-cvs] [e2befa425a8] master: GPencil: Update-on-write using update cache

Falk David noreply at git.blender.org
Thu Feb 10 11:38:02 CET 2022


Commit: e2befa425a84c9e4ec715442e85624a5d3669a4f
Author: Falk David
Date:   Thu Feb 10 11:34:12 2022 +0100
Branches: master
https://developer.blender.org/rBe2befa425a84c9e4ec715442e85624a5d3669a4f

GPencil: Update-on-write using update cache

This implements the update cache described in T95401.

The cache is currently only used for drawing strokes and
sculpting (using the push brush).
**Note: Making use of the cache throughout grease pencil will
have to be done incrementally in other patches. **

The update cache stores what elements have changed in the
original data-block since the last time the eval object
was updated. Additionally, the update cache can store multiple
updates to the data and minimizes the number of elements
that need to be copied.

Elements can be tagged using `BKE_gpencil_tag_full_update` and
`BKE_gpencil_tag_light_update`. A full update means that the element
itself will be copied but also all of the content inside. E.g. when a
layer is tagged for a full update, the layer, all the frames inside the
layer and all the strokes inside the frames will be copied.
A light update means that only the properties of the element are copied
without any of the content. E.g. if a layer is tagged with a light
update, it will copy the layer name, opacity, transform, etc.

When the update cache is in use (e.g. elements have been tagged) then
the depsgraph will not trigger a copy-on-write, but an update-on-write.
This means that the update cache will be used to determine what elements
have changed and then only those elements will be copied over to the
eval object.

If the update cache is empty or the data block was tagged with a full
update, we always fall back to a copy-on-write.

Currently, the update cache is only used by the active depsgraph. This
is because we need to free the update cache after an update-on-write so
it's reset and we need to make sure it is not freed or read by other
depsgraphs.

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

Reviewed By: sergey, antoniov, #dependency_graph, pepeland, mendio

Maniphest Tasks: T95401

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

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

M	source/blender/blenkernel/BKE_gpencil.h
A	source/blender/blenkernel/BKE_gpencil_update_cache.h
M	source/blender/blenkernel/CMakeLists.txt
M	source/blender/blenkernel/intern/gpencil.c
A	source/blender/blenkernel/intern/gpencil_update_cache.c
M	source/blender/blenlib/BLI_dlrbTree.h
M	source/blender/blenlib/BLI_listbase.h
M	source/blender/blenlib/intern/DLRB_tree.c
M	source/blender/blenlib/intern/listbase.c
M	source/blender/blenlib/tests/BLI_listbase_test.cc
M	source/blender/depsgraph/CMakeLists.txt
M	source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
M	source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
M	source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
A	source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.cc
A	source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h
M	source/blender/editors/gpencil/gpencil_paint.c
M	source/blender/editors/gpencil/gpencil_sculpt_paint.c
M	source/blender/makesdna/DNA_gpencil_types.h
M	source/blender/makesrna/intern/rna_gpencil.c

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

diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 885d0c2fd90..fd8996993c0 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -48,6 +48,7 @@ struct bGPDlayer;
 struct bGPDlayer_Mask;
 struct bGPDstroke;
 struct bGPdata;
+struct GPencilUpdateCache;
 
 #define GPENCIL_SIMPLIFY(scene) (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)
 #define GPENCIL_SIMPLIFY_ONPLAY(playing) \
@@ -175,10 +176,28 @@ struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, b
 struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src,
                                               bool dup_frames,
                                               bool dup_strokes);
+
+/**
+ * Make a copy of a given gpencil data settings.
+ */
+void BKE_gpencil_data_copy_settings(const struct bGPdata *gpd_src, struct bGPdata *gpd_dst);
+
 /**
  * Make a copy of a given gpencil layer settings.
  */
 void BKE_gpencil_layer_copy_settings(const struct bGPDlayer *gpl_src, struct bGPDlayer *gpl_dst);
+
+/**
+ * Make a copy of a given gpencil frame settings.
+ */
+void BKE_gpencil_frame_copy_settings(const struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst);
+
+/**
+ * Make a copy of a given gpencil stroke settings.
+ */
+void BKE_gpencil_stroke_copy_settings(const struct bGPDstroke *gpf_src,
+                                      struct bGPDstroke *gpf_dst);
+
 /**
  * Make a copy of strokes between gpencil frames.
  * \param gpf_src: Source grease pencil frame
@@ -675,6 +694,9 @@ extern void (*BKE_gpencil_batch_cache_free_cb)(struct bGPdata *gpd);
  */
 void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig,
                                                 const struct bGPDframe *gpf_eval);
+
+void BKE_gpencil_layer_original_pointers_update(const struct bGPDlayer *gpl_orig,
+                                                const struct bGPDlayer *gpl_eval);
 /**
  * Update pointers of eval data to original data to keep references.
  * \param ob_orig: Original grease pencil object
@@ -682,6 +704,14 @@ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig
  */
 void BKE_gpencil_update_orig_pointers(const struct Object *ob_orig, const struct Object *ob_eval);
 
+/**
+ * Update pointers of eval data to original data to keep references.
+ * \param gpd_orig: Original grease pencil data
+ * \param gpd_eval: Evaluated grease pencil data
+ */
+void BKE_gpencil_data_update_orig_pointers(const struct bGPdata *gpd_orig,
+                                           const struct bGPdata *gpd_eval);
+
 /**
  * Get parent matrix, including layer parenting.
  * \param depsgraph: Depsgraph
@@ -711,6 +741,10 @@ int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char
 
 void BKE_gpencil_blend_read_data(struct BlendDataReader *reader, struct bGPdata *gpd);
 
+bool BKE_gpencil_can_avoid_full_copy_on_write(const struct Depsgraph *depsgraph, struct bGPdata *gpd);
+
+void BKE_gpencil_update_on_write(struct bGPdata *gpd_orig, struct bGPdata *gpd_eval);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenkernel/BKE_gpencil_update_cache.h b/source/blender/blenkernel/BKE_gpencil_update_cache.h
new file mode 100644
index 00000000000..3ac78267922
--- /dev/null
+++ b/source/blender/blenkernel/BKE_gpencil_update_cache.h
@@ -0,0 +1,152 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2022, Blender Foundation
+ * This is a new part of Blender
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "BLI_sys_types.h" /* for bool */
+
+struct DLRBT_Tree;
+struct bGPdata;
+struct bGPDlayer;
+struct bGPDframe;
+struct bGPDstroke;
+struct GPencilUpdateCache;
+
+/* GPencilUpdateCache.flag */
+typedef enum eGPUpdateCacheNodeFlag {
+  /* Node is a placeholder (e.g. when only an index is needed). */
+  GP_UPDATE_NODE_NO_COPY = 0,
+  /* Copy only element, not the content. */
+  GP_UPDATE_NODE_LIGHT_COPY = 1,
+  /* Copy the element as well as all of its content. */
+  GP_UPDATE_NODE_FULL_COPY = 2,
+} eGPUpdateCacheNodeFlag;
+
+/**
+ *  Cache for what needs to be updated after bGPdata was modified.
+ *
+ *  Every node holds information about one element that was changed:
+ *    - the index of where that element is in the linked-list
+ *    - the pointer to the original element in bGPdata
+ *  Additionally, nodes also hold other nodes that are one "level" below them.
+ *  E.g. a node that represents a change on a bGPDframe could contain a set of
+ *  nodes that represent a change on bGPDstrokes.
+ *  These nodes are stored in a red-black tree so that they are sorted by their
+ *  index to make sure they can be processed in the correct order.
+ */
+typedef struct GPencilUpdateCache {
+  /* Mapping from index to a GPencilUpdateCache struct. */
+  struct DLRBT_Tree *children;
+  /* eGPUpdateCacheNodeFlag */
+  int flag;
+  /* Index of the element in the linked-list. */
+  int index;
+  /* Pointer to one of bGPdata, bGPDLayer, bGPDFrame, bGPDStroke. */
+  void *data;
+} GPencilUpdateCache;
+
+/* Node structure in the DLRBT_Tree for GPencilUpdateCache mapping. */
+typedef struct GPencilUpdateCacheNode {
+  /* DLRB tree capabilities. */
+  struct GPencilUpdateCacheNode *next, *prev;
+  struct GPencilUpdateCacheNode *left, *right;
+  struct GPencilUpdateCacheNode *parent;
+  char tree_col;
+
+  char _pad[7];
+  /* Content of DLRB tree node. */
+  GPencilUpdateCache *cache;
+} GPencilUpdateCacheNode;
+
+/**
+ * Callback that is called in BKE_gpencil_traverse_update_cache at each level. If the callback
+ * returns true, then the children will not be iterated over and instead continue.
+ * \param cache: The cache at this level.
+ * \param user_data: Pointer to the user_data passed to BKE_gpencil_traverse_update_cache.
+ * \returns true, if iterating over the children of \a cache should be skipped, false if not.
+ */
+typedef bool (*GPencilUpdateCacheIter_Cb)(GPencilUpdateCache *cache, void *user_data);
+
+typedef struct GPencilUpdateCacheTraverseSettings {
+  /* Callbacks for the update cache traversal. Callback with index 0 is for layers, 1 for frames
+   * and 2 for strokes. */
+  GPencilUpdateCacheIter_Cb update_cache_cb[3];
+} GPencilUpdateCacheTraverseSettings;
+
+/**
+ * Allocates a new GPencilUpdateCache and populates it.
+ * \param data: A data pointer to populate the initial cache with.
+ * \param full_copy: If true, will mark this update cache as a full copy
+ * (GP_UPDATE_NODE_FULL_COPY). If false, it will be marked as a struct copy
+ * (GP_UPDATE_NODE_LIGHT_COPY).
+ */
+GPencilUpdateCache *BKE_gpencil_create_update_cache(void *data, bool full_copy);
+
+/**
+ * Traverses an update cache and executes callbacks at each level.
+ * \param cache: The update cache to traverse.
+ * \param ts: The traversal settings. This stores the callbacks that are called at each level.
+ * \param user_data: Custom data passed to each callback.
+ */
+void BKE_gpencil_traverse_update_cache(GPencilUpdateCache *cache,
+                                       GPencilUpdateCacheTraverseSettings *ts,
+                                       void *user_data);
+
+/**
+ * Tags an element (bGPdata, bGPDlayer, bGPDframe, or bGPDstroke) and all of its containing data to
+ * be updated in the next update-on-write operation.
+ *
+ * The function assumes that when a parameter is NULL all of the following parameters are NULL too.
+ * E.g. in order to tag a layer (gpl), the parameters would *have* to be (gpd, gpl, NULL, NULL).
+ */
+void BKE_gpencil_tag_full_update(struct bGPdata *gpd,
+                                 struct bGPDlayer *gpl,
+                                 struct bGPDframe *gpf,
+                                 struct bGPDstroke *gps);
+
+/**
+ * Tags an element (bGPdata, bGPDlayer, bGPDframe, or bGPDstroke) to be updated in the next
+ * update-on-write operation. This function will not update any of the containing data, only the
+ * struct itself.
+ *
+ * The function assumes that when a parameter is NULL all of the following parameters are NULL too.
+ * E.g. in order to tag a layer (gpl), the parameters would *have* to be (gpd, gpl, NULL, NULL).
+ */
+void BKE_gpencil_tag_light_update(struct bGPdata *gpd,
+                                  struct bGPDlayer *gpl,
+                                  struct bGPDframe *gpf,
+                                  struct bGPDstroke *gps);
+
+/**
+ * Frees the GPencilUpdateCache on the gpd->runtime. This will not free the data that the cache
+ * node might point to. It assumes that the cache does not own the data.
+ */
+void BKE_gpencil_free_update_cache(struct bGPdata *gpd);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index efc9cd6e99f..1483466061f 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -157,6 +157,7 @@ set(SRC
   intern/gpencil_curve.c
   intern/gpencil_geom.cc
   intern/gpencil_modifier.c
+  intern/gpencil_update_cache.c
   intern/icons.cc
   intern/icons_rasterize.c
   intern/idprop.c
@@ -385,6 +386,7 @@ set(SRC
   BKE_gpencil_curve.h
   BKE_gpencil_geom.h
   BKE_gpencil_modifier.h
+  BKE_gpencil_update_cache.h
   BKE_icons.h
   BKE_idprop.h
   BKE_idprop.hh
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
ind

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list