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
This commit is contained in:
Hans Goudey
2024-01-08 18:49:26 +01:00
committed by Hans Goudey
parent cc13d58e53
commit aaa25bc882
15 changed files with 123 additions and 249 deletions

View File

@@ -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<int> 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);

View File

@@ -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<int> tri_faces = pbvh->corner_tri_faces;
pbvh->vert_bitmap = blender::Array<bool>(totvert, false);
Array<bool> 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<float3> positions,
const blender::OffsetIndices<int> faces,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> mask,
const Span<int> face_indices,
MutableSpan<float3> 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<float3> positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<int> face_indices,
MutableSpan<float3> 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<float3> positions,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const PBVH &pbvh,
const Span<const PBVHNode *> nodes,
MutableSpan<float3> face_normals)
{
threading::EnumerableThreadSpecific<Vector<int>> all_index_data;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
Vector<int> &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<int> vert_to_face_map,
static void normals_calc_verts_simple(const GroupedSpan<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<int> mask,
const Span<int> verts,
MutableSpan<float3> 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<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<int> verts,
MutableSpan<float3> 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<int> vert_to_face_map,
const Span<float3> face_normals,
const Span<PBVHNode *> nodes,
MutableSpan<float3> 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<PBVHNode *> nodes, Mesh &mesh)
static void update_normals_faces(PBVH &pbvh, Span<PBVHNode *> 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<float3> positions = pbvh.vert_positions;
const OffsetIndices faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
MutableSpan<bool> update_tags = pbvh.vert_bitmap;
VectorSet<int> faces_to_update;
VectorSet<int> 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<int> verts_to_update;
VectorSet<int> 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<float3> &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<PBVHNode *> 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<float3> &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<int> 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<float3> 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));
}
}

View File

@@ -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<bool> vert_bitmap;
#ifdef PERFCNTRS
int perf_modified;
#endif

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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));
}
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -281,6 +281,7 @@ struct PartialUpdateData {
bool changed_hide_vert;
bool changed_mask;
Span<bool> modified_grids;
Span<bool> modified_position_verts;
Span<bool> modified_hidden_verts;
Span<bool> modified_hidden_faces;
Span<bool> 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<int> 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<bool> 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<bool> modified_verts_position;
Vector<bool> modified_verts_hide;
Vector<bool> modified_faces_hide;
Vector<bool> 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;