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:
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user