[Bf-blender-cvs] [a16659efb69] temp-vert-normals-cleanup: Cleanup: Use C++ types and lambdas for mesh normal calculation

Hans Goudey noreply at git.blender.org
Sun Sep 5 18:04:52 CEST 2021


Commit: a16659efb69570fa376b7a2fa926e18bdbdc6b5a
Author: Hans Goudey
Date:   Sat Sep 4 14:54:42 2021 -0500
Branches: temp-vert-normals-cleanup
https://developer.blender.org/rBa16659efb69570fa376b7a2fa926e18bdbdc6b5a

Cleanup: Use C++ types and lambdas for mesh normal calculation

 - Use `threading::parallel_for` for multithreading, for a simpler API,
   more readable and concise code.
 - Use `Span` and `Array` (only internally, the public API is still C)
   for safer, more automatic memory management.
 - Since code is much less verbose, combine the callbacks into the
   main function. Note that the accumulation code could be more concise
   with `float3`, I just wanted to keep these changes minimal.

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

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

M	source/blender/blenkernel/intern/mesh_normals.cc

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

diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index 9a761c6fa11..e6df3161fae 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -35,12 +35,16 @@
 #include "BLI_alloca.h"
 #include "BLI_bitmap.h"
 
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
 #include "BLI_linklist.h"
 #include "BLI_linklist_stack.h"
 #include "BLI_math.h"
 #include "BLI_memarena.h"
+#include "BLI_span.hh"
 #include "BLI_stack.h"
 #include "BLI_task.h"
+#include "BLI_task.hh"
 #include "BLI_utildefines.h"
 
 #include "BKE_customdata.h"
@@ -50,6 +54,13 @@
 
 #include "atomic_ops.h"
 
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using namespace blender::threading;
+
 // #define DEBUG_TIME
 
 #ifdef DEBUG_TIME
@@ -115,24 +126,6 @@ void BKE_mesh_normals_tag_dirty(Mesh *mesh)
 /** \name Mesh Normal Calculation (Polygons)
  * \{ */
 
-struct MeshCalcNormalsData_Poly {
-  const MVert *mvert;
-  const MLoop *mloop;
-  const MPoly *mpoly;
-
-  /** Polygon normal output. */
-  float (*pnors)[3];
-};
-
-static void mesh_calc_normals_poly_fn(void *__restrict userdata,
-                                      const int pidx,
-                                      const TaskParallelTLS *__restrict UNUSED(tls))
-{
-  const MeshCalcNormalsData_Poly *data = (MeshCalcNormalsData_Poly *)userdata;
-  const MPoly *mp = &data->mpoly[pidx];
-  BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mvert, data->pnors[pidx]);
-}
-
 void BKE_mesh_calc_normals_poly(const MVert *mvert,
                                 int UNUSED(mvert_len),
                                 const MLoop *mloop,
@@ -141,19 +134,14 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert,
                                 int mpoly_len,
                                 float (*r_poly_normals)[3])
 {
-  TaskParallelSettings settings;
-  BLI_parallel_range_settings_defaults(&settings);
-  settings.min_iter_per_thread = 1024;
-
   BLI_assert((r_poly_normals != nullptr) || (mpoly_len == 0));
 
-  MeshCalcNormalsData_Poly data = {};
-  data.mpoly = mpoly;
-  data.mloop = mloop;
-  data.mvert = mvert;
-  data.pnors = r_poly_normals;
-
-  BLI_task_parallel_range(0, mpoly_len, &data, mesh_calc_normals_poly_fn, &settings);
+  blender::threading::parallel_for(IndexRange(mpoly_len), 1024, [&](IndexRange range) {
+    for (const int i : range) {
+      const MPoly &poly = mpoly[i];
+      BKE_mesh_calc_poly_normal(&poly, &mloop[poly.loopstart], mvert, r_poly_normals[i]);
+    }
+  });
 }
 
 /** \} */
