[Bf-blender-cvs] [7e7c6bc4689] master: Cleanup: Use spans and lambdas for mesh normal calculation
Hans Goudey
noreply at git.blender.org
Mon Nov 28 19:30:04 CET 2022
Commit: 7e7c6bc46896ad3589320f062601b310070870b5
Author: Hans Goudey
Date: Mon Nov 28 09:54:07 2022 -0600
Branches: master
https://developer.blender.org/rB7e7c6bc46896ad3589320f062601b310070870b5
Cleanup: Use spans and lambdas for mesh normal calculation
Makes code safer, easier to understand, and less verbose. I detected
negligible performance differences, only a slight improvement for the
normalize step where the function call overhead was probably more
of a bottleneck.
I kept `memset` instead of `.fill(float3(0))` because that gave
better performance in my tests. In the future that stage could be
parallelized, or we could make sure new arrays are allocated with
`calloc`.
===================================================================
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 d4ba2499140..e7788d82769 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -260,45 +260,32 @@ void BKE_mesh_calc_poly_normal_coords(const MPoly *mpoly,
}
}
-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 /*tls*/)
+static void calculate_normals_poly(const Span<MVert> verts,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ MutableSpan<float3> poly_normals)
{
- 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]);
+ using namespace blender;
+ threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
+ for (const int poly_i : range) {
+ const MPoly &poly = polys[poly_i];
+ BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), poly_normals[poly_i]);
+ }
+ });
}
void BKE_mesh_calc_normals_poly(const MVert *mvert,
- int /*mvert_len*/,
+ const int mvert_len,
const MLoop *mloop,
- int /*mloop_len*/,
+ const int mloop_len,
const MPoly *mpoly,
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);
+ calculate_normals_poly({mvert, mvert_len},
+ {mpoly, mpoly_len},
+ {mloop, mloop_len},
+ {reinterpret_cast<float3 *>(r_poly_normals), mpoly_len});
}
/** \} */
@@ -310,123 +297,110 @@ 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 {
- const MVert *mvert;
- const MLoop *mloop;
- const MPoly *mpoly;
-
- /** Polygon normal output. */
- float (*pnors)[3];
- /** Vertex normal output. */
- float (*vnors)[3];
-};
-
-static void mesh_calc_normals_poly_and_vertex_accum_fn(void *__restrict userdata,
- const int pidx,
- const TaskParallelTLS *__restrict /*tls*/)
+static void calculate_normals_poly_and_vert(const Span<MVert> verts,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ 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;
-
- const int i_end = mp->totloop - 1;
+ using namespace blender;
- /* Polygon Normal and edge-vector. */
- /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */
+ /* Zero the vertex normal array for accumulation. */
{
- 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. */
- }
+ memset(vert_normals.data(), 0, vert_normals.as_span().size_in_bytes());
}
- /* Accumulate angle weighted face normal into the vertex normal. */
- /* Inline version of #accumulate_vertex_normals_poly_v3. */
+ /* Compute poly normals, accumulating them into vertex normals. */
{
- 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);
- }
-
- /* 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};
+ threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
+ for (const int poly_i : range) {
+ const MPoly &poly = polys[poly_i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ float3 &pnor = poly_normals[poly_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 = verts[poly_loops[i_end].v].co;
+ for (int i_next = 0; i_next <= i_end; i_next++) {
+ const float *v_next = verts[poly_loops[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. */
+ }
+ }
- add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add);
- v_curr = v_next;
- copy_v3_v3(edvec_prev, edvec_next);
- }
+ /* 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 = verts[poly_loops[i_end].v].co;
+ sub_v3_v3v3(edvec_prev, verts[poly_loops[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 = verts[poly_loops[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);
+ }
+
+ /* 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};
+
+ float *vnor = vert_normals[poly_loops[i_curr].v];
+ add_v3_v3_atomic(vnor, vnor_add);
+ v_curr = v_next;
+ copy_v3_v3(edvec_prev, edvec_next);
+ }
+ }
+ }
+ });
}
-}
-
-static void mesh_calc_normals_poly_and_vertex_finalize_fn(
- void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict /*tls*/)
-{
- MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
- const MVert *mv = &data->mvert[vidx];
- float *no = data->vnors[vidx];
+ /* Normalize and validate computed vertex normals. */
+ {
+ threading::parallel_for(verts.index_range(), 1024, [&](const IndexRange range) {
+ for (const int vert_i : range) {
+ float *no = vert_normals[vert_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, mv->co);
+ if (UNLIKELY(normalize_v3(no) == 0.0f)) {
+ /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
+ normalize_v3_v3(no, verts[vert_i].co);
+ }
+ }
+ });
}
}
void BKE_mesh_calc_normals_poly_and_vertex(const MVert *mvert,
const int mvert_len,
const MLoop *mloop,
- const int /*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;
-
- memset(r_vert_normals, 0, sizeof(*r_vert_normals) * size_t(mvert_len));
-
- MeshCalcNormalsData_PolyAndVertex data = {};
- data.mpoly = mpoly;
- data.m
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list