Geometry Nodes: Use common utility for copying attribute data

Attribute copying often uses identical logic for copying selected
elements or copying with an index map. Instead of reimplementing
this in each file, use the common implementation in the array_utils
namespace. This makes the commonality more obvious, gives improved
performance (this implementation is multithreaded), reduces binary
size (I observed a 173KB reduction), and probably reduces compile time.
This commit is contained in:
Hans Goudey 2022-10-19 12:38:48 -05:00
parent e6902d19a0
commit a803dbe7ed
9 changed files with 81 additions and 159 deletions

View File

@ -9,6 +9,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_bounds.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_length_parameterize.hh"
@ -1111,21 +1112,11 @@ static void copy_between_buffers(const CPPType &type,
src_range.size());
}
template<typename T>
static void copy_with_map(const Span<T> src, const Span<int> map, MutableSpan<T> dst)
{
threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
dst[i] = src[map[i]];
}
});
}
static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
copy_with_map(src.typed<T>(), map, dst.typed<T>());
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
});
}

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_cpp_type_make.hh"
#include "BLI_rand.hh"
#include "BLI_task.hh"
@ -107,18 +108,6 @@ blender::Span<InstanceReference> Instances::references() const
return references_;
}
template<typename T>
static void copy_data_based_on_mask(Span<T> src, MutableSpan<T> dst, IndexMask mask)
{
BLI_assert(src.data() != dst.data());
using namespace blender;
threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
dst[i] = src[mask[i]];
}
});
}
void Instances::remove(const IndexMask mask)
{
using namespace blender;
@ -129,11 +118,14 @@ void Instances::remove(const IndexMask mask)
return;
}
const Span<int> old_handles = this->reference_handles();
Vector<int> new_handles(mask.size());
copy_data_based_on_mask<int>(this->reference_handles(), new_handles, mask);
array_utils::gather(old_handles, mask.indices(), new_handles.as_mutable_span());
reference_handles_ = std::move(new_handles);
const Span<float4x4> old_tansforms = this->transforms();
Vector<float4x4> new_transforms(mask.size());
copy_data_based_on_mask<float4x4>(this->transforms(), new_transforms, mask);
array_utils::gather(old_tansforms, mask.indices(), new_transforms.as_mutable_span());
transforms_ = std::move(new_transforms);
const bke::CustomDataAttributes &src_attributes = attributes_;
@ -150,11 +142,8 @@ void Instances::remove(const IndexMask mask)
GSpan src = *src_attributes.get_for_read(id);
dst_attributes.create(id, meta_data.data_type);
GMutableSpan dst = *dst_attributes.get_for_write(id);
array_utils::gather(src, mask.indices(), dst);
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
copy_data_based_on_mask<T>(src.typed<T>(), dst.typed<T>(), mask);
});
return true;
},
ATTR_DOMAIN_INSTANCE);

View File

@ -39,6 +39,11 @@ inline void copy(const Span<T> src,
*/
void gather(const GVArray &src, IndexMask indices, GMutableSpan dst, int64_t grain_size = 4096);
/**
* Fill the destination span by gathering indexed values from the `src` array.
*/
void gather(GSpan src, IndexMask indices, GMutableSpan dst, int64_t grain_size = 4096);
/**
* Fill the destination span by gathering indexed values from the `src` array.
*/

View File

@ -28,4 +28,9 @@ void gather(const GVArray &src,
});
}
void gather(const GSpan src, const IndexMask indices, GMutableSpan dst, const int64_t grain_size)
{
gather(GVArray::ForSpan(src), indices, dst, grain_size);
}
} // namespace blender::array_utils

View File