@@ -167,139 +155,108 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert,
  * meshes can slow down high-poly meshes. For details on performance, see D11993.
  * \{ */
 
-struct MeshCalcNormalsData_PolyAndVertex {
-  /** Write into vertex normals #MVert.no. */
-  MVert *mvert;
-  const MLoop *mloop;
-  const MPoly *mpoly;
-
-  /** Polygon normal output. */
-  float (*pnors)[3];
-  /** Vertex normal output (may be freed, copied into #MVert.no). */
-  float (*vnors)[3];
-};
-
-static void mesh_calc_normals_poly_and_vertex_accum_fn(
-    void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls))
+static void mesh_calc_normals_poly_and_vertex(MutableSpan<MVert> mverts,
+                                              Span<MLoop> loops,
+                                              Span<MPoly> polys,
+                                              MutableSpan<float3> poly_normals,
+                                              MutableSpan<float3> vert_normals)
 {
-  const MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
-  const MPoly *mp = &data->mpoly[pidx];
-  const MLoop *ml = &data->mloop[mp->loopstart];
-  const MVert *mverts = data->mvert;
-  float(*vnors)[3] = data->vnors;
-
-  float pnor_temp[3];
-  float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp;
+  Array<float3> vert_normals_internal;
+  if (vert_normals.is_empty()) {
+    vert_normals_internal.reinitialize(mverts.size());
+    vert_normals = vert_normals_internal;
+  }
+
+  vert_normals.fill(float3(0));
+
+  /* Compute poly normals, accumulating them into vertex normals. */
+  blender::threading::parallel_for(polys.index_range(), 1024, [&](IndexRange range) {
+    for (const int i : range) {
+      const MPoly &poly = polys[i];
+      Span<MLoop> ml = loops.slice(poly.loopstart, poly.totloop);
+
+      float3 pnor_temp;
+      float3 &pnor = poly_normals.is_empty() ? pnor_temp : poly_normals[i];
+
+      const int i_end = poly.totloop - 1;
+
+      /* Polygon Normal and edge-vector. */
+      /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */
+      {
+        zero_v3(pnor);
+        /* Newell's Method */
+        const float *v_curr = mverts[ml[i_end].v].co;
+        for (int i_next = 0; i_next <= i_end; i_next++) {
+          const float *v_next = mverts[ml[i_next].v].co;
+          add_newell_cross_v3_v3v3(pnor, v_curr, v_next);
+          v_curr = v_next;
+        }
+        if (UNLIKELY(normalize_v3(pnor) == 0.0f)) {
+          pnor[2] = 1.0f; /* Other axes set to zero. */
+        }
+      }
 
-  const int i_end = mp->totloop - 1;
+      /* Accumulate angle weighted face normal into the vertex normal. */
+      /* Inline version of #accumulate_vertex_normals_poly_v3. */
+      {
+        float edvec_prev[3], edvec_next[3], edvec_end[3];
+        const float *v_curr = mverts[ml[i_end].v].co;
+        sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr);
+        normalize_v3(edvec_prev);
+        copy_v3_v3(edvec_end, edvec_prev);
+
+        for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) {
+          const float *v_next = mverts[ml[i_next].v].co;
+
+          /* Skip an extra normalization by reusing the first calculated edge. */
+          if (i_next != i_end) {
+            sub_v3_v3v3(edvec_next, v_curr, v_next);
+            normalize_v3(edvec_next);
+          }
+          else {
+            copy_v3_v3(edvec_next, edvec_end);
+          }
 
-  /* Polygon Normal and edge-vector. */
-  /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */
-  {
-    zero_v3(pnor);
-    /* Newell's Method */
-    const float *v_curr = mverts[ml[i_end].v].co;
-    for (int i_next = 0; i_next <= i_end; i_next++) {
-      const float *v_next = mverts[ml[i_next].v].co;
-      add_newell_cross_v3_v3v3(pnor, v_curr, v_next);
-      v_curr = v_next;
-    }
-    if (UNLIKELY(normalize_v3(pnor) == 0.0f)) {
-      pnor[2] = 1.0f; /* Other axes set to zero. */
-    }
-  }
+          /* Calculate angle between the two poly edges incident on this vertex. */
+          const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next));
+          const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac};
 
-  /* Accumulate angle weighted face normal into the vertex normal. */
-  /* Inline version of #accumulate_vertex_normals_poly_v3. */
-  {
-    float edvec_prev[3], edvec_next[3], edvec_end[3];
-    const float *v_curr = mverts[ml[i_end].v].co;
-    sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr);
-    normalize_v3(edvec_prev);
-    copy_v3_v3(edvec_end, edvec_prev);
-
-    for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) {
-      const float *v_next = mverts[ml[i_next].v].co;
-
-      /* Skip an extra normalization by reusing the first calculated edge. */
-      if (i_next != i_end) {
-        sub_v3_v3v3(edvec_next, v_curr, v_next);
-        normalize_v3(edvec_next);
+          add_v3_v3_atomic(vert_normals[ml[i_curr].v], vnor_add);
+          v_curr = v_next;
+          copy_v3_v3(edvec_prev, edvec_next);
+        }
       }
-      else {
-        copy_v3_v3(edvec_next, edvec_end);
+    }
+  });
+
+  /* Normalize and validate computed vertex normals. */
+  blender::threading::parallel_for(mverts.index_range(), 1024, [&](IndexRange range) {
+    for (const int i : range) {
+      MVert &vert = mverts[i];
+      float3 &no = vert_normals[i];
+      if (UNLIKELY(normalize_v3(no) == 0.0f)) {
+        /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
+        normalize_v3_v3(no, vert.co);
       }
-
-      /* Calculate angle between the two poly edges incident on this vertex. */
-      const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next));
-      const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac};
-
-      add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add);
-      v_curr = v_next;
-      copy_v3_v3(edvec_prev, edvec_next);
+      normal_float_to_short_v3(vert.no, no);
     }
-  }
-}
-
-static void mesh_calc_normals_poly_and_vertex_finalize_fn(
-    void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict UNUSED(tls))
-{
-  MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
-
-  MVert *mv = &data->mvert[vidx];
-  float *no = data->vnors[vidx];
-
-  if (UNLIKELY(normalize_v3(no) == 0.0f)) {
-    /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
-    normalize_v3_v3(no, mv->co);
-  }
-
-  normal_float_to_short_v3(mv->no, no);
+  });
 }
 
 void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert,
                                            const int mvert_len,
                                            const MLoop *mloop,
-                                           const int UNUSED(mloop_len),
+                                           const int mloop_len,
                                            const MPoly *mpoly,
                                            const int mpoly_len,
                                            float (*r_poly_normals)[3],
                                            float (*r_vert_normals)[3])
 {
-  TaskParallelSettings settings;
-  BLI_parallel_range_settings_defaults(&settings);
-  settings.min_iter_per_thread = 1024;
-
-  float(*vnors)[3] = r_vert_normals;
-  bool free_vnors = false;
-
-  /* First go through and calculate normals for all the polys. */
-  if (vnors == nullptr) {
-    vnors = (floa

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list