[Bf-blender-cvs] [511ac66dabf] master: Mesh: Use shared cache for derived triangulation

Hans Goudey noreply at git.blender.org
Sat Nov 19 00:33:16 CET 2022


Commit: 511ac66dabf170de2e0e445afbee261ddb944cf6
Author: Hans Goudey
Date:   Fri Nov 18 17:29:24 2022 -0600
Branches: master
https://developer.blender.org/rB511ac66dabf170de2e0e445afbee261ddb944cf6

Mesh: Use shared cache for derived triangulation

Use the shared cache system introduced in e8f4010611e76e for the
"looptris" triangulation cache. This avoids recalculation when meshes
are copied but the positions or topology don't change. The most obvious
improvement is for cases like a large meshes being adjusted slightly
with a simple geometry nodes modifier. In a basic test with a transform
node with a 1 million point grid I observed an improvement of 13%, from
9.75 to 11 FPS, which shows that we avoid spending 6ms recalculating
the triangulation of every update.

This also makes the thread safety for the triangulation data use a
more standard double-checked lock pattern, which is nice because we
can avoid holding a lock whenever the cached data is retrieved.

Split from https://developer.blender.org/D16530

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

M	source/blender/blenkernel/BKE_mesh_types.h
M	source/blender/blenkernel/intern/mesh.cc
M	source/blender/blenkernel/intern/mesh_runtime.cc

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

diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h
index 25387cc2bad..93b65d1561f 100644
--- a/source/blender/blenkernel/BKE_mesh_types.h
+++ b/source/blender/blenkernel/BKE_mesh_types.h
@@ -13,6 +13,7 @@
 
 #  include "MEM_guardedalloc.h"
 
+#  include "BLI_array.hh"
 #  include "BLI_bit_vector.hh"
 #  include "BLI_bounds_types.hh"
 #  include "BLI_math_vec_types.hh"
@@ -20,6 +21,7 @@
 #  include "BLI_span.hh"
 
 #  include "DNA_customdata_types.h"
+#  include "DNA_meshdata_types.h"
 
 struct BVHCache;
 struct EditMeshData;
@@ -78,19 +80,6 @@ struct LooseEdgeCache {
   int count = -1;
 };
 
-/**
- * \warning Typical access is done via #Mesh::looptris().
- */
-struct MLoopTri_Store {
-  /* WARNING! swapping between array (ready-to-be-used data) and array_wip
-   * (where data is actually computed)
-   * shall always be protected by same lock as one used for looptris computing. */
-  MLoopTri *array = nullptr;
-  MLoopTri *array_wip = nullptr;
-  int len = 0;
-  int len_alloc = 0;
-};
-
 struct MeshRuntime {
   /* Evaluated mesh for objects which do not have effective modifiers.
    * This mesh is used as a result of modifier stack evaluation.
@@ -120,8 +109,8 @@ struct MeshRuntime {
    */
   void *batch_cache = nullptr;
 
-  /** Cache for derived triangulation of the mesh. */
-  MLoopTri_Store looptris;
+  /** Cache for derived triangulation of the mesh, accessed with #Mesh::looptris(). */
+  SharedCache<Array<MLoopTri>> looptris_cache;
 
   /** Cache for BVH trees generated for the mesh. Defined in 'BKE_bvhutil.c' */
   BVHCache *bvh_cache = nullptr;
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 23fee166a0b..02d375bd782 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -129,6 +129,7 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
    * Caches will be "un-shared" as necessary later on. */
   mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache;
   mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache;
+  mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache;
 
   /* Only do tessface if we have no polys. */
   const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0));
diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc
index d62e4442d2a..51c98f6aa6b 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.cc
+++ b/source/blender/blenkernel/intern/mesh_runtime.cc
@@ -105,23 +105,14 @@ MeshRuntime::~MeshRuntime()
     BKE_shrinkwrap_boundary_data_free(this->shrinkwrap_data);
   }
   MEM_SAFE_FREE(this->subsurf_face_dot_tags);
-  MEM_SAFE_FREE(this->looptris.array);
 }
 
 }  // namespace blender::bke
 
-blender::Span<MLoopTri> Mesh::looptris() const
-{
-  const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(this);
-  const int num_looptris = BKE_mesh_runtime_looptri_len(this);
-  return {looptris, num_looptris};
-}
-
 const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
 {
   using namespace blender::bke;
   this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
-    SCOPED_TIMER("loose_edges");
     blender::BitVector<> &loose_edges = r_data.is_loose_bits;
     loose_edges.resize(0);
     loose_edges.resize(this->totedge, true);
@@ -149,97 +140,42 @@ void Mesh::loose_edges_tag_none() const
   });
 }
 
