[Bf-blender-cvs] [e8f4010611e] master: Geometry: Cache bounds min and max, share between data-blocks

Hans Goudey noreply at git.blender.org
Tue Nov 15 20:49:06 CET 2022


Commit: e8f4010611e76e0b88cd8a706b630ad843c09941
Author: Hans Goudey
Date:   Tue Nov 15 13:46:55 2022 -0600
Branches: master
https://developer.blender.org/rBe8f4010611e76e0b88cd8a706b630ad843c09941

Geometry: Cache bounds min and max, share between data-blocks

Bounding box calculation can be a large in some situations, especially
instancing. This patch caches the min and max of the bounding box in
runtime data of meshes, point clouds, and curves, implementing part of
T96968.

Bounds are now calculated lazily-- only after they are tagged dirty.
Also, cached bounds are also shared when copying geometry data-blocks
that have equivalent data. When bounds are calculated on an evaluated
data-block, they are also accessible on the original, and the next
evaluated ID will also share them. A geometry will stop sharing bounds
as soon as its positions (or radii) are changed.

Just caching the bounds gave a 2-3x speedup with thousands of mesh
geometry instances in the viewport. Sharing the bounds can eliminate
recalculations entirely in cases like copying meshes in geometry nodes
or the selection paint brush in curves sculpt mode, which causes a
reevaluation but doesn't change the positions.

**Implementation**
The sharing is achieved with a `shared_ptr` that points to a cache mutex
(from D16419) and the cached bounds data. When geometries are copied,
the bounds are shared by default, and only "un-shared" when the bounds
are tagged dirty.

Point clouds have a new runtime struct to store this data. Functions
for tagging the data dirty are improved for added for point clouds
and improved for curves. A missing tag has also been fixed for mesh
sculpt mode.

**Future**
There are further improvements which can be worked on next
- Apply changes to volume objects and other types where it makes sense
- Continue cleanup changes described in T96968
- Apply shared cache design to more expensive data like triangulation
  or normals

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

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

M	source/blender/blenkernel/BKE_curves.hh
M	source/blender/blenkernel/BKE_mesh_types.h
M	source/blender/blenkernel/BKE_pointcloud.h
M	source/blender/blenkernel/intern/curves.cc
M	source/blender/blenkernel/intern/curves_geometry.cc
M	source/blender/blenkernel/intern/editmesh_cache.cc
M	source/blender/blenkernel/intern/geometry_component_curves.cc
M	source/blender/blenkernel/intern/geometry_component_pointcloud.cc
M	source/blender/blenkernel/intern/geometry_set.cc
M	source/blender/blenkernel/intern/mesh.cc
M	source/blender/blenkernel/intern/mesh_runtime.cc
M	source/blender/blenkernel/intern/pointcloud.cc
M	source/blender/blenlib/BLI_bounds.hh
A	source/blender/blenlib/BLI_bounds_types.hh
A	source/blender/blenlib/BLI_shared_cache.hh
M	source/blender/blenlib/CMakeLists.txt
M	source/blender/editors/sculpt_paint/sculpt.cc
M	source/blender/makesdna/DNA_pointcloud_types.h

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

diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index a479dcb574d..ff11090ca12 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -11,6 +11,7 @@
 
 #include <mutex>
 
+#include "BLI_bounds_types.hh"
 #include "BLI_cache_mutex.hh"
 #include "BLI_float3x3.hh"
 #include "BLI_float4x4.hh"
@@ -21,6 +22,7 @@
 #include "BLI_task.hh"
 #include "BLI_vector.hh"
 #include "BLI_virtual_array.hh"
+#include "BLI_shared_cache.hh"
 
 #include "BKE_attribute.hh"
 #include "BKE_attribute_math.hh"
