Commit: 445d506ac9b59c4eb9de8a475fd89ba908ecbcdf
Author: Hans Goudey
Date:   Mon Jun 21 16:17:02 2021 -0500
Branches: master
https://developer.blender.org/rB445d506ac9b59c4eb9de8a475fd89ba908ecbcdf

Geometry Nodes: Use multithreading for the curve to mesh node

This commit optimizes the node for the case where it works on many
splines by allowing it to generate mesh data from their combinations
in parallel. By itself, this made the node around twice as fast in my
test file with a result of 20 million vertices, around 600ms instead of
1.2s before.

That isn't actually a very good result; it reveals another bottleneck,
a single threaded loop over all face corners in the mesh normal
calculation code. As a simple change that might improve performance
in some situations, this commit moves normal calculation out of this
node, so at least the work isn't wasted if the mesh is changed later
on in the node tree anyway.

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

M       source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc

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

diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc 
b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index fc8558dfd2f..c0d817385e2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -16,7 +16,7 @@
 
 #include "BLI_array.hh"
 #include "BLI_float4x4.hh"
-#include "BLI_timeit.hh"
+#include "BLI_task.hh"
 
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
@@ -47,8 +47,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
                                       const float3 profile_vert,
                                       MutableSpan<MVert> r_verts,
                                       MutableSpan<MEdge> r_edges,
-                                      int &vert_offset,
-                                      int &edge_offset)
+                                      int vert_offset,
+                                      int edge_offset)
 {
   Span<float3> positions = spline.evaluated_positions();
 
@@ -85,10 +85,10 @@ static void spline_extrude_to_mesh_data(const Spline 
&spline,
                                         MutableSpan<MEdge> r_edges,
                                         MutableSpan<MLoop> r_loops,
                                         MutableSpan<MPoly> r_polys,
-                                        int &vert_offset,
-                                        int &edge_offset,
-                                        int &loop_offset,
-                                        int &poly_offset)
+                                        int vert_offset,
+                                        int edge_offset,
+                                        int loop_offset,
+                                        int poly_offset)
 {
   const int spline_vert_len = spline.evaluated_points_size();
   const int spline_edge_len = spline.evaluated_edges_size();
@@ -207,63 +207,114 @@ static void spline_extrude_to_mesh_data(const Spline 
&spline,
   }
 }
 
-static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval 
&profile_curve)
+static inline int spline_extrude_vert_size(const Spline &curve, const Spline 
&profile)
 {
-  int profile_vert_total = 0;
-  int profile_edge_total = 0;
-  for (const SplinePtr &profile_spline : profile_curve.splines()) {
-    profile_vert_total += profile_spline->evaluated_points_size();
-    profile_edge_total += profile_spline->evaluated_edges_size();
-  }
+  return curve.evaluated_points_size() * profile.evaluated_points_size();
+}
 
-  int vert_total = 0;
-  int edge_total = 0;
-  int poly_total = 0;
-  for (const SplinePtr &spline : curve.splines()) {
-    const int spline_vert_len = spline->evaluated_points_size();
-    const int spline_edge_len = spline->evaluated_edges_size();
-    vert_total += spline_vert_len * profile_vert_total;
-    poly_total += spline_edge_len * profile_edge_total;
-
-    /* Add the ring edges, with one ring for every curve vertex, and the edge 
loops
-     * that run along the length of the curve, starting on the first profile. 
*/
-    edge_total += profile_edge_total * spline_vert_len + profile_vert_total * 
spline_edge_len;
-  }
-  const int corner_total = poly_total * 4;
+static inline int spline_extrude_edge_size(const Spline &curve, const Spline 
&profile)
+{
+  /* Add the ring edges, with one ring for every curve vertex, and the edge 
loops
+   * that run along the length of the curve, starting on the first profile. */
+  return curve.evaluated_points_size() * profile.evaluated_edges_size() +
+         curve.evaluated_edges_size() * profile.evaluated_points_size();
+}
 
-  if (vert_total == 0) {
-    return nullptr;
-  }
+static inline int spline_extrude_loop_size(const Spline &curve, const Spline 
&profile)
+{
+  return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
+}
 
