[Bf-blender-cvs] [7f958217ada] master: Curves: Use shared caches for evaluated data

Hans Goudey noreply at git.blender.org
Fri Feb 3 18:19:51 CET 2023


Commit: 7f958217ada294a3aa19116aebb7bef832b9c648
Author: Hans Goudey
Date:   Fri Feb 3 12:14:15 2023 -0500
Branches: master
https://developer.blender.org/rB7f958217ada294a3aa19116aebb7bef832b9c648

Curves: Use shared caches for evaluated data

Use the `SharedCache` concept introduced in D16204 to share lazily
calculated evaluated data between original and multiple evaluated
curves data-blocks. Combined with D14139, this should basically remove
most costs associated with copying large curves data-blocks (though
they add slightly higher constant overhead). The caches should
interact well with undo steps, limiting recalculations on undo/redo.

Options for avoiding the new overhead associated with the shared
caches are described in T104327 and can be addressed separately.

Simple situations affected by this change are using any of the following data
on an evaluated curves data-block without first invalidating it:
- Evaluated offsets (size of evaluated curves)
- Evaluated positions
- Evaluated tangents
- Evaluated normals
- Evaluated lengths (spline parameter node)
- Internal Bezier and NURBS caches

In a test with 4m points and 170k curves, using curve normals in a
procedural setup that didn't change positions procedurally gave 5x
faster playback speeds. Avoiding recalculating the offsets on every
update saved about 3 ms for every sculpt update for brushes that don't
change topology.

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

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

M	source/blender/blenkernel/BKE_curves.hh
M	source/blender/blenkernel/intern/curves.cc
M	source/blender/blenkernel/intern/curves_geometry.cc

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

diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 8e4a83a1135..04898f6d2d6 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -69,21 +69,24 @@ class CurvesGeometryRuntime {
    * Cache of offsets into the evaluated array for each curve, accounting for all previous
    * evaluated points, Bezier curve vector segments, different resolutions per curve, etc.
    */
-  mutable Vector<int> evaluated_offsets_cache;
-  mutable Vector<int> all_bezier_evaluated_offsets;
-  mutable CacheMutex offsets_cache_mutex;
+  struct EvaluatedOffsets {
+    Vector<int> evaluated_offsets;
+    Vector<int> all_bezier_offsets;
+  };
+  mutable SharedCache<EvaluatedOffsets> evaluated_offsets_cache;
 
-  mutable Vector<curves::nurbs::BasisCache> nurbs_basis_cache;
-  mutable CacheMutex nurbs_basis_cache_mutex;
+  mutable SharedCache<Vector<curves::nurbs::BasisCache>> nurbs_basis_cache;
 
   /** Cache of evaluated positions. */
-  mutable Vector<float3> evaluated_position_cache;
-  mutable CacheMutex position_cache_mutex;
-  /**
-   * The evaluated positions result, using a separate span in case all curves are poly curves,
-   * in which case a separate array of evaluated positions is unnecessary.
-   */
-  mutable Span<float3> evaluated_positions_span;
+  struct EvaluatedPositions {
+    Vector<float3> vector;
+    /**
+     * The evaluated positions result, using a separate span in case all curves are poly curves,
+     * in which case a separate array of evaluated positions is unnecessary.
+     */
+    Span<float3> span;
+  };
+  mutable SharedCache<EvaluatedPositions> evaluated_position_cache;
 
   /**
    * A cache of bounds shared between data-blocks with unchanged positions and radii.
@@ -97,16 +100,13 @@ class CurvesGeometryRuntime {
    * cyclic, it needs one more length value to correspond to the last segment, so in order to
    * make slicing this array for a curve fast, an extra float is stored for every curve.
    */
-  mutable Vector<float> evaluated_length_cache;
-  mutable CacheMutex length_cache_mutex;
+  mutable SharedCache<Vector<float>> evaluated_length_cache;
 
   /** Direction of the curve at each evaluated point. */
-  mutable Vector<float3> evaluated_tangent_cache;
-  mutable CacheMutex tangent_cache_mutex;
+  mutable SharedCache<Vector<float3>> evaluated_tangent_cache;
 
   /** Normal direction vectors for each evaluated point. */
-  mutable Vector<float3> evaluated_normal_cache;
-  mutable CacheMutex normal_cache_mutex;
+  mutable SharedCache<Vector<float3>> evaluated_normal_cache;
 };
 
 /**
@@ -866,7 +866,8 @@ inline Span<int> CurvesGeometry::bezier_evaluated_offsets_for_curve(const int cu
   const OffsetIndices points_by_curve = this->points_by_curve();
   const IndexRange points = points_by_curve[curve_index];
   const IndexRange range = curves::per_curve_point_offsets_range(points, curve_index);
-  return this->runtime->all_bezier_evaluated_offsets.as_span().slice(range);
+  const Span<int> offsets = this->runtime->evaluated_offsets_cache.data().all_bezier_offsets;
+  return offsets.slice(range);
 }
 
 inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
@@ -881,9 +882,8 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
 inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index,
                                                                const bool cyclic) const
 {
-  BLI_assert(this->runtime->length_cache_mutex.is_cached());
   const IndexRange range = this->lengths_range_for_curve(curve_index, cyclic);
-  return this->runtime->evaluated_length_cache.as_span().slice(range);
+  return this->runtime->evaluated_length_cache.data().as_span().slice(range);
 }
 
 inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_index,
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index a7ac35ce56b..1519c0addd2 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -93,7 +93,13 @@ 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->evaluated_offsets_cache = src.runtime->evaluated_offsets_cache;
+  dst.runtime->nurbs_basis_cache = src.runtime->nurbs_basis_cache;
+  dst.runtime->evaluated_position_cache = src.runtime->evaluated_position_cache;
   dst.runtime->bounds_cache = src.runtime->bounds_cache;
+  dst.runtime->evaluated_length_cache = src.runtime->evaluated_length_cache;
+  dst.runtime->evaluated_tangent_cache = src.runtime->evaluated_tangent_cache;
+  dst.runtime->evaluated_normal_cache = src.runtime->evaluated_normal_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 26a4f41db48..380b3c54b80 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -93,7 +93,13 @@ 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->evaluated_offsets_cache = src.runtime->evaluated_offsets_cache;
+  dst.runtime->nurbs_basis_cache = src.runtime->nurbs_basis_cache;
+  dst.runtime->evaluated_position_cache = src.runtime->evaluated_position_cache;
   dst.runtime->bounds_cache = src.runtime->bounds_cache;
+  dst.runtime->evaluated_length_cache = src.runtime->evaluated_length_cache;
+  dst.runtime->evaluated_tangent_cache = src.runtime->evaluated_tangent_cache;
+  dst.runtime->evaluated_normal_cache = src.runtime->evaluated_normal_cache;
 }
 
 CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
@@ -499,30 +505,31 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves,
 
 OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
 {
+  const bke::CurvesGeometryRuntime &runtime = *this->runtime;
   if (this->is_single_type(CURVE_TYPE_POLY)) {
     /* When all the curves are poly curves, the evaluated offsets are the same as the control
      * point offsets, so it's possible to completely avoid building a new offsets array. */