@@ -95,6 +97,13 @@ class CurvesGeometryRuntime {
    */
   mutable Span<float3> evaluated_positions_span;
 
+  /**
+   * A cache of bounds shared between data-blocks with unchanged positions and radii.
+   * When data changes affect the bounds, the cache is "un-shared" with other geometries.
+   * See #SharedCache comments.
+   */
+  mutable SharedCache<Bounds<float3>> bounds_cache;
+
   /**
    * Cache of lengths along each evaluated curve for each evaluated point. If a curve is
    * cyclic, it needs one more length value to correspond to the last segment, so in order to
@@ -391,6 +400,11 @@ class CurvesGeometry : public ::CurvesGeometry {
   void tag_topology_changed();
   /** Call after changing the "tilt" or "up" attributes. */
   void tag_normals_changed();
+  /**
+   * Call when making manual changes to the "radius" attribute. The attribute API will also call
+   * this in #finish() calls.
+   */
+  void tag_radii_changed();
 
   void translate(const float3 &translation);
   void transform(const float4x4 &matrix);
diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h
index 80f61086052..29b4dafcd35 100644
--- a/source/blender/blenkernel/BKE_mesh_types.h
+++ b/source/blender/blenkernel/BKE_mesh_types.h
@@ -15,6 +15,10 @@
 
 #  include "DNA_customdata_types.h"
 
+#  include "BLI_bounds_types.hh"
+#  include "BLI_math_vec_types.hh"
+#  include "BLI_shared_cache.hh"
+
 #  include "MEM_guardedalloc.h"
 
 struct BVHCache;
@@ -84,6 +88,12 @@ struct MeshRuntime {
   /** Needed to ensure some thread-safety during render data pre-processing. */
   std::mutex render_mutex;
 
+  /**
+   * A cache of bounds shared between data-blocks with unchanged positions. When changing positions
+   * affect the bounds, the cache is "un-shared" with other geometries. See #SharedCache comments.
+   */
+  SharedCache<Bounds<float3>> bounds_cache;
+
   /** Lazily initialized SoA data from the #edit_mesh field in #Mesh. */
   EditMeshData *edit_data = nullptr;
 
diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h
index d6367ac5a61..d493e051cb5 100644
--- a/source/blender/blenkernel/BKE_pointcloud.h
+++ b/source/blender/blenkernel/BKE_pointcloud.h
@@ -6,6 +6,15 @@
  * \ingroup bke
  * \brief General operations for point clouds.
  */
+
+#ifdef __cplusplus
+#  include <mutex>
+
+#  include "BLI_bounds_types.hh"
+#  include "BLI_math_vec_types.hh"
+#  include "BLI_shared_cache.hh"
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -22,6 +31,23 @@ struct Scene;
 extern const char *POINTCLOUD_ATTR_POSITION;
 extern const char *POINTCLOUD_ATTR_RADIUS;
 
+#ifdef __cplusplus
+namespace blender::bke {
+
+struct PointCloudRuntime {
+  /**
+   * A cache of bounds shared between data-blocks with unchanged positions and radii.
+   * When data changes affect the bounds, the cache is "un-shared" with other geometries.
+   * See #SharedCache comments.
+   */
+  mutable SharedCache<Bounds<float3>> bounds_cache;
+
+  MEM_CXX_CLASS_ALLOC_FUNCS("PointCloudRuntime");
+};
+
+}  // namespace blender::bke
+#endif
+
 void *BKE_pointcloud_add(struct Main *bmain, const char *name);
 void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
 struct PointCloud *BKE_pointcloud_new_nomain(int totpoint);
@@ -30,7 +56,6 @@ void BKE_pointcloud_nomain_to_pointcloud(struct PointCloud *pointcloud_src,
                                          bool take_ownership);
 
 struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
-bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
 
 bool BKE_pointcloud_attribute_required(const struct PointCloud *pointcloud, const char *name);
 
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index 61755a5be9b..8d3f90230f6 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -14,7 +14,6 @@
 #include "DNA_material_types.h"
 #include "DNA_object_types.h"
 
-#include "BLI_bounds.hh"
 #include "BLI_index_range.hh"
 #include "BLI_listbase.h"
 #include "BLI_math_base.h"
@@ -94,6 +93,7 @@ static void curves_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, con
   dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__);
 
   dst.runtime->type_counts = src.runtime->type_counts;
+  dst.runtime->bounds_cache = src.runtime->bounds_cache;
 
   curves_dst->batch_cache = nullptr;
 }
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 43bdb8e7b8c..18b6cdc2691 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -95,6 +95,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
 
   /* Though type counts are a cache, they must be copied because they are calculated eagerly. */
   dst.runtime->type_counts = src.runtime->type_counts;