-/**
- * Ensure the array is large enough
- *
- * \note This function must always be thread-protected by caller.
- * It should only be used by internal code.
- */
-static void mesh_ensure_looptri_data(Mesh *mesh)
+blender::Span<MLoopTri> Mesh::looptris() const
 {
-  /* This is a ported copy of `DM_ensure_looptri_data(dm)`. */
-  const uint totpoly = mesh->totpoly;
-  const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop);
+  this->runtime->looptris_cache.ensure([&](blender::Array<MLoopTri> &r_data) {
+    const Span<MVert> verts = this->verts();
+    const Span<MPoly> polys = this->polys();
+    const Span<MLoop> loops = this->loops();
 
-  BLI_assert(mesh->runtime->looptris.array_wip == nullptr);
+    r_data.reinitialize(poly_to_tri_count(polys.size(), loops.size()));
 
-  SWAP(MLoopTri *, mesh->runtime->looptris.array, mesh->runtime->looptris.array_wip);
-
-  if ((looptris_len > mesh->runtime->looptris.len_alloc) ||
-      (looptris_len < mesh->runtime->looptris.len_alloc * 2) || (totpoly == 0)) {
-    MEM_SAFE_FREE(mesh->runtime->looptris.array_wip);
-    mesh->runtime->looptris.len_alloc = 0;
-    mesh->runtime->looptris.len = 0;
-  }
-
-  if (totpoly) {
-    if (mesh->runtime->looptris.array_wip == nullptr) {
-      mesh->runtime->looptris.array_wip = static_cast<MLoopTri *>(
-          MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime->looptris.array_wip), __func__));
-      mesh->runtime->looptris.len_alloc = looptris_len;
+    if (BKE_mesh_poly_normals_are_dirty(this)) {
+      BKE_mesh_recalc_looptri(
+          loops.data(), polys.data(), verts.data(), loops.size(), polys.size(), r_data.data());
     }
+    else {
+      BKE_mesh_recalc_looptri_with_normals(loops.data(),
+                                           polys.data(),
+                                           verts.data(),
+                                           loops.size(),
+                                           polys.size(),
+                                           r_data.data(),
+                                           BKE_mesh_poly_normals_ensure(this));
+    }
+  });
 
-    mesh->runtime->looptris.len = looptris_len;
-  }
-}
-
-static void recalc_loopris(Mesh *mesh)
-{
-  mesh_ensure_looptri_data(mesh);
-  BLI_assert(mesh->totpoly == 0 || mesh->runtime->looptris.array_wip != nullptr);
-  const Span<MVert> verts = mesh->verts();
-  const Span<MPoly> polys = mesh->polys();
-  const Span<MLoop> loops = mesh->loops();
-
-  if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
-    BKE_mesh_recalc_looptri_with_normals(loops.data(),
-                                         polys.data(),
-                                         verts.data(),
-                                         mesh->totloop,
-                                         mesh->totpoly,
-                                         mesh->runtime->looptris.array_wip,
-                                         BKE_mesh_poly_normals_ensure(mesh));
-  }
-  else {
-    BKE_mesh_recalc_looptri(loops.data(),
-                            polys.data(),
-                            verts.data(),
-                            mesh->totloop,
-                            mesh->totpoly,
-                            mesh->runtime->looptris.array_wip);
-  }
-
-  BLI_assert(mesh->runtime->looptris.array == nullptr);
-  atomic_cas_ptr((void **)&mesh->runtime->looptris.array,
-                 mesh->runtime->looptris.array,
-                 mesh->runtime->looptris.array_wip);
-  mesh->runtime->looptris.array_wip = nullptr;
+  return this->runtime->looptris_cache.data();
 }
 
 int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
 {
-  /* This is a ported copy of `dm_getNumLoopTri(dm)`. */
-  const int looptri_len = poly_to_tri_count(mesh->totpoly, mesh->totloop);
-  BLI_assert(ELEM(mesh->runtime->looptris.len, 0, looptri_len));
-  return looptri_len;
+  /* Allow returning the size without calculating the cache. */
+  return poly_to_tri_count(mesh->totpoly, mesh->totloop);
 }
 
 const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
 {
-  std::lock_guard lock{mesh->runtime->eval_mutex};
-
-  MLoopTri *looptri = mesh->runtime->looptris.array;
-
-  if (looptri != nullptr) {
-    BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime->looptris.len);
-  }
-  else {
-    /* Must isolate multithreaded tasks while holding a mutex lock. */
-    blender::threading::isolate_task([&]() { recalc_loopris(const_cast<Mesh *>(mesh)); });
-    looptri = mesh->runtime->looptris.array;
-  }
-
-  return looptri;
+  return mesh->looptris().data();
 }
 
 void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
@@ -288,18 +224,18 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
   free_subdiv_ccg(*mesh->runtime);
   mesh->runtime->bounds_cache.tag_dirty();
   mesh->runtime->loose_edges_cache.tag_dirty();
+  mesh->runtime->looptris_cache.tag_dirty();
   if (mesh->runtime->shrinkwrap_data) {
     BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data);
   }
   MEM_SAFE_FREE(mesh->runtime->subsurf_face_dot_tags);
-  MEM_SAFE_FREE(mesh->runtime->looptris.array);
 }
 
 void BKE_mesh_tag_coords_changed(Mesh *mesh)
 {
   BKE_mesh_normals_tag_dirty(mesh);
   free_bvh_cache(*mesh->runtime);
-  MEM_SAFE_FREE(mesh->runtime->looptris.array);
+  mesh->runtime->looptris_cache.tag_dirty();
   mesh->runtime->bounds_cache.tag_dirty();
 }



More information about the Bf-blender-cvs mailing list