Geometry Nodes: Fill new quad poly offsets in parallel

Add an offset indices utility to do fill constant size new offsets in
parallel, which was already done in the duplicate elements node.

For example, filling poly offsets for a new part of a mesh that is only
quads. In the extrude node this was single-threaded before, so the
new poly offsets is about 10x faster, saving about 10 out of 157 ms
when extruding 2 million faces.
This commit is contained in:
Hans Goudey 2023-07-05 15:39:27 -04:00
parent 4c6653274c
commit 2b4666b17b
5 changed files with 22 additions and 20 deletions

View File

@ -125,8 +125,7 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
MutableSpan<int> poly_offsets = mesh->poly_offsets_for_write();
MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
poly_offsets.fill(4);
blender::offset_indices::accumulate_counts_to_offsets(poly_offsets);
blender::offset_indices::fill_constant_group_size(4, 0, poly_offsets);
mesh->vert_positions_for_write().copy_from(
Span(reinterpret_cast<float3 *>(qrd.out_verts), qrd.out_totverts));
@ -230,10 +229,11 @@ static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set
MutableSpan<int> poly_offsets = mesh->poly_offsets_for_write();
MutableSpan<int> mesh_corner_verts = mesh->corner_verts_for_write();
const int triangle_loop_start = quads.size() * 4;
if (!poly_offsets.is_empty()) {
poly_offsets.take_front(quads.size()).fill(4);
poly_offsets.drop_front(quads.size()).fill(3);
blender::offset_indices::accumulate_counts_to_offsets(poly_offsets);
blender::offset_indices::fill_constant_group_size(4, 0, poly_offsets.take_front(quads.size()));
blender::offset_indices::fill_constant_group_size(
3, triangle_loop_start, poly_offsets.drop_front(quads.size()));
}
for (const int i : vert_positions.index_range()) {
@ -248,7 +248,6 @@ static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set
mesh_corner_verts[loopstart + 3] = quads[i][1];
}
const int triangle_loop_start = quads.size() * 4;
for (const int i : IndexRange(tris.size())) {
const int loopstart = triangle_loop_start + i * 3;
mesh_corner_verts[loopstart] = tris[i][2];

View File

@ -140,6 +140,9 @@ template<typename T> struct GroupedSpan {
OffsetIndices<int> accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets,
int start_offset = 0);
/** Create offsets where every group has the same size. */
void fill_constant_group_size(int size, int start_offset, MutableSpan<int> offsets);
/** Copy the number of indices in every group in the mask to the corresponding index. */
void copy_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, MutableSpan<int> sizes);

View File

@ -22,6 +22,15 @@ OffsetIndices<int> accumulate_counts_to_offsets(MutableSpan<int> counts_to_offse
return OffsetIndices<int>(counts_to_offsets);
}
void fill_constant_group_size(const int size, const int start_offset, MutableSpan<int> offsets)
{
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t i : range) {
offsets[i] = size * i + start_offset;
}
});
}
void copy_group_sizes(const OffsetIndices<int> offsets,
const IndexMask &mask,
MutableSpan<int> sizes)

View File

@ -68,14 +68,8 @@ static OffsetIndices<int> accumulate_counts_to_offsets(const IndexMask &selectio
Array<int> &r_offset_data)
{
r_offset_data.reinitialize(selection.size() + 1);
if (counts.is_single()) {
const int count = counts.get_internal_single();
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t i : range) {
r_offset_data[i] = count * i;
}
});
r_offset_data.last() = count * selection.size();
if (const std::optional<int> count = counts.get_if_single()) {
offset_indices::fill_constant_group_size(*count, 0, r_offset_data);
}
else {
array_utils::gather(counts, selection, r_offset_data.as_mutable_span().drop_back(1), 1024);

View File

@ -487,8 +487,7 @@ static void extrude_mesh_edges(Mesh &mesh,
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
MutableSpan<int> new_corner_edges = corner_edges.slice(new_loop_range);
new_poly_offsets.fill(4);
offset_indices::accumulate_counts_to_offsets(new_poly_offsets, orig_loop_size);
offset_indices::fill_constant_group_size(4, orig_loop_size, new_poly_offsets);
const OffsetIndices polys = mesh.polys();
for (const int i : connect_edges.index_range()) {
@ -848,8 +847,7 @@ static void extrude_mesh_face_regions(Mesh &mesh,
/* Initialize the new side polygons. */
if (!new_poly_offsets.is_empty()) {
new_poly_offsets.fill(4);
offset_indices::accumulate_counts_to_offsets(new_poly_offsets, orig_loop_size);
offset_indices::fill_constant_group_size(4, orig_loop_size, new_poly_offsets);
}
const OffsetIndices polys = mesh.polys();
@ -1169,8 +1167,7 @@ static void extrude_individual_mesh_faces(
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
new_poly_offsets.fill(4);
offset_indices::accumulate_counts_to_offsets(new_poly_offsets, orig_loop_size);
offset_indices::fill_constant_group_size(4, orig_loop_size, new_poly_offsets);
const OffsetIndices polys = mesh.polys();
/* For every selected polygon, change it to use the new extruded vertices and the duplicate