Geometry Nodes: Avoid extra attribute copy in Sort Elements node
Implicit sharing dramatically reduces the cost of coping geometry which is extremely helpful for read-only and slight modification. But case of Sort Elements node implies a total rewrite of whole domain, so implicit sharing only adds redundant cost for write access. This fix uses kernel API to create domains without attributes, manually forcing implicit sharing for offset indices and uses kernel attribute copy functions to force sharing for all attributes when this is possible. In all other cases new attributes are created from scratch and copied as re-ordered only once. In attached example file shown the difference depend on the number of attributes on the mesh, benchmark result: | Attributes | Before | After | | ---------- | ------ | ----- | | 1 | 2 ms | 1.5 ms | | 5 | 5 ms | 4 ms | | 10 | 9 ms | 6 ms | | 50 | 45 ms | 30 ms | | 100 | 89 ms | 59 ms | Pull Request: https://projects.blender.org/blender/blender/pulls/117910
This commit is contained in:
committed by
Hans Goudey
parent
9945563fac
commit
9d13e39585
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_filters.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -46,6 +47,7 @@ static void reorder_attributes_group_to_group(const bke::AttributeAccessor src_a
|
||||
const OffsetIndices<int> src_offsets,
|
||||
const OffsetIndices<int> dst_offsets,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
bke::MutableAttributeAccessor dst_attributes)
|
||||
{
|
||||
src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
@@ -55,6 +57,9 @@ static void reorder_attributes_group_to_group(const bke::AttributeAccessor src_a
|
||||
if (iter.data_type == CD_PROP_STRING) {
|
||||
return;
|
||||
}
|
||||
if (attribute_filter.allow_skip(iter.name)) {
|
||||
return;
|
||||
}
|
||||
const GVArray src = *iter.get(domain);
|
||||
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
||||
iter.name, domain, iter.data_type);
|
||||
@@ -84,75 +89,160 @@ static Array<int> invert_permutation(const Span<int> permutation)
|
||||
return data;
|
||||
}
|
||||
|
||||
static void reorder_mesh_verts_exec(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
Mesh &dst_mesh)
|
||||
static void copy_and_reorder_mesh_verts(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
Mesh &dst_mesh)
|
||||
{
|
||||
bke::gather_attributes(src_mesh.attributes(),
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
|
||||
|
||||
bke::gather_attributes(src_attributes,
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_mesh.attributes_for_write());
|
||||
dst_attributes);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Edge,
|
||||
bke::AttrDomain::Edge,
|
||||
bke::attribute_filter_with_skip_ref(attribute_filter, {".edge_verts"}),
|
||||
dst_attributes);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Face,
|
||||
bke::AttrDomain::Face,
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
|
||||
implicit_sharing::free_shared_data(&dst_mesh.face_offset_indices,
|
||||
&dst_mesh.runtime->face_offsets_sharing_info);
|
||||
implicit_sharing::copy_shared_pointer(src_mesh.face_offset_indices,
|
||||
src_mesh.runtime->face_offsets_sharing_info,
|
||||
&dst_mesh.face_offset_indices,
|
||||
&dst_mesh.runtime->face_offsets_sharing_info);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Corner,
|
||||
bke::AttrDomain::Corner,
|
||||
bke::attribute_filter_with_skip_ref(attribute_filter, {".corner_vert"}),
|
||||
dst_attributes);
|
||||
|
||||
const Array<int> new_by_old_map = invert_permutation(old_by_new_map);
|
||||
|
||||
dst_attributes.add<int2>(".edge_verts", bke::AttrDomain::Edge, bke::AttributeInitConstruct());
|
||||
array_utils::gather(new_by_old_map.as_span(),
|
||||
dst_mesh.edges().cast<int>(),
|
||||
src_mesh.edges().cast<int>(),
|
||||
dst_mesh.edges_for_write().cast<int>());
|
||||
|
||||
dst_attributes.add<int>(".corner_vert", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
|
||||
array_utils::gather(
|
||||
new_by_old_map.as_span(), dst_mesh.corner_verts(), dst_mesh.corner_verts_for_write());
|
||||
new_by_old_map.as_span(), src_mesh.corner_verts(), dst_mesh.corner_verts_for_write());
|
||||
}
|
||||
|
||||
static void reorder_mesh_edges_exec(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
Mesh &dst_mesh)
|
||||
static void copy_and_reorder_mesh_edges(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
Mesh &dst_mesh)
|
||||
{
|
||||
bke::gather_attributes(src_mesh.attributes(),
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
|
||||
bke::gather_attributes(src_attributes,
|
||||
bke::AttrDomain::Edge,
|
||||
bke::AttrDomain::Edge,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_mesh.attributes_for_write());
|
||||
dst_attributes);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Face,
|
||||
bke::AttrDomain::Face,
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
|
||||
implicit_sharing::free_shared_data(&dst_mesh.face_offset_indices,
|
||||
&dst_mesh.runtime->face_offsets_sharing_info);
|
||||
implicit_sharing::copy_shared_pointer(src_mesh.face_offset_indices,
|
||||
src_mesh.runtime->face_offsets_sharing_info,
|
||||
&dst_mesh.face_offset_indices,
|
||||
&dst_mesh.runtime->face_offsets_sharing_info);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Corner,
|
||||
bke::AttrDomain::Corner,
|
||||
bke::attribute_filter_with_skip_ref(attribute_filter, {".corner_edge"}),
|
||||
dst_attributes);
|
||||
|
||||
const Array<int> new_by_old_map = invert_permutation(old_by_new_map);
|
||||
|
||||
dst_attributes.add<int>(".corner_edge", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
|
||||
array_utils::gather(
|
||||
new_by_old_map.as_span(), dst_mesh.corner_edges(), dst_mesh.corner_edges_for_write());
|
||||
new_by_old_map.as_span(), src_mesh.corner_edges(), dst_mesh.corner_edges_for_write());
|
||||
}
|
||||
|
||||
static void reorder_mesh_faces_exec(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
Mesh &dst_mesh)
|
||||
static void copy_and_reorder_mesh_faces(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
Mesh &dst_mesh)
|
||||
{
|
||||
bke::gather_attributes(src_mesh.attributes(),
|
||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
|
||||
bke::copy_attributes(src_attributes,
|
||||
bke::AttrDomain::Edge,
|
||||
bke::AttrDomain::Edge,
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
|
||||
bke::gather_attributes(src_attributes,
|
||||
bke::AttrDomain::Face,
|
||||
bke::AttrDomain::Face,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_mesh.attributes_for_write());
|
||||
dst_attributes);
|
||||
|
||||
const Span<int> old_offsets = src_mesh.face_offsets();
|
||||
MutableSpan<int> new_offsets = dst_mesh.face_offsets_for_write();
|
||||
offset_indices::gather_group_sizes(old_offsets, old_by_new_map, new_offsets);
|
||||
offset_indices::accumulate_counts_to_offsets(new_offsets);
|
||||
reorder_attributes_group_to_group(src_mesh.attributes(),
|
||||
reorder_attributes_group_to_group(src_attributes,
|
||||
bke::AttrDomain::Corner,
|
||||
old_offsets,
|
||||
new_offsets.as_span(),
|
||||
old_by_new_map,
|
||||
dst_mesh.attributes_for_write());
|
||||
attribute_filter,
|
||||
dst_attributes);
|
||||
}
|
||||
|
||||
static void reorder_mesh_exec(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttrDomain domain,
|
||||
Mesh &dst_mesh)
|
||||
static void copy_and_reorder_mesh(const Mesh &src_mesh,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttrDomain domain,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
Mesh &dst_mesh)
|
||||
{
|
||||
switch (domain) {
|
||||
case bke::AttrDomain::Point:
|
||||
reorder_mesh_verts_exec(src_mesh, old_by_new_map, dst_mesh);
|
||||
copy_and_reorder_mesh_verts(src_mesh, old_by_new_map, attribute_filter, dst_mesh);
|
||||
break;
|
||||
case bke::AttrDomain::Edge:
|
||||
reorder_mesh_edges_exec(src_mesh, old_by_new_map, dst_mesh);
|
||||
copy_and_reorder_mesh_edges(src_mesh, old_by_new_map, attribute_filter, dst_mesh);
|
||||
break;
|
||||
case bke::AttrDomain::Face:
|
||||
reorder_mesh_faces_exec(src_mesh, old_by_new_map, dst_mesh);
|
||||
copy_and_reorder_mesh_faces(src_mesh, old_by_new_map, attribute_filter, dst_mesh);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -161,28 +251,30 @@ static void reorder_mesh_exec(const Mesh &src_mesh,
|
||||
dst_mesh.tag_topology_changed();
|
||||
}
|
||||
|
||||
static void reorder_points_exec(const PointCloud &src_pointcloud,
|
||||
const Span<int> old_by_new_map,
|
||||
PointCloud &dst_pointcloud)
|
||||
static void copy_and_reorder_points(const PointCloud &src_pointcloud,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
PointCloud &dst_pointcloud)
|
||||
{
|
||||
bke::gather_attributes(src_pointcloud.attributes(),
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_pointcloud.attributes_for_write());
|
||||
dst_pointcloud.tag_positions_changed();
|
||||
dst_pointcloud.tag_radii_changed();
|
||||
}
|
||||
|
||||
static void reorder_curves_exec(const bke::CurvesGeometry &src_curves,
|
||||
const Span<int> old_by_new_map,
|
||||
bke::CurvesGeometry &dst_curves)
|
||||
static void copy_and_reorder_curves(const bke::CurvesGeometry &src_curves,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
bke::CurvesGeometry &dst_curves)
|
||||
{
|
||||
bke::gather_attributes(src_curves.attributes(),
|
||||
bke::AttrDomain::Curve,
|
||||
bke::AttrDomain::Curve,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_curves.attributes_for_write());
|
||||
|
||||
@@ -196,42 +288,33 @@ static void reorder_curves_exec(const bke::CurvesGeometry &src_curves,
|
||||
old_offsets,
|
||||
new_offsets.as_span(),
|
||||
old_by_new_map,
|
||||
attribute_filter,
|
||||
dst_curves.attributes_for_write());
|
||||
dst_curves.tag_topology_changed();
|
||||
}
|
||||
|
||||
static void reorder_instaces_exec(const bke::Instances &src_instances,
|
||||
const Span<int> old_by_new_map,
|
||||
bke::Instances &dst_instances)
|
||||
static void copy_and_reorder_instaces(const bke::Instances &src_instances,
|
||||
const Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter,
|
||||
bke::Instances &dst_instances)
|
||||
{
|
||||
dst_instances.resize(src_instances.instances_num());
|
||||
|
||||
bke::gather_attributes(src_instances.attributes(),
|
||||
bke::AttrDomain::Instance,
|
||||
bke::AttrDomain::Instance,
|
||||
{},
|
||||
attribute_filter,
|
||||
old_by_new_map,
|
||||
dst_instances.attributes_for_write());
|
||||
}
|
||||
|
||||
static void clean_unused_attributes(const bke::AttributeFilter &attribute_filter,
|
||||
bke::MutableAttributeAccessor attributes)
|
||||
{
|
||||
Vector<std::string> unused_ids;
|
||||
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
if (!bke::attribute_name_is_anonymous(iter.name)) {
|
||||
return;
|
||||
}
|
||||
if (iter.data_type == CD_PROP_STRING) {
|
||||
return;
|
||||
}
|
||||
if (!attribute_filter.allow_skip(iter.name)) {
|
||||
return;
|
||||
}
|
||||
unused_ids.append(iter.name);
|
||||
});
|
||||
|
||||
for (const std::string &unused_id : unused_ids) {
|
||||
attributes.remove(unused_id);
|
||||
for (const bke::InstanceReference &reference : src_instances.references()) {
|
||||
dst_instances.add_reference(reference);
|
||||
}
|
||||
BLI_assert(src_instances.references() == dst_instances.references());
|
||||
|
||||
const Span<float4x4> old_transforms = src_instances.transforms();
|
||||
MutableSpan<float4x4> new_transforms = dst_instances.transforms_for_write();
|
||||
array_utils::gather(old_transforms, old_by_new_map, new_transforms);
|
||||
}
|
||||
|
||||
Mesh *reorder_mesh(const Mesh &src_mesh,
|
||||
@@ -239,9 +322,10 @@ Mesh *reorder_mesh(const Mesh &src_mesh,
|
||||
bke::AttrDomain domain,
|
||||
const bke::AttributeFilter &attribute_filter)
|
||||
{
|
||||
Mesh *dst_mesh = BKE_mesh_copy_for_eval(src_mesh);
|
||||
clean_unused_attributes(attribute_filter, dst_mesh->attributes_for_write());
|
||||
reorder_mesh_exec(src_mesh, old_by_new_map, domain, *dst_mesh);
|
||||
Mesh *dst_mesh = bke::mesh_new_no_attributes(
|
||||
src_mesh.verts_num, src_mesh.edges_num, src_mesh.faces_num, src_mesh.corners_num);
|
||||
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
||||
copy_and_reorder_mesh(src_mesh, old_by_new_map, domain, attribute_filter, *dst_mesh);
|
||||
return dst_mesh;
|
||||
}
|
||||
|
||||
@@ -249,9 +333,8 @@ PointCloud *reorder_points(const PointCloud &src_pointcloud,
|
||||
Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter)
|
||||
{
|
||||
PointCloud *dst_pointcloud = BKE_pointcloud_copy_for_eval(&src_pointcloud);
|
||||
clean_unused_attributes(attribute_filter, dst_pointcloud->attributes_for_write());
|
||||
reorder_points_exec(src_pointcloud, old_by_new_map, *dst_pointcloud);
|
||||
PointCloud *dst_pointcloud = bke::pointcloud_new_no_attributes(src_pointcloud.totpoint);
|
||||
copy_and_reorder_points(src_pointcloud, old_by_new_map, attribute_filter, *dst_pointcloud);
|
||||
return dst_pointcloud;
|
||||
}
|
||||
|
||||
@@ -259,9 +342,9 @@ bke::CurvesGeometry reorder_curves_geometry(const bke::CurvesGeometry &src_curve
|
||||
Span<int> old_by_new_map,
|
||||
const bke::AttributeFilter &attribute_filter)
|
||||
{
|
||||
bke::CurvesGeometry dst_curves = bke::CurvesGeometry(src_curves);
|
||||
clean_unused_attributes(attribute_filter, dst_curves.attributes_for_write());
|
||||
reorder_curves_exec(src_curves, old_by_new_map, dst_curves);
|
||||
bke::CurvesGeometry dst_curves = bke::curves_new_no_attributes(src_curves.points_num(),
|
||||
src_curves.curves_num());
|
||||
copy_and_reorder_curves(src_curves, old_by_new_map, attribute_filter, dst_curves);
|
||||
return dst_curves;
|
||||
}
|
||||
|
||||
@@ -270,7 +353,7 @@ Curves *reorder_curves(const Curves &src_curves,
|
||||
const bke::AttributeFilter &attribute_filter)
|
||||
{
|
||||
const bke::CurvesGeometry src_curve_geometry = src_curves.geometry.wrap();
|
||||
Curves *dst_curves = BKE_curves_copy_for_eval(&src_curves);
|
||||
Curves *dst_curves = bke::curves_new_nomain(0, 0);
|
||||
dst_curves->geometry.wrap() = reorder_curves_geometry(
|
||||
src_curve_geometry, old_by_new_map, attribute_filter);
|
||||
return dst_curves;
|
||||
@@ -281,8 +364,7 @@ bke::Instances *reorder_instaces(const bke::Instances &src_instances,
|
||||
const bke::AttributeFilter &attribute_filter)
|
||||
{
|
||||
bke::Instances *dst_instances = new bke::Instances(src_instances);
|
||||
clean_unused_attributes(attribute_filter, dst_instances->attributes_for_write());
|
||||
reorder_instaces_exec(src_instances, old_by_new_map, *dst_instances);
|
||||
copy_and_reorder_instaces(src_instances, old_by_new_map, attribute_filter, *dst_instances);
|
||||
return dst_instances;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user