diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 379b7b99e8b..a3112e7d797 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -919,4 +919,10 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const return *anonymous_id_; } +void gather_attributes(AttributeAccessor src_attributes, + eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, + const IndexMask &selection, + MutableAttributeAccessor dst_attributes); + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 814932772ff..1763b681185 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -994,4 +994,34 @@ Vector retrieve_attributes_for_transfer( /** \} */ +void gather_attributes(const AttributeAccessor src_attributes, + const eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, + const IndexMask &selection, + MutableAttributeAccessor dst_attributes) +{ + const int src_size = src_attributes.domain_size(domain); + src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != domain) { + return true; + } + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { + return true; + } + const bke::GAttributeReader src = src_attributes.lookup(id, domain); + if (selection.size() == src_size && src.sharing_info && src.varray.is_span()) { + const bke::AttributeInitShared init(src.varray.get_internal_span().data(), + *src.sharing_info); + dst_attributes.add(id, domain, meta_data.data_type, init); + } + else { + bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, domain, meta_data.data_type); + array_utils::gather(src.varray, selection, dst.span); + dst.finish(); + } + return true; + }); +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 3d8f82f912a..f1f0ca0eec6 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -15,6 +15,7 @@ #include "BLI_length_parameterize.hh" #include "BLI_math_matrix.hh" #include "BLI_math_rotation_legacy.hh" +#include "BLI_multi_value_map.hh" #include "BLI_task.hh" #include "BLO_read_write.h" @@ -1108,113 +1109,55 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const return true; } -static void copy_construct_data(const GSpan src, GMutableSpan dst) -{ - BLI_assert(src.size() == dst.size()); - src.type().copy_construct_n(src.data(), dst.data(), src.size()); -} - static CurvesGeometry copy_with_removed_points( const CurvesGeometry &curves, const IndexMask &points_to_delete, const AnonymousAttributePropagationInfo &propagation_info) { - /* Use a map from points to curves to facilitate using an #IndexMask input. */ + IndexMaskMemory memory; + const IndexMask points_to_copy = points_to_delete.complement(curves.points_range(), memory); + const Array point_to_curve_map = curves.point_to_curve_map(); - - const Vector copy_point_ranges = points_to_delete.to_ranges_invert( - curves.points_range()); - - /* For every range of points to copy, find the offset in the result curves point layers. */ - int new_point_count = 0; - Array copy_point_range_dst_offsets(copy_point_ranges.size()); - for (const int i : copy_point_ranges.index_range()) { - copy_point_range_dst_offsets[i] = new_point_count; - new_point_count += copy_point_ranges[i].size(); - } - BLI_assert(new_point_count == (curves.points_num() - points_to_delete.size())); - - /* Find out how many non-deleted points there are in every curve. */ Array curve_point_counts(curves.curves_num(), 0); - for (const IndexRange range : copy_point_ranges) { - for (const int point_i : range) { - curve_point_counts[point_to_curve_map[point_i]]++; - } - } + points_to_copy.foreach_index( + [&](const int64_t point_i) { curve_point_counts[point_to_curve_map[point_i]]++; }); - /* Build the offsets for the new curve points, skipping curves that had all points deleted. - * Also store the original indices of the corresponding input curves, to facilitate parallel - * copying of curve domain data. */ - int new_curve_count = 0; - int curve_point_offset = 0; - Vector new_curve_offsets; - Vector new_curve_orig_indices; - new_curve_offsets.append(0); - for (const int i : curve_point_counts.index_range()) { - if (curve_point_counts[i] > 0) { - curve_point_offset += curve_point_counts[i]; - new_curve_offsets.append(curve_point_offset); - - new_curve_count++; - new_curve_orig_indices.append(i); - } - } - - CurvesGeometry new_curves{new_point_count, new_curve_count}; - Vector point_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), - new_curves.attributes_for_write(), - ATTR_DOMAIN_MASK_POINT, - propagation_info); - Vector curve_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), - new_curves.attributes_for_write(), - ATTR_DOMAIN_MASK_CURVE, - propagation_info); - - threading::parallel_invoke( - 256 < new_point_count * new_curve_count, - /* Initialize curve offsets. */ - [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, - [&]() { - /* Copy over point attributes. */ - for (bke::AttributeTransferData &attribute : point_attributes) { - threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) { - for (const int range_i : range) { - const IndexRange src_range = copy_point_ranges[range_i]; - const IndexRange dst_range(copy_point_range_dst_offsets[range_i], src_range.size()); - copy_construct_data(attribute.src.slice(src_range), - attribute.dst.span.slice(dst_range)); - } - }); - } - }, - [&]() { - /* Copy over curve attributes. - * In some cases points are just dissolved, so the number of - * curves will be the same. That could be optimized in the future. */ - for (bke::AttributeTransferData &attribute : curve_attributes) { - if (new_curves.curves_num() == curves.curves_num()) { - attribute.dst.span.copy_from(attribute.src); - } - else { - bke::attribute_math::gather(attribute.src, new_curve_orig_indices, attribute.dst.span); - } - } + const IndexMask curves_to_copy = IndexMask::from_predicate( + curves.curves_range(), GrainSize(4096), memory, [&](const int64_t i) { + return curve_point_counts[i] > 0; }); - for (bke::AttributeTransferData &attribute : point_attributes) { - attribute.dst.finish(); + CurvesGeometry dst_curves(points_to_copy.size(), curves_to_copy.size()); + + threading::parallel_invoke( + dst_curves.curves_num() > 1024, + [&]() { + MutableSpan new_curve_offsets = dst_curves.offsets_for_write(); + array_utils::gather( + curve_point_counts.as_span(), curves_to_copy, new_curve_offsets.drop_back(1)); + offset_indices::accumulate_counts_to_offsets(new_curve_offsets); + }, + [&]() { + gather_attributes(curves.attributes(), + ATTR_DOMAIN_POINT, + propagation_info, + points_to_copy, + dst_curves.attributes_for_write()); + gather_attributes(curves.attributes(), + ATTR_DOMAIN_CURVE, + propagation_info, + curves_to_copy, + dst_curves.attributes_for_write()); + }); + + if (dst_curves.curves_num() == curves.curves_num()) { + dst_curves.runtime->type_counts = curves.runtime->type_counts; } - for (bke::AttributeTransferData &attribute : curve_attributes) { - attribute.dst.finish(); + else { + dst_curves.remove_attributes_based_on_types(); } - if (new_curves.curves_num() != curves.curves_num()) { - new_curves.remove_attributes_based_on_types(); - } - - return new_curves; + return dst_curves; } void CurvesGeometry::remove_points(const IndexMask &points_to_delete, @@ -1229,103 +1172,73 @@ void CurvesGeometry::remove_points(const IndexMask &points_to_delete, *this = copy_with_removed_points(*this, points_to_delete, propagation_info); } +template +static void gather_group_to_group(const OffsetIndices src_offsets, + const OffsetIndices dst_offsets, + const IndexMask &selection, + const Span src, + MutableSpan dst) +{ + selection.foreach_index(GrainSize(256), [&](const int64_t src_i, const int64_t dst_i) { + dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i])); + }); +} + +static void gather_group_to_group(const OffsetIndices src_offsets, + const OffsetIndices dst_offsets, + const IndexMask &selection, + const GSpan src, + GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + gather_group_to_group(src_offsets, dst_offsets, selection, src.typed(), dst.typed()); + }); +} + static CurvesGeometry copy_with_removed_curves( const CurvesGeometry &curves, const IndexMask &curves_to_delete, const AnonymousAttributePropagationInfo &propagation_info) { - const OffsetIndices old_points_by_curve = curves.points_by_curve(); - const Span old_offsets = curves.offsets(); - const Vector old_curve_ranges = curves_to_delete.to_ranges_invert( - curves.curves_range()); - Vector new_curve_ranges; - Vector old_point_ranges; - Vector new_point_ranges; - int new_tot_points = 0; - int new_tot_curves = 0; - for (const IndexRange &curve_range : old_curve_ranges) { - new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size())); - new_tot_curves += curve_range.size(); + IndexMaskMemory memory; + const IndexMask curves_to_copy = curves_to_delete.complement(curves.curves_range(), memory); - const IndexRange old_point_range = old_points_by_curve[curve_range]; - old_point_ranges.append(old_point_range); - new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size())); - new_tot_points += old_point_range.size(); - } + CurvesGeometry dst_curves(0, curves_to_copy.size()); + MutableSpan new_curve_offsets = dst_curves.offsets_for_write(); + offset_indices::gather_group_sizes( + curves.points_by_curve(), curves_to_copy, new_curve_offsets.drop_back(1)); + offset_indices::accumulate_counts_to_offsets(new_curve_offsets); + dst_curves.resize(new_curve_offsets.last(), dst_curves.curves_num()); - CurvesGeometry new_curves{new_tot_points, new_tot_curves}; - Vector point_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), - new_curves.attributes_for_write(), - ATTR_DOMAIN_MASK_POINT, - propagation_info); - Vector curve_attributes = bke::retrieve_attributes_for_transfer( - curves.attributes(), - new_curves.attributes_for_write(), - ATTR_DOMAIN_MASK_CURVE, - propagation_info); + const OffsetIndices src_points_by_curve = curves.points_by_curve(); + const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve(); - threading::parallel_invoke( - 256 < new_tot_points * new_tot_curves, - /* Initialize curve offsets. */ - [&]() { - MutableSpan new_offsets = new_curves.offsets_for_write(); - new_offsets.last() = new_tot_points; - threading::parallel_for( - old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { - for (const int range_i : ranges_range) { - const IndexRange old_curve_range = old_curve_ranges[range_i]; - const IndexRange new_curve_range = new_curve_ranges[range_i]; - const IndexRange old_point_range = old_point_ranges[range_i]; - const IndexRange new_point_range = new_point_ranges[range_i]; - const int offset_shift = new_point_range.start() - old_point_range.start(); - const int curves_in_range = old_curve_range.size(); - threading::parallel_for( - IndexRange(curves_in_range), 512, [&](const IndexRange range) { - for (const int i : range) { - const int old_curve_i = old_curve_range[i]; - const int new_curve_i = new_curve_range[i]; - const int old_offset = old_offsets[old_curve_i]; - const int new_offset = old_offset + offset_shift; - new_offsets[new_curve_i] = new_offset; - } - }); - } - }); - }, - [&]() { - /* Copy over point attributes. */ - for (bke::AttributeTransferData &attribute : point_attributes) { - threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { - for (const int range_i : range) { - copy_construct_data(attribute.src.slice(old_point_ranges[range_i]), - attribute.dst.span.slice(new_point_ranges[range_i])); - } - }); - } - }, - [&]() { - /* Copy over curve attributes. */ - for (bke::AttributeTransferData &attribute : curve_attributes) { - threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { - for (const int range_i : range) { - copy_construct_data(attribute.src.slice(old_curve_ranges[range_i]), - attribute.dst.span.slice(new_curve_ranges[range_i])); - } - }); - } - }); + const AttributeAccessor src_attributes = curves.attributes(); + MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - for (bke::AttributeTransferData &attribute : point_attributes) { - attribute.dst.finish(); - } - for (bke::AttributeTransferData &attribute : curve_attributes) { - attribute.dst.finish(); - } + src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; + } + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { + return true; + } + const GVArraySpan src = *src_attributes.lookup(id); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, meta_data.domain, meta_data.data_type); + gather_group_to_group(src_points_by_curve, dst_points_by_curve, curves_to_copy, src, dst.span); + dst.finish(); + return true; + }); - new_curves.remove_attributes_based_on_types(); + gather_attributes( + src_attributes, ATTR_DOMAIN_CURVE, propagation_info, curves_to_copy, dst_attributes); - return new_curves; + dst_curves.remove_attributes_based_on_types(); + dst_curves.update_curve_types(); + + return dst_curves; } void CurvesGeometry::remove_curves(const IndexMask &curves_to_delete,