diff options
author | Hans Goudey <h.goudey@me.com> | 2022-03-18 00:36:43 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-03-18 00:36:43 +0300 |
commit | 663bd38ed65eb08013677bab2e581bc384a29efe (patch) | |
tree | 5ee5b132331f9d63b90fe39c1a464869ac432660 /source/blender/nodes | |
parent | 6d97a5f93c77c9fdcd1b92b43b0c02a6ff623c9b (diff) |
Curves: Port duplicate elements node to new data-block
Remove the conversion to and from `CurveEval` by supporting the
new Curves data-block in the node. This allows for some simplifications
to the code, as well as a fix for transfering curve domain attributes
when duplicating the curve domain.
The performance improvements (obverved through the timings overlay)
can be relatively massive with many curves. When duplicating 10000
4-point curves to become 2 million curves, I observed an approximate
150x improvement, from about 3 seconds to about 20ms.
Diffstat (limited to 'source/blender/nodes')
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc | 244 |
1 files changed, 130 insertions, 114 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 518d7650a74..535392fbca7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -10,9 +10,9 @@ #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" +#include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -114,6 +114,13 @@ static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, Mut }); } +static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst) +{ + for (const int i : src.index_range()) { + dst[i] = noise::hash(src[i], hash); + } +} + static void threaded_id_offset_copy(const Span<int> offsets, const Span<int> src, MutableSpan<int> dst) @@ -275,11 +282,12 @@ static void copy_stable_id_faces(const Mesh &mesh, * destination, * then loop over the remaining ones point by point, hashing their ids to the new ids. */ -static void copy_stable_id_splines(const CurveEval &curve, +static void copy_stable_id_splines(const bke::CurvesGeometry &src_curves, const IndexMask selection, const Span<int> curve_offsets, - const GeometryComponent &src_component, - GeometryComponent &dst_component) + const CurveComponent &src_component, + bke::CurvesGeometry &dst_curves, + CurveComponent &dst_component) { ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); if (!src_attribute) { @@ -291,33 +299,18 @@ static void copy_stable_id_splines(const CurveEval &curve, return; } - Array<int> control_point_offsets = curve.control_point_offsets(); VArray_Span<int> src{src_attribute.varray.typed<int>()}; MutableSpan<int> dst = dst_attribute.as_span<int>(); - Array<int> curve_point_offsets(selection.size() + 1); - int dst_point_size = 0; - for (const int i_curve : selection.index_range()) { - const int spline_size = curve.splines()[i_curve]->size(); - const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); - - curve_point_offsets[i_curve] = dst_point_size; - dst_point_size += curve_range.size() * spline_size; - } - curve_point_offsets.last() = dst_point_size; - - threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) { - for (const int i_curve : range) { - const int spline_size = curve.splines()[i_curve]->size(); - const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); - - dst.slice(curve_point_offsets[i_curve], spline_size) - .copy_from(src.slice(control_point_offsets[i_curve], spline_size)); - for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) { - for (const int i_point : IndexRange(spline_size)) { - dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash( - src[control_point_offsets[i_curve] + i_point], i_duplicate); - } + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const Span<int> curve_src = src.slice(src_curves.range_for_curve(i_src_curve)); + const IndexRange duplicates_range = range_for_offsets_index(curve_offsets, i_selection); + for (const int i_duplicate : IndexRange(duplicates_range.size()).drop_front(1)) { + const int i_dst_curve = duplicates_range[i_duplicate]; + copy_hashed_ids( + curve_src, i_duplicate, dst.slice(dst_curves.range_for_curve(i_dst_curve))); } } }); @@ -369,15 +362,16 @@ static void copy_point_attributes_without_id(GeometrySet &geometry_set, * copied with an offset fill, otherwise a mapping is used. */ static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, - const Span<int> point_mapping, - const Span<int> offsets, - const Span<std::string> attributes_to_ignore, - const GeometryComponent &src_component, - GeometryComponent &dst_component) + const CurveComponent &src_component, + const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span<int> curve_offsets, + bke::CurvesGeometry &dst_curves, + CurveComponent &dst_component) { Map<AttributeIDRef, AttributeKind> gathered_attributes; gather_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes); + geometry_set, GEO_COMPONENT_TYPE_CURVE, {}, false, gathered_attributes); for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { @@ -403,10 +397,18 @@ static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, switch (out_domain) { case ATTR_DOMAIN_CURVE: - threaded_slice_fill<T>(offsets, src, dst); + threaded_slice_fill<T>(curve_offsets, src, dst); break; case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(point_mapping, src, dst); + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const Span<T> curve_src = src.slice(src_curves.range_for_curve(i_src_curve)); + for (const int i_dst_curve : range_for_offsets_index(curve_offsets, i_selection)) { + dst.slice(dst_curves.range_for_curve(i_dst_curve)).copy_from(curve_src); + } + } + }); break; default: break; @@ -540,65 +542,67 @@ static void duplicate_splines(GeometrySet &geometry_set, } geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); - const GeometryComponent &src_component = *geometry_set.get_component_for_read( - GEO_COMPONENT_TYPE_CURVE); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); + const Curves &curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, curves.curves_size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); const VArray<int> counts = evaluator.get_evaluated<int>(0); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + /* The offset in the result curve domain at every selected input curve. */ Array<int> curve_offsets(selection.size() + 1); + Array<int> point_offsets(selection.size() + 1); int dst_splines_size = 0; int dst_points_size = 0; for (const int i_spline : selection.index_range()) { const int count = std::max(counts[selection[i_spline]], 0); curve_offsets[i_spline] = dst_splines_size; + point_offsets[i_spline] = dst_points_size; dst_splines_size += count; - dst_points_size += count * curve->splines()[selection[i_spline]]->size(); + dst_points_size += count * curves.range_for_curve(selection[i_spline]).size(); } curve_offsets.last() = dst_splines_size; - - Array<int> control_point_offsets = curve->control_point_offsets(); - Array<int> point_mapping(dst_points_size); - - std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - int point_index = 0; - for (const int i_spline : selection.index_range()) { - const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline); - for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) { - SplinePtr spline = curve->splines()[selection[i_spline]]->copy(); - for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) { - point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point; + point_offsets.last() = dst_points_size; + + Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_splines_size); + bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); + MutableSpan<int> all_dst_offsets = new_curves.offsets(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i_src_curve = selection[i_selection]; + const IndexRange src_curve_range = curves.range_for_curve(i_src_curve); + const IndexRange dst_curves_range = range_for_offsets_index(curve_offsets, i_selection); + MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range); + for (const int i_duplicate : IndexRange(dst_curves_range.size())) { + dst_offsets[i_duplicate] = point_offsets[i_selection] + + src_curve_range.size() * i_duplicate; } - new_curve->add_spline(std::move(spline)); } - } - new_curve->attributes.reallocate(new_curve->splines().size()); + }); + all_dst_offsets.last() = dst_points_size; CurveComponent dst_component; - dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); - - Vector<std::string> skip( - {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"}); + dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); copy_spline_attributes_without_id( - geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component); + geometry_set, src_component, curves, selection, curve_offsets, new_curves, dst_component); - copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component); + copy_stable_id_splines( + curves, selection, curve_offsets, src_component, new_curves, dst_component); if (attributes.duplicate_index) { create_duplicate_index_attribute( dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); } - geometry_set.replace_curves(dst_component.get_for_write()); + geometry_set.replace_curves(new_curves_id); } static void duplicate_faces(GeometrySet &geometry_set, @@ -776,13 +780,14 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexAttributes &attributes) { const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const Curves &src_curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.points_size() == 0) { return; } GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, domain_size}; + FieldEvaluator evaluator{field_context, src_curves.points_size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -790,60 +795,71 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + const int dst_size = offsets.last(); - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - Array<int> control_point_offsets = curve->control_point_offsets(); - std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - - Array<int> parent(domain_size); - int spline = 0; - for (const int i_spline : IndexRange(domain_size)) { - if (i_spline == control_point_offsets[spline + 1]) { - spline++; + Array<int> point_to_curve_map(src_curves.points_size()); + threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { + for (const int i_curve : range) { + const IndexRange point_range = src_curves.range_for_curve(i_curve); + point_to_curve_map.as_mutable_span().slice(point_range).fill(i_curve); } - parent[i_spline] = spline; + }); + + Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); + MutableSpan<int> new_curve_offsets = new_curves.offsets(); + for (const int i : new_curves.curves_range()) { + new_curve_offsets[i] = i; } + new_curve_offsets.last() = dst_size; - for (const int i_point : selection) { - const IndexRange point_range = range_for_offsets_index(offsets, i_point); - for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) { - const SplinePtr &parent_spline = curve->splines()[parent[i_point]]; - switch (parent_spline->type()) { - case CurveType::CURVE_TYPE_BEZIER: { - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->resize(1); - spline->set_resolution(2); - new_curve->add_spline(std::move(spline)); - break; - } - case CurveType::CURVE_TYPE_NURBS: { - std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); - spline->resize(1); - spline->set_resolution(2); - new_curve->add_spline(std::move(spline)); + CurveComponent dst_component; + dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); + + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (domain) { + case ATTR_DOMAIN_CURVE: + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const T &src_value = src[point_to_curve_map[selection[i_selection]]]; + const IndexRange duplicate_range = range_for_offsets_index(offsets, i_selection); + dst.slice(duplicate_range).fill(src_value); + } + }); break; - } - case CurveType::CURVE_TYPE_POLY: { - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(1); - new_curve->add_spline(std::move(spline)); + case ATTR_DOMAIN_POINT: + threaded_slice_fill(offsets, src, dst); break; - } - case CurveType::CURVE_TYPE_CATMULL_ROM: { - /* Catmull Rom curves are not supported yet. */ + default: break; - } } - } + }); + dst_attribute.save(); } - new_curve->attributes.reallocate(new_curve->splines().size()); - CurveComponent dst_component; - dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); - - copy_point_attributes_without_id( - geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component); copy_stable_id_point(offsets, src_component, dst_component); @@ -852,7 +868,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); } - curve_component.replace(dst_component.get_for_write()); + geometry_set.replace_curves(new_curves_id); } static void duplicate_points_mesh(GeometrySet &geometry_set, |