Sculpt: Add function to push multiple undo nodes at once

Pushing multiple nodes at the same time helps to reduce the amount
of time spent waiting for threads to unlock while they manipulate the
nodes map, and equalizes the amount of work per thread, since we
can iterate over just the nodes that need data stored. I observed a
2.6% speedup in the benchmark file from #118145 (0.59s to 0.57s).
This commit is contained in:
Hans Goudey
2024-06-20 12:50:52 -04:00
parent c1c221a009
commit fd205d9bb9
3 changed files with 49 additions and 14 deletions

View File

@@ -3544,38 +3544,46 @@ static void sculpt_topology_update(const Scene & /*scene*/,
mul_m4_v3(ob.object_to_world().ptr(), location);
}
static void push_undo_nodes(Object &ob, const Brush &brush, PBVHNode *node)
static void push_undo_nodes(Object &ob, const Brush &brush, const Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
bool need_coords = ss.cache->supports_gravity;
if (brush.sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
BKE_pbvh_node_mark_update_face_sets(node);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_update_face_sets(node);
}
/* Draw face sets in smooth mode moves the vertices. */
if (ss.cache->alt_smooth) {
need_coords = true;
}
else {
undo::push_node(ob, node, undo::Type::FaceSet);
undo::push_nodes(ob, nodes, undo::Type::FaceSet);
}
}
else if (brush.sculpt_tool == SCULPT_TOOL_MASK) {
undo::push_node(ob, node, undo::Type::Mask);
BKE_pbvh_node_mark_update_mask(node);
undo::push_nodes(ob, nodes, undo::Type::Mask);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_update_mask(node);
}
}
else if (SCULPT_tool_is_paint(brush.sculpt_tool)) {
undo::push_node(ob, node, undo::Type::Color);
BKE_pbvh_node_mark_update_color(node);
undo::push_nodes(ob, nodes, undo::Type::Color);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_update_color(node);
}
}
else {
need_coords = true;
}
if (need_coords) {
undo::push_node(ob, node, undo::Type::Position);
BKE_pbvh_node_mark_positions_update(node);
undo::push_nodes(ob, nodes, undo::Type::Position);
for (PBVHNode *node : nodes) {
BKE_pbvh_node_mark_positions_update(node);
}
}
}
@@ -3685,11 +3693,7 @@ static void do_brush_action(const Scene &scene,
float location[3];
if (!use_pixels) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
push_undo_nodes(ob, brush, nodes[i]);
}
});
push_undo_nodes(ob, brush, nodes);
}
if (sculpt_brush_needs_normal(ss, sd, brush)) {

View File

@@ -1653,6 +1653,7 @@ namespace blender::ed::sculpt_paint::undo {
* This is only possible when building an undo step, in between #push_begin and #push_end.
*/
void push_node(const Object &object, const PBVHNode *node, undo::Type type);
void push_nodes(Object &object, Span<const PBVHNode *> nodes, undo::Type type);
/**
* Retrieve the undo data of a given type for the active undo step. For example, this is used to

View File

@@ -1587,6 +1587,36 @@ void push_node(const Object &object, const PBVHNode *node, Type type)
fill_node_data(object, node, type, *unode);
}
void push_nodes(Object &object, const Span<const PBVHNode *> nodes, const Type type)
{
SculptSession &ss = *object.sculpt;
if (ss.bm || ELEM(type, Type::DyntopoBegin, Type::DyntopoEnd)) {
for (const PBVHNode *node : nodes) {
bmesh_push(object, node, type);
}
return;
}
StepData *step_data = get_step_data();
Vector<std::pair<const PBVHNode *, Node *>, 32> nodes_to_fill;
for (const PBVHNode *node : nodes) {
bool newly_added;
Node *unode = ensure_node(*step_data, *node, type, newly_added);
if (newly_added) {
nodes_to_fill.append({node, unode});
}
}
ss.needs_flush_to_id = 1;
threading::parallel_for(nodes_to_fill.index_range(), 1, [&](const IndexRange range) {
for (const auto &[node, unode] : nodes_to_fill.as_span().slice(range)) {
fill_node_data(object, node, type, *unode);
}
});
}
static void save_active_attribute(Object &object, SculptAttrRef *attr)
{
Mesh *mesh = BKE_object_get_original_mesh(&object);