From aaa25bc882faf0a22d8e1e6803cb32789c0772ac Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 8 Jan 2024 18:49:26 +0100 Subject: [PATCH] Sculpt: Improve mesh normals update performance Instead of storing a boolean "update tag" for every vertex, just recalculate the normals of all the faces and vertices in affected PBVH nodes. This avoids the overhead of tracking position updates at the vertex level, and simplifies the normal calculation, since we can just calculate values for all the normals in a node. The main way we gain performance is avoiding building a `VectorSet` for all vertices and faces that need updates in the entire mesh. That process had to be single threaded, and was a large bottleneck when many vertices were affected at a time. That `VectorSet` was necessary for thread safety deduplication of work though, because neighboring nodes can't calculate the normals of the same faces or vertices at the same time. (Normals need to be calculated for all faces connected to moved vertices and all vertices connected to those faces). In this PR, we only build a set of the *boundary* faces and vertices. We calculate those in a separate step, which duplicates work from the non-boundary calculations, but at least it's threadsafe. I measured normal recalculation timing when sculpting on a 16 million vertex mesh. The removal of the `vert_bitmap` array also saves 16 MB of memory. | Nodes | Affected Vertices | Before (ms) | After (ms) | | ----- | ------------ | ----------- | ---------- | | 4 | 15625 | 0.208 | 0.304 | | 35 | 136281 | 2.98 | 0.988 | | 117 | 457156 | 15.0 | 3.21 | | 2455 | 9583782 | 566 | 84.0 | Pull Request: https://projects.blender.org/blender/blender/pulls/116209 --- source/blender/blenkernel/BKE_pbvh_api.hh | 8 - source/blender/blenkernel/intern/pbvh.cc | 173 +++++++++++------- .../blender/blenkernel/intern/pbvh_intern.hh | 4 - .../editors/sculpt_paint/paint_mask.cc | 3 - source/blender/editors/sculpt_paint/sculpt.cc | 7 - .../editors/sculpt_paint/sculpt_boundary.cc | 24 --- .../sculpt_paint/sculpt_brush_types.cc | 91 --------- .../editors/sculpt_paint/sculpt_cloth.cc | 4 - .../editors/sculpt_paint/sculpt_face_set.cc | 4 - .../sculpt_paint/sculpt_filter_mesh.cc | 3 - .../sculpt_paint/sculpt_multiplane_scrape.cc | 4 - .../editors/sculpt_paint/sculpt_pose.cc | 4 - .../editors/sculpt_paint/sculpt_smooth.cc | 10 - .../editors/sculpt_paint/sculpt_transform.cc | 8 - .../editors/sculpt_paint/sculpt_undo.cc | 25 ++- 15 files changed, 123 insertions(+), 249 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index 6695b3c73c3..7f2f4fbef9b 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -295,7 +295,6 @@ void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_unmasked_get(const PBVHNode *node); void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex); blender::Span BKE_pbvh_node_get_grid_indices(const PBVHNode &node); @@ -515,13 +514,6 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, float (**r_orco_coords)[3], BMVert ***r_orco_verts); -/** - * \note doing a full search on all vertices here seems expensive, - * however this is important to avoid having to recalculate bound-box & sync the buffers to the - * GPU (which is far more expensive!) See: #47232. - */ -bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node); - bool pbvh_has_mask(const PBVH *pbvh); bool pbvh_has_face_sets(PBVH *pbvh); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 0c211bb088a..76dab77f315 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -14,6 +14,7 @@ #include "BLI_bit_span_ops.hh" #include "BLI_bitmap.h" #include "BLI_bounds.hh" +#include "BLI_enumerable_thread_specific.hh" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" @@ -723,7 +724,7 @@ PBVH *build_mesh(Mesh *mesh) update_mesh_pointers(pbvh.get(), mesh); const Span tri_faces = pbvh->corner_tri_faces; - pbvh->vert_bitmap = blender::Array(totvert, false); + Array vert_bitmap(totvert, false); pbvh->totvert = totvert; #ifdef TEST_PBVH_FACE_SPLIT @@ -768,7 +769,7 @@ PBVH *build_mesh(Mesh *mesh) hide_poly, material_index, sharp_face, - pbvh->vert_bitmap, + vert_bitmap, &cb, prim_bounds, corner_tris_num); @@ -778,9 +779,6 @@ PBVH *build_mesh(Mesh *mesh) #endif } - /* Clear the bitmap so it can be used as an update tag later on. */ - pbvh->vert_bitmap.fill(false); - BKE_pbvh_update_active_vcol(pbvh.get(), mesh); #ifdef VALIDATE_UNIQUE_NODE_FACES @@ -1130,65 +1128,124 @@ static bool update_search(PBVHNode *node, const int flag) } static void normals_calc_faces(const Span positions, - const blender::OffsetIndices faces, + const OffsetIndices faces, const Span corner_verts, - const Span mask, + const Span face_indices, MutableSpan face_normals) { - threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) { - for (const int i : mask.slice(range)) { - face_normals[i] = mesh::face_normal_calc(positions, corner_verts.slice(faces[i])); + for (const int i : face_indices) { + face_normals[i] = mesh::face_normal_calc(positions, corner_verts.slice(faces[i])); + } +} + +static void calc_boundary_face_normals(const Span positions, + const OffsetIndices faces, + const Span corner_verts, + const Span face_indices, + MutableSpan face_normals) +{ + threading::parallel_for(face_indices.index_range(), 512, [&](const IndexRange range) { + normals_calc_faces(positions, faces, corner_verts, face_indices.slice(range), face_normals); + }); +} + +static void calc_node_face_normals(const Span positions, + const OffsetIndices faces, + const Span corner_verts, + const PBVH &pbvh, + const Span nodes, + MutableSpan face_normals) +{ + threading::EnumerableThreadSpecific> all_index_data; + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + Vector &node_faces = all_index_data.local(); + for (const PBVHNode *node : nodes.slice(range)) { + normals_calc_faces(positions, + faces, + corner_verts, + node_face_indices_calc_mesh(pbvh, *node, node_faces), + face_normals); } }); } -static void normals_calc_verts_simple(const blender::GroupedSpan vert_to_face_map, +static void normals_calc_verts_simple(const GroupedSpan vert_to_face_map, const Span face_normals, - const Span mask, + const Span verts, MutableSpan vert_normals) { - threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { - for (const int vert : mask.slice(range)) { - float3 normal(0.0f); - for (const int face : vert_to_face_map[vert]) { - normal += face_normals[face]; - } - vert_normals[vert] = math::normalize(normal); + for (const int vert : verts) { + float3 normal(0.0f); + for (const int face : vert_to_face_map[vert]) { + normal += face_normals[face]; + } + vert_normals[vert] = math::normalize(normal); + } +} + +static void calc_boundary_vert_normals(const GroupedSpan vert_to_face_map, + const Span face_normals, + const Span verts, + MutableSpan vert_normals) +{ + threading::parallel_for(verts.index_range(), 1024, [&](const IndexRange range) { + normals_calc_verts_simple(vert_to_face_map, face_normals, verts.slice(range), vert_normals); + }); +} + +static void calc_node_vert_normals(const GroupedSpan vert_to_face_map, + const Span face_normals, + const Span nodes, + MutableSpan vert_normals) +{ + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + for (const PBVHNode *node : nodes.slice(range)) { + normals_calc_verts_simple(vert_to_face_map, + face_normals, + node->vert_indices.as_span().take_front(node->uniq_verts), + vert_normals); } }); } -static void pbvh_faces_update_normals(PBVH &pbvh, Span nodes, Mesh &mesh) +static void update_normals_faces(PBVH &pbvh, Span nodes, Mesh &mesh) { + /* Position changes are tracked on a per-node level, so all the vertex and face normals for every + * affected node are recalculated. However, the additional complexity comes from the fact that + * changing vertex normals also changes surrounding face normals. Those changed face normals then + * change the normals of all connected vertices, which can be in other nodes. So the set of + * vertices that need recalculated normals can propagate into unchanged/untagged PBVH nodes. + * + * Currently we have no good way of finding neighboring PBVH nodes, so we use the vertex to + * face topology map to find the neighboring vertices that need normal recalculation. + * + * Those boundary face and vertex indices are deduplicated with #VectorSet in order to avoid + * duplicate work recalculation for the same vertex, and to make parallel storage for vertices + * during reclculation thread-safe. */ const Span positions = pbvh.vert_positions; const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); - MutableSpan update_tags = pbvh.vert_bitmap; - - VectorSet faces_to_update; + VectorSet boundary_faces; for (const PBVHNode *node : nodes) { - for (const int vert : node->vert_indices.as_span().take_front(node->uniq_verts)) { - if (update_tags[vert]) { - faces_to_update.add_multiple(pbvh.vert_to_face_map[vert]); - } + for (const int vert : node->vert_indices.as_span().drop_front(node->uniq_verts)) { + boundary_faces.add_multiple(pbvh.vert_to_face_map[vert]); } } - if (faces_to_update.is_empty()) { - return; - } - - VectorSet verts_to_update; + VectorSet boundary_verts; threading::parallel_invoke( [&]() { if (pbvh.deformed) { - normals_calc_faces( - positions, faces, corner_verts, faces_to_update, pbvh.face_normals_deformed); + calc_node_face_normals( + positions, faces, corner_verts, pbvh, nodes, pbvh.face_normals_deformed); + calc_boundary_face_normals( + positions, faces, corner_verts, boundary_faces, pbvh.face_normals_deformed); } else { mesh.runtime->face_normals_cache.update([&](Vector &r_data) { - normals_calc_faces(positions, faces, corner_verts, faces_to_update, r_data); + calc_node_face_normals(positions, faces, corner_verts, pbvh, nodes, r_data); + calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_data); }); /* #SharedCache::update() reallocates cached vectors if they were shared initially. */ pbvh.face_normals = mesh.runtime->face_normals_cache.data(); @@ -1196,29 +1253,29 @@ static void pbvh_faces_update_normals(PBVH &pbvh, Span nodes, Mesh & }, [&]() { /* Update all normals connected to affected faces, even if not explicitly tagged. */ - verts_to_update.reserve(faces_to_update.size()); - for (const int face : faces_to_update) { - verts_to_update.add_multiple(corner_verts.slice(faces[face])); - } - - for (const int vert : verts_to_update) { - update_tags[vert] = false; - } - for (PBVHNode *node : nodes) { - node->flag &= ~PBVH_UpdateNormals; + boundary_verts.reserve(boundary_faces.size()); + for (const int face : boundary_faces) { + boundary_verts.add_multiple(corner_verts.slice(faces[face])); } }); if (pbvh.deformed) { - normals_calc_verts_simple( - pbvh.vert_to_face_map, pbvh.face_normals, verts_to_update, pbvh.vert_normals_deformed); + calc_node_vert_normals( + pbvh.vert_to_face_map, pbvh.face_normals, nodes, pbvh.vert_normals_deformed); + calc_boundary_vert_normals( + pbvh.vert_to_face_map, pbvh.face_normals, boundary_verts, pbvh.vert_normals_deformed); } else { mesh.runtime->vert_normals_cache.update([&](Vector &r_data) { - normals_calc_verts_simple(pbvh.vert_to_face_map, pbvh.face_normals, verts_to_update, r_data); + calc_node_vert_normals(pbvh.vert_to_face_map, pbvh.face_normals, nodes, r_data); + calc_boundary_vert_normals(pbvh.vert_to_face_map, pbvh.face_normals, boundary_verts, r_data); }); pbvh.vert_normals = mesh.runtime->vert_normals_cache.data(); } + + for (PBVHNode *node : nodes) { + node->flag &= ~PBVH_UpdateNormals; + } } void update_normals(PBVH &pbvh, SubdivCCG *subdiv_ccg) @@ -1230,7 +1287,7 @@ void update_normals(PBVH &pbvh, SubdivCCG *subdiv_ccg) bmesh_normals_update(nodes); } else if (pbvh.header.type == PBVH_FACES) { - pbvh_faces_update_normals(pbvh, nodes, *pbvh.mesh); + update_normals_faces(pbvh, nodes, *pbvh.mesh); } else if (pbvh.header.type == PBVH_GRIDS) { IndexMaskMemory memory; @@ -1729,12 +1786,6 @@ bool BKE_pbvh_node_fully_unmasked_get(const PBVHNode *node) return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked); } -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - pbvh->vert_bitmap[vertex.i] = true; -} - blender::Span BKE_pbvh_node_get_loops(const PBVHNode *node) { return node->loop_indices; @@ -1853,17 +1904,6 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, } } -bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - for (const int vert : node->vert_indices) { - if (pbvh->vert_bitmap[vert]) { - return true; - } - } - return false; -} - /********************************* Ray-cast ***********************************/ namespace blender::bke::pbvh { @@ -2817,7 +2857,6 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const Span vert_positions) /* no need for float comparison here (memory is exactly equal or not) */ if (memcmp(positions[a], vert_positions[a], sizeof(float[3])) != 0) { positions[a] = vert_positions[a]; - BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a)); } } diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index a21fad24de5..76884c11ed5 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -161,10 +161,6 @@ struct PBVH { CCGKey gridkey; SubdivCCG *subdiv_ccg; - /* Used during BVH build and later to mark that a vertex needs to update - * (its normal must be recalculated). */ - blender::Array vert_bitmap; - #ifdef PERFCNTRS int perf_modified; #endif diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index b0334495bf6..0079c3e939e 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1930,9 +1930,6 @@ static void project_line_gesture_apply_task(SculptGestureContext *sgcontext, PBV continue; } add_v3_v3(vd.co, disp); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex); - } any_updated = true; } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index d7327f88273..8331f1fd7f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1370,9 +1370,6 @@ static void paint_mesh_restore_node(Object *ob, const undo::Type type, PBVHNode BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_vert_data, &vd); copy_v3_v3(vd.co, orig_vert_data.co); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_update(node); @@ -3113,10 +3110,6 @@ static void do_gravity_task(SculptSession *ss, ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask, vd.vertex, thread_id, nullptr); mul_v3_v3fl(proxy[vd.i], offset, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 84e9684b80f..48b0258dfb6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -684,10 +684,6 @@ static void do_boundary_brush_bend_task(Object *ob, const Brush *brush, PBVHNode boundary->bend.pivot_rotation_axis[vd.index], angle * boundary->edit_info[vd.index].strength_factor * mask * automask); add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -729,10 +725,6 @@ static void do_boundary_brush_slide_task(Object *ob, const Brush *brush, PBVHNod boundary->slide.directions[vd.index], boundary->edit_info[vd.index].strength_factor * disp * mask * automask * strength); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -774,10 +766,6 @@ static void do_boundary_brush_inflate_task(Object *ob, const Brush *brush, PBVHN orig_data.no, boundary->edit_info[vd.index].strength_factor * disp * mask * automask * strength); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -816,10 +804,6 @@ static void do_boundary_brush_grab_task(Object *ob, const Brush *brush, PBVHNode orig_data.co, ss->cache->grab_delta_symmetry, boundary->edit_info[vd.index].strength_factor * mask * automask * strength); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -869,10 +853,6 @@ static void do_boundary_brush_twist_task(Object *ob, const Brush *brush, PBVHNod boundary->twist.rotation_axis, angle * mask * automask * boundary->edit_info[vd.index].strength_factor); add_v3_v3(target_co, boundary->twist.pivot_position); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -924,10 +904,6 @@ static void do_boundary_brush_smooth_task(Object *ob, const Brush *brush, PBVHNo float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl( target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 3d01fd522e2..4d5af686764 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -297,10 +297,6 @@ static void do_draw_brush_task(Object *ob, const Brush *brush, const float *offs &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); } - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -388,10 +384,6 @@ static void do_fill_brush_task( &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -479,10 +471,6 @@ static void do_scrape_brush_task( &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -593,10 +581,6 @@ static void do_clay_thumb_brush_task(Object *ob, &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -729,10 +713,6 @@ static void do_flatten_brush_task( &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } } BKE_pbvh_vertex_iter_end; @@ -867,10 +847,6 @@ static void do_clay_brush_task( &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -984,10 +960,6 @@ static void do_clay_strips_brush_task(Object *ob, &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1174,10 +1146,6 @@ static void do_snake_hook_brush_task(Object *ob, auto_mask::factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], disp); } - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1253,10 +1221,6 @@ static void do_thumb_brush_task(Object *ob, const Brush *brush, const float *con &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1326,10 +1290,6 @@ static void do_rotate_brush_task(Object *ob, const Brush *brush, const float ang mul_v3_m3v3(proxy[vd.i], rot, vec); add_v3_v3(proxy[vd.i], ss->cache->location); sub_v3_v3(proxy[vd.i], orig_data.co); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1434,10 +1394,6 @@ static void do_layer_brush_task(Object *ob, Sculpt *sd, const Brush *brush, PBVH add_v3_v3v3(final_co, vd.co, vdisp); SCULPT_clip(sd, ss, vd.co, final_co); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1503,10 +1459,6 @@ static void do_inflate_brush_task(Object *ob, const Brush *brush, PBVHNode *node mul_v3_fl(val, fade * ss->cache->radius); mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1557,10 +1509,6 @@ static void do_nudge_brush_task(Object *ob, const Brush *brush, const float *con &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1648,10 +1596,6 @@ static void do_crease_brush_task(Object *ob, mul_v3_v3fl(val2, offset, fade); add_v3_v3v3(proxy[vd.i], val1, val2); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1761,10 +1705,6 @@ static void do_pinch_brush_task(Object *ob, project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal); } mul_v3_v3fl(proxy[vd.i], disp_center, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1867,10 +1807,6 @@ static void do_grab_brush_task(Object *ob, } mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1969,10 +1905,6 @@ static void do_elastic_deform_brush_task(Object *ob, auto_mask::factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], final_disp); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2044,10 +1976,6 @@ static void do_draw_sharp_brush_task(Object *ob, &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2151,10 +2079,6 @@ static void do_topology_slide_task(Object *ob, const Brush *brush, PBVHNode *nod SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); mul_v3_v3fl(proxy[vd.i], final_disp, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2288,9 +2212,6 @@ static void do_topology_relax_task(Object *ob, const Brush *brush, PBVHNode *nod &automask_data); smooth::relax_vertex(ss, &vd, fade * bstrength, false, vd.co); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2370,10 +2291,6 @@ static void do_displacement_eraser_brush_task(Object *ob, const Brush *brush, PB SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co); sub_v3_v3v3(disp, limit_co, vd.co); mul_v3_v3fl(proxy[vd.i], disp, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2479,10 +2396,6 @@ static void do_displacement_smear_brush_task(Object *ob, const Brush *brush, PBV float new_co[3]; add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp); interp_v3_v3v3(vd.co, vd.co, new_co, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -2598,10 +2511,6 @@ static void do_topology_rake_bmesh_task( madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 5341c60ced3..3465248ff2a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -783,10 +783,6 @@ static void do_cloth_brush_solve_simulation_task(Object *ob, copy_v3_fl(cloth_sim->acceleration[i], 0.0f); copy_v3_v3(vd.co, cloth_sim->pos[vd.index]); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 855bc76dda3..cb0f2cd97e4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -438,9 +438,6 @@ static void do_relax_face_sets_brush_task(Object *ob, &automask_data); smooth::relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -1388,7 +1385,6 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, for (int i = 0; i < totvert; i++) { if (fair_verts[i]) { interp_v3_v3v3(positions[i], orig_positions[i], positions[i], strength); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_index_to_vertex(ss->pbvh, i)); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index e692cea2b98..617094969d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -529,9 +529,6 @@ static void mesh_filter_task(Object *ob, add_v3_v3v3(final_pos, orig_co, disp); } copy_v3_v3(vd.co, final_pos); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc index 3a3d1cf4c46..05a4d5e0711 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc @@ -180,10 +180,6 @@ static void do_multiplane_scrape_brush_task(Object *ob, &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.cc b/source/blender/editors/sculpt_paint/sculpt_pose.cc index 485115a59bd..6266876ca5b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.cc +++ b/source/blender/editors/sculpt_paint/sculpt_pose.cc @@ -190,10 +190,6 @@ static void do_pose_brush_task(Object *ob, const Brush *brush, PBVHNode *node) float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); copy_v3_v3(target_co, final_pos); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index bed4cb6b1a1..0637f97f6b7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -234,10 +234,6 @@ static void do_enhance_details_brush_task(Object *ob, float disp[3]; madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); SCULPT_clip(sd, ss, vd.co, disp); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -384,9 +380,6 @@ static void smooth_position_node( sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } @@ -527,9 +520,6 @@ static void do_surface_smooth_brush_laplacian_task(Object *ob, const Brush *brus surface_smooth_laplacian_step( ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index 6d11e75d7b4..3eab4dba818 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -173,10 +173,6 @@ static void sculpt_transform_task(Object *ob, const float transform_mats[8][4][4 sub_v3_v3v3(disp, transformed_co, start_co); mul_v3_fl(disp, 1.0f - fade); add_v3_v3v3(vd.co, start_co, disp); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; @@ -243,10 +239,6 @@ static void sculpt_elastic_transform_task(Object *ob, mul_v3_fl(final_disp, 20.0f * (1.0f - fade)); copy_v3_v3(proxy[vd.i], final_disp); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index b14d5716216..968b5bc3a9d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -281,6 +281,7 @@ struct PartialUpdateData { bool changed_hide_vert; bool changed_mask; Span modified_grids; + Span modified_position_verts; Span modified_hidden_verts; Span modified_hidden_faces; Span modified_mask_verts; @@ -290,10 +291,15 @@ struct PartialUpdateData { static void update_modified_node_mesh(PBVHNode &node, PartialUpdateData &data) { - if (BKE_pbvh_node_has_vert_with_normal_update_tag(data.pbvh, &node)) { - BKE_pbvh_node_mark_update(&node); - } const Span verts = BKE_pbvh_node_get_vert_indices(&node); + if (!data.modified_position_verts.is_empty()) { + for (const int vert : verts) { + if (data.modified_position_verts[vert]) { + BKE_pbvh_node_mark_normals_update(&node); + break; + } + } + } if (!data.modified_mask_verts.is_empty()) { for (const int vert : verts) { if (data.modified_mask_verts[vert]) { @@ -407,7 +413,8 @@ static bool restore_deformed( return false; } -static bool restore_coords(bContext *C, Object *ob, Depsgraph *depsgraph, Node &unode) +static bool restore_coords( + bContext *C, Object *ob, Depsgraph *depsgraph, Node &unode, MutableSpan modified_verts) { SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; @@ -471,20 +478,20 @@ static bool restore_coords(bContext *C, Object *ob, Depsgraph *depsgraph, Node & if (ss->deform_modifiers_active) { for (const int i : index.index_range()) { restore_deformed(ss, unode, i, index[i], positions[index[i]]); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); + modified_verts[index[i]] = true; } } else { for (const int i : index.index_range()) { swap_v3_v3(positions[index[i]], unode.orig_position[i]); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); + modified_verts[index[i]] = true; } } } else { for (const int i : index.index_range()) { swap_v3_v3(positions[index[i]], unode.position[i]); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); + modified_verts[index[i]] = true; } } } @@ -916,6 +923,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt) /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep * track of other updates. In order to tell the corresponding PBVH nodes to update, keep track * of which elements were updated for specific layers. */ + Vector modified_verts_position; Vector modified_verts_hide; Vector modified_faces_hide; Vector modified_verts_mask; @@ -945,7 +953,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt) switch (unode->type) { case Type::Position: - if (restore_coords(C, ob, depsgraph, *unode)) { + if (restore_coords(C, ob, depsgraph, *unode, modified_verts_position)) { changed_position = true; } break; @@ -1024,6 +1032,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt) data.changed_mask = changed_mask; data.pbvh = ss->pbvh; data.modified_grids = modified_grids; + data.modified_hidden_verts = modified_verts_position; data.modified_hidden_verts = modified_verts_hide; data.modified_hidden_faces = modified_faces_hide; data.modified_mask_verts = modified_verts_mask;