[Bf-blender-cvs] [1ea169d90e3] master: Mesh: Move loose edge flag to a separate cache

Hans Goudey noreply at git.blender.org
Fri Nov 18 23:05:27 CET 2022


Commit: 1ea169d90e39647eac721cc1fee9927b0432bf97
Author: Hans Goudey
Date:   Fri Nov 18 16:05:06 2022 -0600
Branches: master
https://developer.blender.org/rB1ea169d90e39647eac721cc1fee9927b0432bf97

Mesh: Move loose edge flag to a separate cache

As part of T95966, this patch moves loose edge information out of the
flag on each edge and into a new lazily calculated cache in mesh
runtime data. The number of loose edges is also cached, so further
processing can be skipped completely when there are no loose edges.

Previously the `ME_LOOSEEDGE` flag was updated on a "best effort"
basis. In order to be sure that it was correct, you had to be sure
to call `BKE_mesh_calc_edges_loose` first. Now the loose edge tag
is always correct. It also doesn't have to be calculated eagerly
in various places like the screw modifier where the complexity
wasn't worth the theoretical performance benefit.

The patch also adds a function to eagerly set the number of loose
edges to zero to avoid building the cache. This is used by various
primitive nodes, with the goal of improving drawing performance.
This results in a few ms shaved off extracting draw data for some
large meshes in my tests.

In the Python API, `MeshEdge.is_loose` is no longer editable.
No built-in addons set the value anyway. The upside is that
addons can be sure the data is correct based on the mesh.

**Tests**
There is one test failure in the Python OBJ exporter: `export_obj_cube`
that happens because of existing incorrect versioning. Opening the
file in master, all the edges were set to "loose", which is fixed
by this patch.

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

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

M	source/blender/blenkernel/BKE_mesh.h
M	source/blender/blenkernel/BKE_mesh_legacy_convert.h
M	source/blender/blenkernel/BKE_mesh_types.h
M	source/blender/blenkernel/intern/bvhutils.cc
M	source/blender/blenkernel/intern/cloth.cc
M	source/blender/blenkernel/intern/curve_to_mesh_convert.cc
M	source/blender/blenkernel/intern/geometry_component_mesh.cc
M	source/blender/blenkernel/intern/mesh.cc
M	source/blender/blenkernel/intern/mesh_calc_edges.cc
M	source/blender/blenkernel/intern/mesh_convert.cc
M	source/blender/blenkernel/intern/mesh_legacy_convert.cc
M	source/blender/blenkernel/intern/mesh_runtime.cc
M	source/blender/blenkernel/intern/mesh_validate.cc
M	source/blender/blenkernel/intern/subsurf_ccg.c
M	source/blender/blenloader/intern/versioning_280.c
M	source/blender/bmesh/intern/bmesh_construct.c
M	source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
M	source/blender/editors/include/ED_mesh.h
M	source/blender/editors/mesh/mesh_data.cc
M	source/blender/editors/object/object_modifier.cc
M	source/blender/geometry/intern/mesh_merge_by_distance.cc
M	source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.cc
M	source/blender/io/collada/GeometryExporter.cpp
M	source/blender/io/collada/MeshImporter.cpp
M	source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
M	source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
M	source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
M	source/blender/io/wavefront_obj/exporter/obj_exporter.cc
M	source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
M	source/blender/makesdna/DNA_mesh_types.h
M	source/blender/makesdna/DNA_meshdata_types.h
M	source/blender/makesrna/intern/rna_mesh.c
M	source/blender/modifiers/intern/MOD_mask.cc
M	source/blender/modifiers/intern/MOD_screw.c
M	source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
M	source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
M	source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
M	source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
M	source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
M	tests/python/CMakeLists.txt

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

diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 1eadc3a39b0..d85d89b72b6 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -70,6 +70,8 @@ void BKE_mesh_tag_coords_changed(struct Mesh *mesh);
  */
 void BKE_mesh_tag_coords_changed_uniformly(struct Mesh *mesh);
 
