Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-03-09 18:56:14 +0300
committerHans Goudey <h.goudey@me.com>2022-03-09 18:58:02 +0300
commit6c6f591f909a02967daff87ab348601953f61670 (patch)
treeca9bef05953e67f40bbf80baa6ef8f04c067570e /source/blender
parent39c50d8f31bf63b4ccf22eae2d33b69e4cc0dc05 (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')
-rw-r--r--source/blender/geometry/intern/realize_instances.cc392
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();
+ }
}
/** \} */