diff options
author | Hans Goudey <h.goudey@me.com> | 2022-03-09 18:56:14 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-03-09 18:58:02 +0300 |
commit | 6c6f591f909a02967daff87ab348601953f61670 (patch) | |
tree | ca9bef05953e67f40bbf80baa6ef8f04c067570e /source/blender/geometry/intern | |
parent | 39c50d8f31bf63b4ccf22eae2d33b69e4cc0dc05 (diff) |
Curves: Port realize instances node to the new data-block
This commit replaces the temporary conversion to `CurveEval` with
use of the new curves data-block. The end result is that the
process looks more like the other components-- somewhere in between
meshes and point clouds in terms of complexity.
The final result is that the logic between meshes and curves is
very similar. There are a few different strategies to reduce
duplication here, so I'll investigate that separately.
There is some special behavior for the radius and handle position
attributes. I used the attribute API to store spans of these
attributes temporarily. Using access methods on `CurvesGeometry`
would be reasonable to, storing spans separately feels a bit more
predictable for now though.
There should be significant performance improvements in some cases,
I haven't tested that specifically though.
Differential Revision: https://developer.blender.org/D14247
Diffstat (limited to 'source/blender/geometry/intern')
-rw-r--r-- | source/blender/geometry/intern/realize_instances.cc | 392 |
1 files changed, 257 insertions, 135 deletions
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 4f7024eea82..c2337c37d1f 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -121,15 +121,37 @@ struct RealizeMeshTask { struct RealizeCurveInfo { const Curves *curves; /** - * Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional` - * will be empty. + * Matches the order in #AllCurvesInfo.attributes. */ - Array<std::optional<GVArray_GSpan>> spline_attributes; + Array<std::optional<GVArray_GSpan>> attributes; + + /** ID attribute on the curves. If there are no ids, this #Span is empty. */ + Span<int> stored_ids; + + /** + * Handle position attributes must be transformed along with positions. Accessing them in + * advance isn't necessary theoretically, but is done to simplify other code and to avoid + * some overhead. + */ + Span<float3> handle_left; + Span<float3> handle_right; + + /** + * The radius attribute must be filled with a default of 1.0 if it + * doesn't exist on some (but not all) of the input curves data-blocks. + */ + Span<float> radius; +}; + +/** Start indices in the final output curves data-block. */ +struct CurvesElementStartIndices { + int point = 0; + int curve = 0; }; struct RealizeCurveTask { - /* Start index in the final curve. */ - int start_spline_index = 0; + CurvesElementStartIndices start_indices; + const RealizeCurveInfo *curve_info; /* Transformation applied to the position of control points and handles. */ float4x4 transform; @@ -168,6 +190,8 @@ struct AllCurvesInfo { /** Preprocessed data about every original curve. This is ordered by #order. */ Array<RealizeCurveInfo> realize_info; bool create_id_attribute = false; + bool create_handle_postion_attributes = false; + bool create_radius_attribute = false; }; /** Collects all tasks that need to be executed to realize all instances. */ @@ -185,7 +209,7 @@ struct GatherTasks { struct GatherOffsets { int pointcloud_offset = 0; MeshElementStartIndices mesh_offsets; - int spline_offset = 0; + CurvesElementStartIndices curves_offsets; }; struct GatherTasksInfo { @@ -230,6 +254,17 @@ struct InstanceContext { } }; +static void copy_transformed_positions(const Span<float3> src, + const float4x4 &transform, + MutableSpan<float3> dst) +{ + threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + dst[i] = transform * src[i]; + } + }); +} + /* -------------------------------------------------------------------- */ /** \name Gather Realize Tasks * \{ */ @@ -448,12 +483,13 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, if (curves != nullptr && curves->geometry.curve_size > 0) { const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; - gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset, + gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets, &curve_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.spline_offset += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; } break; } @@ -571,12 +607,8 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio pointcloud.totpoint}; MutableSpan<int> dst_ids = all_dst_ids.slice(task.start_index, pointcloud.totpoint); - /* Copy transformed positions. */ - threading::parallel_for(IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - for (const int i : range) { - dst_positions[i] = task.transform * src_positions[i]; - } - }); + copy_transformed_positions(src_positions, task.transform, dst_positions); + /* Create point ids. */ if (!all_dst_ids.is_empty()) { if (options.keep_original_ids) { @@ -1007,7 +1039,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Curve +/** \name Curves * \{ */ static OrderedAttributes gather_generic_curve_attributes_to_propagate( @@ -1023,9 +1055,6 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( in_geometry_set.gather_attributes_for_propagation( src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate); attributes_to_propagate.remove("position"); - attributes_to_propagate.remove("cyclic"); - attributes_to_propagate.remove("resolution"); - attributes_to_propagate.remove("tilt"); attributes_to_propagate.remove("radius"); attributes_to_propagate.remove("handle_right"); attributes_to_propagate.remove("handle_left"); @@ -1071,19 +1100,43 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set, /* Access attributes. */ CurveComponent component; component.replace(const_cast<Curves *>(curves), GeometryOwnershipType::ReadOnly); - curve_info.spline_attributes.reinitialize(info.attributes.size()); + curve_info.attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeDomain domain = info.attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_CURVE) { - continue; - } const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index]; const CustomDataType data_type = info.attributes.kinds[attribute_index].data_type; if (component.attribute_exists(attribute_id)) { GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type); - curve_info.spline_attributes[attribute_index].emplace(std::move(attribute)); + curve_info.attributes[attribute_index].emplace(std::move(attribute)); + } + } + if (info.create_id_attribute) { + ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id"); + if (ids_lookup) { + curve_info.stored_ids = ids_lookup.varray.get_internal_span().typed<int>(); } } + + /* Retrieve the radius attribute, if it exists. */ + if (component.attribute_exists("radius")) { + curve_info.radius = component + .attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 0.0f) + .get_internal_span(); + info.create_radius_attribute = true; + } + + /* Retrieve handle position attributes, if they exist. */ + if (component.attribute_exists("handle_right")) { + curve_info.handle_left = component + .attribute_get_for_read<float3>( + "handle_left", ATTR_DOMAIN_POINT, float3(0)) + .get_internal_span(); + curve_info.handle_right = component + .attribute_get_for_read<float3>( + "handle_right", ATTR_DOMAIN_POINT, float3(0)) + .get_internal_span(); + info.create_handle_postion_attributes = true; + } } return info; } @@ -1092,108 +1145,129 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, const AllCurvesInfo &all_curves_info, const RealizeCurveTask &task, const OrderedAttributes &ordered_attributes, - MutableSpan<SplinePtr> dst_splines, - MutableSpan<GMutableSpan> dst_spline_attributes) + bke::CurvesGeometry &dst_curves, + MutableSpan<GMutableSpan> dst_attribute_spans, + MutableSpan<int> all_dst_ids, + MutableSpan<float3> all_handle_left, + MutableSpan<float3> all_handle_right, + MutableSpan<float> all_radii) { - const RealizeCurveInfo &curve_info = *task.curve_info; - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_info.curves); - - const Span<SplinePtr> src_splines = curve->splines(); - - /* Initialize point attributes. */ - threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) { - for (const int src_spline_index : src_spline_range) { - const int dst_spline_index = src_spline_index + task.start_spline_index; - const Spline &src_spline = *src_splines[src_spline_index]; - SplinePtr dst_spline = src_spline.copy_without_attributes(); - dst_spline->transform(task.transform); - const int spline_size = dst_spline->size(); - - const CustomDataAttributes &src_point_attributes = src_spline.attributes; - CustomDataAttributes &dst_point_attributes = dst_spline->attributes; - - /* Create point ids. */ - if (all_curves_info.create_id_attribute) { - dst_point_attributes.create("id", CD_PROP_INT32); - MutableSpan<int> dst_point_ids = dst_point_attributes.get_for_write("id")->typed<int>(); - std::optional<GSpan> src_point_ids_opt = src_point_attributes.get_for_read("id"); - if (options.keep_original_ids) { - if (src_point_ids_opt.has_value()) { - const Span<int> src_point_ids = src_point_ids_opt->typed<int>(); - dst_point_ids.copy_from(src_point_ids); - } - else { - dst_point_ids.fill(0); - } - } - else { - if (src_point_ids_opt.has_value()) { - const Span<int> src_point_ids = src_point_ids_opt->typed<int>(); - for (const int i : IndexRange(dst_spline->size())) { - dst_point_ids[i] = noise::hash(task.id, src_point_ids[i]); - } - } - else { - for (const int i : IndexRange(dst_spline->size())) { - /* Mix spline index into the id, because otherwise points on different splines will - * get the same id. */ - dst_point_ids[i] = noise::hash(task.id, src_spline_index, i); - } - } - } - } + const RealizeCurveInfo &curves_info = *task.curve_info; + const Curves &curves_id = *curves_info.curves; + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - /* Copy generic point attributes. */ - for (const int attribute_index : ordered_attributes.index_range()) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_POINT) { - continue; - } - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; - const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type); - const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index]; - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - const std::optional<GSpan> src_span_opt = src_point_attributes.get_for_read(attribute_id); - void *dst_buffer = MEM_malloc_arrayN(spline_size, cpp_type.size(), "Curve Attribute"); - if (src_span_opt.has_value()) { - const GSpan src_span = *src_span_opt; - cpp_type.copy_construct_n(src_span.data(), dst_buffer, spline_size); - } - else { - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); - } - cpp_type.fill_construct_n(attribute_fallback, dst_buffer, spline_size); - } - dst_point_attributes.create_by_move(attribute_id, data_type, dst_buffer); - } + const IndexRange dst_point_range{task.start_indices.point, curves.points_size()}; + const IndexRange dst_curve_range{task.start_indices.curve, curves.curves_size()}; + + copy_transformed_positions( + curves.positions(), task.transform, dst_curves.positions().slice(dst_point_range)); - dst_splines[dst_spline_index] = std::move(dst_spline); + /* Copy and transform handle positions if necessary. */ + if (all_curves_info.create_handle_postion_attributes) { + if (curves_info.handle_left.is_empty()) { + all_handle_left.fill(float3(0)); } - }); - /* Initialize spline attributes. */ - for (const int attribute_index : ordered_attributes.index_range()) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_CURVE) { - continue; + else { + copy_transformed_positions( + curves_info.handle_left, task.transform, all_handle_left.slice(dst_point_range)); } - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; - const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type); + if (curves_info.handle_right.is_empty()) { + all_handle_right.fill(float3(0)); + } + else { + copy_transformed_positions( + curves_info.handle_right, task.transform, all_handle_right.slice(dst_point_range)); + } + } - GMutableSpan dst_span = dst_spline_attributes[attribute_index].slice(task.start_spline_index, - src_splines.size()); - if (curve_info.spline_attributes[attribute_index].has_value()) { - const GSpan src_span = *curve_info.spline_attributes[attribute_index]; - cpp_type.copy_construct_n(src_span.data(), dst_span.data(), src_splines.size()); + /* Copy radius attribute with 1.0 default if it doesn't exist. */ + if (all_curves_info.create_radius_attribute) { + if (curves_info.radius.is_empty()) { + all_radii.slice(dst_point_range).fill(1.0f); } else { - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); + all_radii.slice(dst_point_range).copy_from(curves_info.radius); + } + } + + /* Copy curve offsets. */ + const Span<int> src_offsets = curves.offsets(); + const MutableSpan<int> dst_offsets = dst_curves.offsets().slice(dst_curve_range); + threading::parallel_for(curves.curves_range(), 2048, [&](const IndexRange range) { + for (const int i : range) { + dst_offsets[i] = task.start_indices.point + src_offsets[i]; + } + }); + + /* Create id attribute. */ + if (!all_dst_ids.is_empty()) { + MutableSpan<int> dst_ids = all_dst_ids.slice(task.start_indices.point, curves.points_size()); + if (options.keep_original_ids) { + if (curves_info.stored_ids.is_empty()) { + dst_ids.fill(0); } - cpp_type.fill_construct_n(attribute_fallback, dst_span.data(), src_splines.size()); + else { + dst_ids.copy_from(curves_info.stored_ids); + } + } + else { + threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange vert_range) { + if (curves_info.stored_ids.is_empty()) { + for (const int i : vert_range) { + dst_ids[i] = noise::hash(task.id, i); + } + } + else { + for (const int i : vert_range) { + const int original_id = curves_info.stored_ids[i]; + dst_ids[i] = noise::hash(task.id, original_id); + } + } + }); } } + + /* Copy generic attributes. */ + threading::parallel_for( + dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) { + for (const int attribute_index : attribute_range) { + const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; + IndexRange element_slice; + switch (domain) { + case ATTR_DOMAIN_POINT: + element_slice = IndexRange(task.start_indices.point, curves.points_size()); + break; + case ATTR_DOMAIN_CURVE: + element_slice = IndexRange(task.start_indices.curve, curves.curves_size()); + break; + default: + BLI_assert_unreachable(); + break; + } + GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice); + const CPPType &cpp_type = dst_span.type(); + const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; + if (curves_info.attributes[attribute_index].has_value()) { + const GSpan src_span = *curves_info.attributes[attribute_index]; + threading::parallel_for( + IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { + cpp_type.copy_assign_n(src_span.slice(sub_range).data(), + dst_span.slice(sub_range).data(), + sub_range.size()); + }); + } + else { + if (attribute_fallback == nullptr) { + attribute_fallback = cpp_type.default_value(); + } + threading::parallel_for( + IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { + cpp_type.fill_assign_n( + attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); + }); + } + } + }); } static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, @@ -1208,42 +1282,90 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size; + const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; + const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; - Array<SplinePtr> dst_splines(tot_splines); + /* Allocate new curves data-block. */ + Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + dst_curves.offsets().last() = points_size; + CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); + dst_component.replace(dst_curves_id); - std::unique_ptr<CurveEval> dst_curve = std::make_unique<CurveEval>(); - dst_curve->attributes.reallocate(tot_splines); - CustomDataAttributes &spline_attributes = dst_curve->attributes; + /* Prepare id attribute. */ + OutputAttribute_Typed<int> point_ids; + MutableSpan<int> point_ids_span; + if (all_curves_info.create_id_attribute) { + point_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT); + point_ids_span = point_ids.as_span(); + } - /* Prepare spline attributes. */ - Vector<GMutableSpan> dst_spline_attributes; + /* Prepare generic output attributes. */ + Vector<OutputAttribute> dst_attributes; + Vector<GMutableSpan> dst_attribute_spans; for (const int attribute_index : ordered_attributes.index_range()) { const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index]; - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain == ATTR_DOMAIN_CURVE) { - spline_attributes.create(attribute_id, data_type); - dst_spline_attributes.append(*spline_attributes.get_for_write(attribute_id)); - } - else { - dst_spline_attributes.append({CPPType::get<float>()}); - } + const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + dst_attribute_spans.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } + + /* Prepare handle position attributes if necessary. */ + OutputAttribute_Typed<float3> handle_left; + OutputAttribute_Typed<float3> handle_right; + MutableSpan<float3> handle_left_span; + MutableSpan<float3> handle_right_span; + if (all_curves_info.create_handle_postion_attributes) { + handle_left = dst_component.attribute_try_get_for_output_only<float3>("handle_left", + ATTR_DOMAIN_POINT); + handle_right = dst_component.attribute_try_get_for_output_only<float3>("handle_right", + ATTR_DOMAIN_POINT); + handle_left_span = handle_left.as_span(); + handle_right_span = handle_right.as_span(); + } + + /* Prepare radius attribute if necessary. */ + OutputAttribute_Typed<float> radius; + MutableSpan<float> radius_span; + if (all_curves_info.create_radius_attribute) { + radius = dst_component.attribute_try_get_for_output_only<float>("radius", ATTR_DOMAIN_POINT); + radius_span = radius.as_span(); } /* Actually execute all tasks. */ threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) { for (const int task_index : task_range) { const RealizeCurveTask &task = tasks[task_index]; - execute_realize_curve_task( - options, all_curves_info, task, ordered_attributes, dst_splines, dst_spline_attributes); + execute_realize_curve_task(options, + all_curves_info, + task, + ordered_attributes, + dst_curves, + dst_attribute_spans, + point_ids_span, + handle_left_span, + handle_right_span, + radius_span); } }); - dst_curve->add_splines(dst_splines); - - CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); - dst_component.replace(curve_eval_to_curves(*dst_curve)); + /* Save modified attributes. */ + for (OutputAttribute &dst_attribute : dst_attributes) { + dst_attribute.save(); + } + if (point_ids) { + point_ids.save(); + } + if (radius) { + radius.save(); + } + if (all_curves_info.create_handle_postion_attributes) { + handle_left.save(); + handle_right.save(); + } } /** \} */ |