+  dst.runtime->bounds_cache = src.runtime->bounds_cache;
 }
 
 CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
@@ -918,20 +919,22 @@ void CurvesGeometry::tag_positions_changed()
   this->runtime->tangent_cache_mutex.tag_dirty();
   this->runtime->normal_cache_mutex.tag_dirty();
   this->runtime->length_cache_mutex.tag_dirty();
+  this->runtime->bounds_cache.tag_dirty();
 }
 void CurvesGeometry::tag_topology_changed()
 {
-  this->runtime->position_cache_mutex.tag_dirty();
-  this->runtime->tangent_cache_mutex.tag_dirty();
-  this->runtime->normal_cache_mutex.tag_dirty();
+  this->tag_positions_changed();
   this->runtime->offsets_cache_mutex.tag_dirty();
   this->runtime->nurbs_basis_cache_mutex.tag_dirty();
-  this->runtime->length_cache_mutex.tag_dirty();
 }
 void CurvesGeometry::tag_normals_changed()
 {
   this->runtime->normal_cache_mutex.tag_dirty();
 }
+void CurvesGeometry::tag_radii_changed()
+{
+  this->runtime->bounds_cache.tag_dirty();
+}
 
 static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
 {
@@ -1006,25 +1009,28 @@ void CurvesGeometry::transform(const float4x4 &matrix)
   this->tag_positions_changed();
 }
 
-static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
-{
-  const Span<float3> positions = curves.positions();
-  const VArray<float> radii = curves.attributes().lookup_or_default<float>(
-      ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f);
-  if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
-    return bounds::min_max_with_radii(positions, radii.get_internal_span());
-  }
-  return bounds::min_max(positions);
-}
-
 bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
 {
-  const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
-  if (!bounds) {
+  if (this->points_num() == 0) {
     return false;
   }
-  min = math::min(bounds->min, min);
-  max = math::max(bounds->max, max);
+
+  this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
+    const Span<float3> positions = this->evaluated_positions();
+    if (this->attributes().contains("radius")) {
+      const VArraySpan<float> radii = this->attributes().lookup<float>("radius");
+      Array<float> evaluated_radii(this->evaluated_points_num());
+      this->interpolate_to_evaluated(radii, evaluated_radii.as_mutable_span());
+      r_bounds = *bounds::min_max_with_radii(positions, evaluated_radii.as_span());
+    }
+    else {
+      r_bounds = *bounds::min_max(positions);
+    }
+  });
+
+  const Bounds<float3> &bounds = this->runtime->bounds_cache.data();
+  min = math::min(bounds.min, min);
+  max = math::max(bounds.max, max);
   return true;
 }
 
diff --git a/source/blender/blenkernel/intern/editmesh_cache.cc b/source/blender/blenkernel/intern/editmesh_cache.cc
index 438d287fb28..6017b81c3b3 100644
--- a/source/blender/blenkernel/intern/editmesh_cache.cc
+++ b/source/blender/blenkernel/intern/editmesh_cache.cc
@@ -122,7 +122,7 @@ bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em,
   if (bm->totvert) {
     if (emd->vertexCos) {
       Span<float3> vert_coords(reinterpret_cast<const float3 *>(emd->vertexCos), bm->totvert);
-      std::optional<bounds::MinMaxResult<float3>> bounds = bounds::min_max(vert_coords);
+      std::optional<Bounds<float3>> bounds = bounds::min_max(vert_coords);
       BLI_assert(bounds.has_value());
       copy_v3_v3(min, math::min(bounds->min, float3(min)));
       copy_v3_v3(max, math::max(bounds->max, float3(max)));
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
index fff9004bc16..debcb35699e 100644
--- a/source/blender/blenkernel/intern/geometry_component_curves.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -315,6 +315,12 @@ static void tag_component_positions_changed(void *owner)
   curves.tag_positions_changed();
 }
 
+static void tag_component_radii_changed(void *owner)
+{
+  blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
+  curves.tag_radii_changed();
+}
+
 static void tag_component_normals_changed(void *owner)
 {
   blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner);
@@ -384,7 +390,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
                                                point_access,
                                                make_array_read_attribute<float>,
                                                make_array_write_attribute<float

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list