@ -4,6 +4,7 @@
#include "UI_resources.h"
#include "BLI_array.hh"
#include "BLI_array_utils.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@ -23,15 +24,9 @@ namespace blender::nodes::node_geo_delete_geometry_cc {
using blender::bke::CustomDataAttributes;
template<typename T>
static void copy_data_based_on_mask(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
{
for (const int i_out : mask.index_range()) {
r_data[i_out] = data[mask[i_out]];
}
}
template<typename T>
static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> index_map)
static void copy_data_based_on_map(const Span<T> src,
const Span<int> index_map,
MutableSpan<T> dst)
{
for (const int i_src : index_map.index_range()) {
const int i_dst = index_map[i_src];
@ -55,26 +50,17 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
if (!attribute) {
continue;
}
/* Only copy if it is on a domain we want. */
if (!domains.contains(attribute.domain)) {
continue;
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> span{attribute.varray.typed<T>()};
MutableSpan<T> out_span = result_attribute.span.typed<T>();
out_span.copy_from(span);
});
attribute.varray.materialize(result_attribute.span.data());
result_attribute.finish();
}
}
@ -95,26 +81,19 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
if (!attribute) {
continue;
}
/* Only copy if it is on a domain we want. */
if (domain != attribute.domain) {
continue;
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> span{attribute.varray.typed<T>()};
MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_mask(span, out_span, mask);
});
array_utils::gather(attribute.varray, mask, result_attribute.span);
result_attribute.finish();
}
}
@ -131,16 +110,13 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
if (!attribute) {
continue;
}
/* Only copy if it is on a domain we want. */
if (domain != attribute.domain) {
continue;
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
}
@ -149,7 +125,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
using T = decltype(dummy);
VArraySpan<T> span{attribute.varray.typed<T>()};
MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_map(span, out_span, index_map);
copy_data_based_on_map(span, index_map, out_span);
});
result_attribute.finish();
}

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_task.hh"
#include "DNA_mesh_types.h"
@ -105,18 +106,6 @@ static void copy_data_based_on_pairs(Span<T> data,
}
}
/* Copy using the map. */
template<typename T>
static void copy_data_based_on_new_to_old_map(Span<T> data,
MutableSpan<T> r_data,
const Span<int> new_to_old_map)
{
for (const int i : r_data.index_range()) {
const int old_i = new_to_old_map[i];
r_data[i] = data[old_i];
}
}
/**
* Transfers the attributes from the original mesh to the new mesh using the following logic:
* - If the attribute was on the face domain it is now on the point domain, and this is true
@ -168,7 +157,6 @@ static void transfer_attributes(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
@ -177,20 +165,24 @@ static void transfer_attributes(
using T = decltype(dummy);
VArraySpan<T> span{src_attribute.varray.typed<T>()};
MutableSpan<T> dst_span = dst_attribute.span.typed<T>();
if (src_attribute.domain == ATTR_DOMAIN_FACE) {
dst_span.take_front(span.size()).copy_from(span);
if (keep_boundaries) {
copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map);
}
}
else if (src_attribute.domain == ATTR_DOMAIN_POINT) {
copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries);
}
else if (src_attribute.domain == ATTR_DOMAIN_EDGE) {
copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_edges_map);
}
else {
copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map);
switch (src_attribute.domain) {
case ATTR_DOMAIN_POINT:
copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries);
break;
case ATTR_DOMAIN_EDGE:
array_utils::gather(span, new_to_old_edges_map, dst_span);
break;
case ATTR_DOMAIN_FACE:
dst_span.take_front(span.size()).copy_from(span);
if (keep_boundaries) {
copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map);
}
break;
case ATTR_DOMAIN_CORNER:
array_utils::gather(span, new_to_old_face_corners_map, dst_span);
break;
default:
BLI_assert_unreachable();
}
});
dst_attribute.finish();

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_map.hh"
#include "BLI_noise.hh"
#include "BLI_span.hh"
@ -105,16 +106,6 @@ static void threaded_slice_fill(Span<int> offsets,
});
}
template<typename T>
static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst)
{
threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
dst[i] = src[mapping[i]];
}
});
}
static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst)
{
for (const int i : src.index_range()) {
@ -440,17 +431,17 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_POINT:
array_utils::gather(src, vert_mapping, dst);
break;
case ATTR_DOMAIN_EDGE:
array_utils::gather(src, edge_mapping, dst);
break;
case ATTR_DOMAIN_FACE:
threaded_slice_fill<T>(offsets, selection, src, dst);
break;
case ATTR_DOMAIN_EDGE:
threaded_mapped_copy<T>(edge_mapping, src, dst);
break;
case ATTR_DOMAIN_POINT:
threaded_mapped_copy<T>(vert_mapping, src, dst);
break;
case ATTR_DOMAIN_CORNER:
threaded_mapped_copy<T>(loop_mapping, src, dst);
array_utils::gather(src, loop_mapping, dst);
break;
default:
break;
@ -653,7 +644,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
threaded_slice_fill<T>(offsets, selection, src, dst);
break;
case ATTR_DOMAIN_POINT:
threaded_mapped_copy<T>(point_mapping, src, dst);
array_utils::gather(src, point_mapping, dst);
break;
default:
break;

View File

@ -1,5 +1,6 @@
/* 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"
@ -175,24 +176,6 @@ static MPoly new_poly(const int loopstart, const int totloop)
return poly;
}
template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices)
{
BLI_assert(dst.size() == indices.size());
for (const int i : dst.index_range()) {
dst[i] = src[indices[i]];
}
}
template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask)
{
BLI_assert(dst.size() == mask.size());
threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
for (const int i : range) {
dst[i] = src[mask[i]];
}
});
}
/**
* \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every
* result point.
@ -265,26 +248,24 @@ static void extrude_mesh_vertices(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
break;
}
case ATTR_DOMAIN_EDGE: {
switch (attribute.domain) {
case ATTR_DOMAIN_POINT:
/* New vertices copy the attribute values from their source vertex. */
array_utils::gather(attribute.span, selection, attribute.span.slice(new_vert_range));
break;
case ATTR_DOMAIN_EDGE:
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
/* New edge values are mixed from of all the edges connected to the source vertex. */
copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) {
return vert_to_edge_map[selection[i]].as_span();
});
break;
}
default:
BLI_assert_unreachable();
}
});
});
break;
default:
BLI_assert_unreachable();
}
attribute.finish();
return true;
@ -524,13 +505,14 @@ static void extrude_mesh_edges(Mesh &mesh,
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
array_utils::gather(
data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
copy_with_mask(duplicate_data, data.as_span(), edge_selection);
array_utils::gather(data.as_span(), edge_selection, duplicate_data);
/* Edges connected to original vertices mix values of selected connected edges. */
MutableSpan<T> connect_data = data.slice(connect_edge_range);
@ -910,17 +892,18 @@ static void extrude_mesh_face_regions(Mesh &mesh,
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
array_utils::gather(
data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
MutableSpan<T> boundary_data = data.slice(boundary_edge_range);
copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices);
array_utils::gather(data.as_span(), boundary_edge_indices.as_span(), boundary_data);
/* Edges inside of face regions also just duplicate their source data. */
MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range);
copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices);
array_utils::gather(data.as_span(), new_inner_edge_indices.as_span(), new_inner_data);
/* Edges connected to original vertices mix values of selected connected edges. */
MutableSpan<T> connect_data = data.slice(connect_edge_range);
@ -932,8 +915,8 @@ static void extrude_mesh_face_regions(Mesh &mesh,
case ATTR_DOMAIN_FACE: {
/* New faces on the side of extrusions get the values from the corresponding selected
* face. */
copy_with_indices(
data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices);
array_utils::gather(
data.as_span(), edge_extruded_face_indices.as_span(), data.slice(side_poly_range));
break;
}
case ATTR_DOMAIN_CORNER: {

View File

@ -2,6 +2,7 @@
#include "DNA_collection_types.h"
#include "BLI_array_utils.hh"
#include "BLI_hash.h"
#include "BLI_task.hh"
@ -172,18 +173,7 @@ static void add_instances_from_component(
dst_attribute_opt = instance_attributes.get_for_write(attribute_id);
}
BLI_assert(dst_attribute_opt);
const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len);
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
using T = decltype(dummy);
VArray<T> src = src_attribute.typed<T>();
MutableSpan<T> dst = dst_attribute.typed<T>();
for (const int range_i : selection_range) {
const int i = selection[range_i];
dst[range_i] = src[i];
}
});
});
array_utils::gather(src_attribute, selection, dst_attribute_opt->slice(start_len, select_len));
}
}