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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user