Geometry Nodes: Improve extrude node vertex group performance

Add separate functions that deal with the vertex domain and copy vertex
groups without using the attribute API which has a large overhead when
abstracting the access of many vertex groups.

In a 1m vertex mesh with 20 vertex groups, I observed an improvement
in the node's runtime from 399 ms to 64 ms.

Also resolves #117553. That was an error when adding weight data to a
mesh without any weight data would invalidate custom data layers. That
is solved more simply now by just doing nothing in that case.
This commit is contained in:
Hans Goudey
2024-01-29 21:54:37 -05:00
parent 67cc9da7ba
commit 6aaa74cda9
5 changed files with 103 additions and 33 deletions

View File

@@ -298,4 +298,9 @@ VMutableArray<float> varray_for_mutable_deform_verts(MutableSpan<MDeformVert> dv
int defgroup_index);
void remove_defgroup_index(MutableSpan<MDeformVert> dverts, int defgroup_index);
void gather_deform_verts(Span<MDeformVert> src, Span<int> indices, MutableSpan<MDeformVert> dst);
void gather_deform_verts(Span<MDeformVert> src,
const IndexMask &indices,
MutableSpan<MDeformVert> dst);
} // namespace blender::bke

View File

@@ -1768,6 +1768,30 @@ void remove_defgroup_index(MutableSpan<MDeformVert> dverts, const int defgroup_i
});
}
void gather_deform_verts(const Span<MDeformVert> src,
const Span<int> indices,
MutableSpan<MDeformVert> dst)
{
threading::parallel_for(indices.index_range(), 512, [&](const IndexRange range) {
for (const int dst_i : range) {
const int src_i = indices[dst_i];
dst[dst_i].dw = static_cast<MDeformWeight *>(MEM_dupallocN(src[src_i].dw));
dst[dst_i].totweight = src[src_i].totweight;
dst[dst_i].flag = src[src_i].flag;
}
});
}
void gather_deform_verts(const Span<MDeformVert> src,
const IndexMask &indices,
MutableSpan<MDeformVert> dst)
{
indices.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
dst[dst_i].dw = static_cast<MDeformWeight *>(MEM_dupallocN(src[src_i].dw));
dst[dst_i].totweight = src[src_i].totweight;
dst[dst_i].flag = src[src_i].flag;
});
}
} // namespace blender::bke
/** \} */

View File

@@ -368,8 +368,7 @@ class CurvesVertexGroupsAttributeProvider final : public DynamicAttributesProvid
}
const Span<MDeformVert> dverts = curves->deform_verts();
if (dverts.is_empty()) {
static const float default_value = 0.0f;
return {VArray<float>::ForSingle(default_value, curves->points_num()), AttrDomain::Point};
return {VArray<float>::ForSingle(0.0f, curves->points_num()), AttrDomain::Point};
}
return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
}

View File