-  Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, 
poly_total);
-  BKE_id_material_eval_ensure_default_slot(&mesh->id);
-  MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
-  MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
-  MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
-  MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
-  mesh->flag |= ME_AUTOSMOOTH;
-  mesh->smoothresh = DEG2RADF(180.0f);
+static inline int spline_extrude_poly_size(const Spline &curve, const Spline 
&profile)
+{
+  return curve.evaluated_edges_size() * profile.evaluated_edges_size();
+}
 
+struct ResultOffsets {
+  Array<int> vert;
+  Array<int> edge;
+  Array<int> loop;
+  Array<int> poly;
+};
+static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, 
Span<SplinePtr> curves)
+{
+  const int total = profiles.size() * curves.size();
+  Array<int> vert(total + 1);
+  Array<int> edge(total + 1);
+  Array<int> loop(total + 1);
+  Array<int> poly(total + 1);
+
+  int mesh_index = 0;
   int vert_offset = 0;
   int edge_offset = 0;
   int loop_offset = 0;
   int poly_offset = 0;
-  for (const SplinePtr &spline : curve.splines()) {
-    for (const SplinePtr &profile_spline : profile_curve.splines()) {
-      spline_extrude_to_mesh_data(*spline,
-                                  *profile_spline,
-                                  verts,
-                                  edges,
-                                  loops,
-                                  polys,
-                                  vert_offset,
-                                  edge_offset,
-                                  loop_offset,
-                                  poly_offset);
+  for (const int i_spline : curves.index_range()) {
+    for (const int i_profile : profiles.index_range()) {
+      vert[mesh_index] = vert_offset;
+      edge[mesh_index] = edge_offset;
+      loop[mesh_index] = loop_offset;
+      poly[mesh_index] = poly_offset;
+      vert_offset += spline_extrude_vert_size(*curves[i_spline], 
*profiles[i_profile]);
+      edge_offset += spline_extrude_edge_size(*curves[i_spline], 
*profiles[i_profile]);
+      loop_offset += spline_extrude_loop_size(*curves[i_spline], 
*profiles[i_profile]);
+      poly_offset += spline_extrude_poly_size(*curves[i_spline], 
*profiles[i_profile]);
+      mesh_index++;
     }
   }
+  vert.last() = vert_offset;
+  edge.last() = edge_offset;
+  loop.last() = loop_offset;
+  poly.last() = poly_offset;
+
+  return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)};
+}
+
+/**
+ * \note Normal calculation is by far the slowest part of calculations 
relating to the result mesh.
+ * Although it would be a sensible decision to use the better topology 
information available while
+ * generating the mesh to also generate the normals, that work may wasted if 
the output mesh is
+ * changed anyway in a way that affects the normals. So currently this code 
uses the safer /
+ * simpler solution of not calculating normals.
+ */
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval 
&profile)
+{
+  Span<SplinePtr> profiles = profile.splines();
+  Span<SplinePtr> curves = curve.splines();
+
+  const ResultOffsets offsets = calculate_result_offsets(profiles, curves);
+  if (offsets.vert.last() == 0) {
+    return nullptr;
+  }
 
-  BKE_mesh_calc_normals(mesh);
+  Mesh *mesh = BKE_mesh_new_nomain(
+      offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), 
offsets.poly.last());
+  BKE_id_material_eval_ensure_default_slot(&mesh->id);
+  mesh->flag |= ME_AUTOSMOOTH;
+  mesh->smoothresh = DEG2RADF(180.0f);
+  mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+  mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
+
+  threading::parallel_for(curves.index_range(), 128, [&](IndexRange 
curves_range) {
+    for (const int i_spline : curves_range) {
+      const int spline_start_index = i_spline * profiles.size();
+      threading::parallel_for(profiles.index_range(), 128, [&](IndexRange 
profiles_range) {
+        for (const int i_profile : profiles_range) {
+          const int i_mesh = spline_start_index + i_profile;
+          spline_extrude_to_mesh_data(*curves[i_spline],
+                                      *profiles[i_profile],
+                                      {mesh->mvert, mesh->totvert},
+                                      {mesh->medge, mesh->totedge},
+                                      {mesh->mloop, mesh->totloop},
+                                      {mesh->mpoly, mesh->totpoly},
+                                      offsets.vert[i_mesh],
+                                      offsets.edge[i_mesh],
+                                      offsets.loop[i_mesh],
+                                      offsets.poly[i_mesh]);
+        }
+      });
+    }
+  });
 
   return mesh;
 }

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to