-    this->runtime->offsets_cache_mutex.ensure(
-        [&]() { this->runtime->evaluated_offsets_cache.clear_and_shrink(); });
+    runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
+      r_data.evaluated_offsets.clear_and_shrink();
+      r_data.all_bezier_offsets.clear_and_shrink();
+    });
     return this->points_by_curve();
   }
 
-  this->runtime->offsets_cache_mutex.ensure([&]() {
-    this->runtime->evaluated_offsets_cache.resize(this->curves_num() + 1);
+  runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
+    r_data.evaluated_offsets.resize(this->curves_num() + 1);
 
     if (this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
-      this->runtime->all_bezier_evaluated_offsets.resize(this->points_num() + this->curves_num());
+      r_data.all_bezier_offsets.resize(this->points_num() + this->curves_num());
     }
     else {
-      this->runtime->all_bezier_evaluated_offsets.clear_and_shrink();
+      r_data.all_bezier_offsets.clear_and_shrink();
     }
 
-    calculate_evaluated_offsets(*this,
-                                this->runtime->evaluated_offsets_cache,
-                                this->runtime->all_bezier_evaluated_offsets);
+    calculate_evaluated_offsets(*this, r_data.evaluated_offsets, r_data.all_bezier_offsets);
   });
 
-  return OffsetIndices<int>(this->runtime->evaluated_offsets_cache);
+  return OffsetIndices<int>(runtime.evaluated_offsets_cache.data().evaluated_offsets);
 }
 
 IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
@@ -553,15 +560,16 @@ Array<int> CurvesGeometry::point_to_curve_map() const
 
 void CurvesGeometry::ensure_nurbs_basis_cache() const
 {
-  this->runtime->nurbs_basis_cache_mutex.ensure([&]() {
+  const bke::CurvesGeometryRuntime &runtime = *this->runtime;
+  runtime.nurbs_basis_cache.ensure([&](Vector<curves::nurbs::BasisCache> &r_data) {
     Vector<int64_t> nurbs_indices;
     const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, nurbs_indices);
     if (nurbs_mask.is_empty()) {
+      r_data.clear_and_shrink();
       return;
     }
 
-    this->runtime->nurbs_basis_cache.resize(this->curves_num());
-    MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache);
+    r_data.resize(this->curves_num());
 
     const OffsetIndices<int> points_by_curve = this->points_by_curve();
     const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
@@ -580,18 +588,14 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
         const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
 
         if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) {
-          basis_caches[curve_index].invalid = true;
+          r_data[curve_index].invalid = true;
           continue;
         }
 
         knots.reinitialize(curves::nurbs::knots_num(points.size(), order, is_cyclic));
         curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
-        curves::nurbs::calculate_basis_cache(points.size(),
-                                             evaluated_points.size(),
-                                             order,
-                                             is_cyclic,
-                                             knots,
-                                             basis_caches[curve_index]);
+        curves::nurbs::calculate_basis_cache(
+            points.size(), evaluated_points.size(), order, is_cyclic, knots, r_data[curve_index]);
       }
     });
   });
@@ -599,16 +603,18 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
 
 Span<float3> Curves

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list