+void BKE_mesh_tag_topology_changed(struct Mesh *mesh);
+
 /* *** mesh.c *** */
 
 struct BMesh *BKE_mesh_to_bmesh_ex(const struct Mesh *me,
@@ -943,7 +945,6 @@ void BKE_mesh_strip_loose_faces(struct Mesh *me);
 void BKE_mesh_strip_loose_polysloops(struct Mesh *me);
 void BKE_mesh_strip_loose_edges(struct Mesh *me);
 
-void BKE_mesh_calc_edges_loose(struct Mesh *mesh);
 /**
  * Calculate edges from polygons.
  */
diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
index 5eae7bf3b22..e46942aa9e9 100644
--- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h
+++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
@@ -82,6 +82,9 @@ void BKE_mesh_legacy_convert_material_indices_to_mpoly(struct Mesh *mesh);
  */
 void BKE_mesh_legacy_convert_mpoly_to_material_indices(struct Mesh *mesh);
 
+/** Convert from runtime loose edge cache to legacy edge flag. */
+void BKE_mesh_legacy_convert_loose_edges_to_flag(struct Mesh *mesh);
+
 #endif
 
 /**
diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h
index 5f20a6c2e14..9be6049fe66 100644
--- a/source/blender/blenkernel/BKE_mesh_types.h
+++ b/source/blender/blenkernel/BKE_mesh_types.h
@@ -11,6 +11,8 @@
 
 #  include <mutex>
 
+#  include "BLI_bit_vector.hh"
+#  include "BLI_shared_cache.hh"
 #  include "BLI_span.hh"
 
 #  include "DNA_customdata_types.h"
@@ -61,6 +63,23 @@ typedef enum eMeshWrapperType {
 
 namespace blender::bke {
 
+/**
+ * Cache of a mesh's loose edges, accessed with #Mesh::loose_edges(). *
+ */
+struct LooseEdgeCache {
+  /**
+   * A bitmap set to true for each loose edge, false if the edge is used by any face.
+   * Allocated only if there is at least one loose edge.
+   */
+  blender::BitVector<> is_loose_bits;
+  /**
+   * The number of loose edges. If zero, the #is_loose_bits shouldn't be accessed.
+   * If less than zero, the cache has been accessed in an invalid way
+   * (i.e.directly instead of through #Mesh::loose_edges()).
+   */
+  int count = -1;
+};
+
 /**
  * \warning Typical access is done via #Mesh::looptris().
  */
@@ -154,6 +173,12 @@ struct MeshRuntime {
   float (*vert_normals)[3] = nullptr;
   float (*poly_normals)[3] = nullptr;
 
+  /**
+   * A cache of data about the loose edges. Can be shared with other data-blocks with unchanged
+   * topology. Accessed with #Mesh::loose_edges().
+   */
+  SharedCache<LooseEdgeCache> loose_edges_cache;
+
   /**
    * A #BLI_bitmap containing tags for the center vertices of subdivided polygons, set by the
    * subdivision surface modifier and used by drawing code instead of polygon center face dots.
diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc
index b897a990aab..b8756db4736 100644
--- a/source/blender/blenkernel/intern/bvhutils.cc
+++ b/source/blender/blenkernel/intern/bvhutils.cc
@@ -1159,22 +1159,12 @@ static BitVector<> loose_verts_map_get(const Span<MEdge> edges,
   return loose_verts_mask;
 }
 
-static BitVector<> loose_edges_map_get(const Span<MEdge> edges, int *r_loose_edge_len)
+static BitVector<> loose_edges_map_get(const Mesh &mesh, int *r_loose_edge_len)
 {
-  BitVector<> loose_edges_mask(edges.size());
-
-  int loose_edges_len = 0;
-  for (const int64_t i : edges.index_range()) {
-    const MEdge &edge = edges[i];
-    if (edge.flag & ME_LOOSEEDGE) {
-      loose_edges_mask[i].set();
-      loose_edges_len++;
-    }
-  }
-
-  *r_loose_edge_len = loose_edges_len;
-
-  return loose_edges_mask;
+  using namespace blender::bke;
+  const LooseEdgeCache &loose_edges = mesh.loose_edges();
+  *r_loose_edge_len = loose_edges.count;
+  return loose_edges.is_loose_bits;
 }
 
 static BitVector<> looptri_no_hidden_map_get(const Span<MPoly> polys,
@@ -1261,7 +1251,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
       break;
 
     case BVHTREE_FROM_LOOSEEDGES:
-      mask = loose_edges_map_get(edges, &mask_bits_act_len);
+      mask = loose_edges_map_get(*mesh, &mask_bits_act_len);
       ATTR_FALLTHROUGH;
     case BVHTREE_FROM_EDGES:
       data->tree = bvhtree_from_mesh_edges_create_tree(
diff --git a/source/blender/blenkernel/intern/cloth.cc b/source/blender/blenkernel/intern/cloth.cc
index a9f7c1a45ba..120e69b4e64 100644
--- a/source/blender/blenkernel/intern/cloth.cc
+++ b/source/blender/blenkernel/intern/cloth.cc
@@ -1456,6 +1456,7 @@ static bool find_internal_spring_target_vertex(BVHTreeFromMesh *treedata,
 
 static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
 {
+  using namespace blender::bke;
   Cloth *cloth = clmd->clothObject;
   ClothSpring *spring = nullptr, *tspring = nullptr, *tspring2 = nullptr;
   uint struct_springs = 0, shear_springs = 0, bend_springs = 0, struct_springs_real = 0;
@@ -1591,12 +1592,14 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
   }
 
   /* Structural springs. */
+  const LooseEdgeCache &loose_edges = mesh->loose_edges();
   for (int i = 0; i < numedges; i++) {
     spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
 
     if (spring) {
       spring_verts_ordered_set(spring, medge[i].v1, medge[i].v2);
-      if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && medge[i].flag & ME_LOOSEEDGE) {
+      if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && loose_edges.count > 0 &&
+          loose_edges.is_loose_bits[i]) {
         /* handle sewing (loose edges will be pulled together) */
         spring->restlen = 0.0f;
         spring->lin_stiffness = 1.0f;
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 3a86068d8e8..e5cafd405df 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -46,14 +46,12 @@ static void fill_mesh_topology(const int vert_offset,
       MEdge &edge = edges[edge_offset + i];
       edge.v1 = vert_offset + i;
       edge.v2 = vert_offset + i + 1;
-      edge.flag = ME_LOOSEEDGE;
     }
 
     if (main_cyclic && main_segment_num > 1) {
       MEdge &edge = edges[edge_offset + main_segment_num - 1];
       edge.v1 = vert_offset + main_point_num - 1;
       edge.v2 = vert_offset;
-      edge.flag = ME_LOOSEEDGE;
     }
     return;
   }
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index d148d59a48b..3f1cc84a107 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -338,9 +338,6 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
   const Span<MPoly> polys = mesh.polys();
   const Span<MLoop> loops = mesh.loops();
 
-  /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
-  Array<bool> loose_edges(mesh.totedge, true);
-
   r_values.fill(true);
   for (const int poly_index : polys.index_range()) {
     const MPoly &poly = polys[poly_index];
@@ -352,22 +349,23 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
       const MLoop &loop = loops[loop_i];
       const int edge_index = loop.e;
 
-      loose_edges[edge_index] = false;
-
       if (!old_values[loop_i] || !old_values[next_loop_i]) {
         r_values[edge_index] = false;
       }
     }
   }
 
-  /* Deselect loose edges without corners that are still selected from the 'true' default. */
-  threading::parallel_for(IndexRange(mesh.totedge), 2048, [&](const IndexRange range) {
-    for (const int edge_index : range) {
-      if (loose_edges[edge_index]) {
-        r_values[edge_index] = false;
+  const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
+  if (loose_edges.count > 0) {
+    /* Deselect loose edges without corners that are still selected from the 'true' default. */
+    threading::parallel_for(IndexRange(mesh.totedge), 2048, [&](const IndexRange range) {
+      for (const int edge_index : range) {
+        if (loose_edges.is_loose_bits[edge_index]) {
+          r_values[edge_index] = false;
+        }
       }
-    }
-  });
+    });
+  }
 }
 
 static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
@@ -776,7 +774,9 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v
 
 }  // namespace blender::bke
 
-static bool can_simple_adapt_for_single(const eAttrDomain from_domain, const eAttrDomain to_domain)
+static bool can_simple_adapt_for_single(const Mesh &mesh,
+                                        const eAttrDomain from_domain,
+                                        const eAttrDomain to_domain)
 {
   /* For some domain combinations, a single value will always map directly. For others, there may
    * be loose elements on the result domain that should have the default value rather than the
@@ -790,9 +790,15 @@ static bool can_simple_adapt_for_single(const eAttrDomain from_domain, const eAt
       return ELEM(to_domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
     case ATTR_DOMAIN_FACE:
       /* There may be loose vertices or edges not connected to faces. */
+      if (to_domain == ATTR_DOMAIN_EDGE) {
+        return mesh.loose_edges().count == 0;
+      }
       return to_domain == ATTR_DOMAIN_CORNER;
     case ATTR_DOMAIN_CORNER:
       /* Only faces are always connected to corners. */
+      if (to_domain == ATTR_DOMAIN_EDGE) {
+        return mesh.loose_edges().count == 0;
+      }
       return to_domain == ATTR_DOMAIN_FACE;
     default:
       BLI_assert_unreachable();
@@ -815,7 +821,7 @@ static blender::GVArray adapt_mesh_attribute_domain(const Mesh &mesh,
     return varray;
   }
   if (varray.is_single()) {
-    if (can_simple_adapt_for_single(from_domain, to_domain)) {
+    if (can_simple_adapt_for_single(mes

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list