@@ -2,7 +2,6 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BLI_enumerable_thread_specific.hh"
@@ -11,6 +10,7 @@
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
#include "BKE_deform.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_mesh.hh"
@@ -124,26 +124,17 @@ static void gather_vert_attributes(const Mesh &mesh_src,
}
const Span<MDeformVert> src = mesh_src.deform_verts();
MutableSpan<MDeformVert> dst = mesh_dst.deform_verts_for_write();
threading::parallel_invoke(
src.size() > 1024,
[&]() {
if (!src.is_empty() && !dst.is_empty()) {
vert_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
dst[dst_i].dw = static_cast<MDeformWeight *>(MEM_dupallocN(src[src_i].dw));
dst[dst_i].totweight = src[src_i].totweight;
dst[dst_i].flag = src[src_i].flag;
});
}
},
[&]() {
bke::gather_attributes(mesh_src.attributes(),
bke::AttrDomain::Point,
propagation_info,
vertex_group_names,
vert_mask,
mesh_dst.attributes_for_write());
});
if (!vertex_group_names.is_empty() && !src.is_empty()) {
MutableSpan<MDeformVert> dst = mesh_dst.deform_verts_for_write();
bke::gather_deform_verts(src, vert_mask, dst);
}
bke::gather_attributes(mesh_src.attributes(),
bke::AttrDomain::Point,
propagation_info,
vertex_group_names,
vert_mask,
mesh_dst.attributes_for_write());
}
std::optional<Mesh *> mesh_copy_selection(

View File

@@ -3,14 +3,15 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_disjoint_set.hh"
#include "BLI_task.hh"
#include "BLI_vector_set.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_customdata.hh"
#include "BKE_deform.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.hh"
#include "BKE_mesh_runtime.hh"
@@ -269,7 +270,7 @@ using IDsByDomain = std::array<Vector<AttributeIDRef>, ATTR_DOMAIN_NUM>;
static IDsByDomain attribute_ids_by_domain(const AttributeAccessor attributes,
const Set<StringRef> &skip)
{
IDsByDomain map;
IDsByDomain ids_by_domain;
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (meta_data.data_type == CD_PROP_STRING) {
return true;
@@ -277,10 +278,10 @@ static IDsByDomain attribute_ids_by_domain(const AttributeAccessor attributes,
if (skip.contains(id.name())) {
return true;
}
map[int(meta_data.domain)].append(id);
ids_by_domain[int(meta_data.domain)].append(id);
return true;
});
return map;
return ids_by_domain;
}
static void gather_attributes(MutableAttributeAccessor attributes,
@@ -307,6 +308,56 @@ static void gather_attributes(MutableAttributeAccessor attributes,
}
}
static void gather_vert_attributes(Mesh &mesh,
const Span<AttributeIDRef> ids,
const Span<int> indices,
const IndexRange new_range)
{
Set<StringRef> vertex_group_names;
LISTBASE_FOREACH (bDeformGroup *, group, &mesh.vertex_group_names) {
vertex_group_names.add(group->name);
}
if (!vertex_group_names.is_empty() && !mesh.deform_verts().is_empty()) {
MutableSpan<MDeformVert> dverts = mesh.deform_verts_for_write();
bke::gather_deform_verts(dverts, indices, dverts.slice(new_range));
}
MutableAttributeAccessor attributes = mesh.attributes_for_write();
for (const AttributeIDRef &id : ids) {
if (!vertex_group_names.contains(id.name())) {
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
bke::attribute_math::gather(attribute.span, indices, attribute.span.slice(new_range));
attribute.finish();
}
}
}
static void gather_vert_attributes(Mesh &mesh,
const Span<AttributeIDRef> ids,
const IndexMask &indices,
const IndexRange new_range)
{
Set<StringRef> vertex_group_names;
LISTBASE_FOREACH (bDeformGroup *, group, &mesh.vertex_group_names) {
vertex_group_names.add(group->name);
}
if (!vertex_group_names.is_empty() && !mesh.deform_verts().is_empty()) {
MutableSpan<MDeformVert> dverts = mesh.deform_verts_for_write();
bke::gather_deform_verts(dverts, indices, dverts.slice(new_range));
}
MutableAttributeAccessor attributes = mesh.attributes_for_write();
for (const AttributeIDRef &id : ids) {
if (!vertex_group_names.contains(id.name())) {
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
array_utils::gather(attribute.span, indices, attribute.span.slice(new_range));
attribute.finish();
}
}
}
static void extrude_mesh_vertices(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
@@ -357,7 +408,7 @@ static void extrude_mesh_vertices(Mesh &mesh,
});
/* New vertices copy the attribute values from their source vertex. */
gather_attributes(attributes, ids_by_domain[int(AttrDomain::Point)], selection, new_vert_range);
gather_vert_attributes(mesh, ids_by_domain[int(AttrDomain::Point)], selection, new_vert_range);
/* New edge values are mixed from of all the edges connected to the source vertex. */
for (const AttributeIDRef &id : ids_by_domain[int(AttrDomain::Edge)]) {
@@ -631,7 +682,7 @@ static void extrude_mesh_edges(Mesh &mesh,
});
/* New vertices copy the attribute values from their source vertex. */
gather_attributes(attributes, ids_by_domain[int(AttrDomain::Point)], new_verts, new_vert_range);
gather_vert_attributes(mesh, ids_by_domain[int(AttrDomain::Point)], new_verts, new_vert_range);
/* Edges parallel to original edges copy the edge attributes from the original edges. */
gather_attributes(
@@ -1006,8 +1057,8 @@ static void extrude_mesh_face_regions(Mesh &mesh,
}
/* New vertices copy the attributes from their original vertices. */
gather_attributes(
attributes, ids_by_domain[int(AttrDomain::Point)], new_vert_indices, new_vert_range);
gather_vert_attributes(
mesh, ids_by_domain[int(AttrDomain::Point)], new_vert_indices, new_vert_range);
/* New faces on the side of extrusions get the values from the corresponding selected face. */
gather_attributes(attributes,
@@ -1276,8 +1327,8 @@ static void extrude_individual_mesh_faces(
});
/* New vertices copy the attributes from their original vertices. */
gather_attributes(
attributes, ids_by_domain[int(AttrDomain::Point)], new_vert_indices, new_vert_range);
gather_vert_attributes(
mesh, ids_by_domain[int(AttrDomain::Point)], new_vert_indices, new_vert_range);
/* The data for the duplicate edge is simply a copy of the original edge's data. */
gather_attributes(attributes,