Geometry Nodes: Optimize realize instances for single component inputs
Previously when there was a single mesh/curve/point cloud/grease pencil input, the code would still do a deep copy of the entire geometry. Not only does this waste time and memory usage, it also inhibits the benefits of the shared cache system, causing normals and other derived data to be recalculated more than necessary. This commit makes the realize instance node "free" in those cases. When the instance has a non-identity transform it only copies and transforms the positions; the rest of the geometry is untouched. For the implementation it was simpler to still copy the ID data-block and rely on implicit sharing to avoid the expense of copying attributes. That's because we don't have access to the original geometry set components after all the preprocessing has happened. Pull Request: https://projects.blender.org/blender/blender/pulls/127867
This commit is contained in:
@@ -360,13 +360,32 @@ static int64_t get_final_points_num(const GatherTasks &tasks)
|
||||
return points_num;
|
||||
}
|
||||
|
||||
static bool skip_transform(const float4x4 &transform)
|
||||
{
|
||||
return math::is_equal(transform, float4x4::identity(), 1e-6f);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (skip_transform(transform)) {
|
||||
dst.copy_from(src);
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
dst[i] = math::transform_point(transform, src[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void transform_positions(const float4x4 &transform, MutableSpan<float3> positions)
|
||||
{
|
||||
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
dst[i] = math::transform_point(transform, src[i]);
|
||||
positions[i] = math::transform_point(transform, positions[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1153,6 +1172,26 @@ static void execute_realize_pointcloud_task(
|
||||
dst_attribute_writers);
|
||||
}
|
||||
|
||||
static void add_instance_attributes_to_single_geometry(
|
||||
const OrderedAttributes &ordered_attributes,
|
||||
const AttributeFallbacksArray &attribute_fallbacks,
|
||||
bke::MutableAttributeAccessor attributes)
|
||||
{
|
||||
for (const int attribute_index : ordered_attributes.index_range()) {
|
||||
const void *value = attribute_fallbacks.array[attribute_index];
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
const bke::AttrDomain domain = ordered_attributes.kinds[attribute_index].domain;
|
||||
const eCustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
|
||||
const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(data_type);
|
||||
GVArray gvaray(GVArray::ForSingle(cpp_type, attributes.domain_size(domain), value));
|
||||
attributes.add(ordered_attributes.ids[attribute_index],
|
||||
domain,
|
||||
data_type,
|
||||
bke::AttributeInitVArray(std::move(gvaray)));
|
||||
}
|
||||
}
|
||||
static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &options,
|
||||
const AllPointCloudsInfo &all_pointclouds_info,
|
||||
const Span<RealizePointCloudTask> tasks,
|
||||
@@ -1163,6 +1202,19 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
|
||||
return;
|
||||
}
|
||||
|
||||
if (tasks.size() == 1) {
|
||||
const RealizePointCloudTask &task = tasks.first();
|
||||
PointCloud *new_points = BKE_pointcloud_copy_for_eval(task.pointcloud_info->pointcloud);
|
||||
if (!skip_transform(task.transform)) {
|
||||
transform_positions(task.transform, new_points->positions_for_write());
|
||||
new_points->tag_positions_changed();
|
||||
}
|
||||
add_instance_attributes_to_single_geometry(
|
||||
ordered_attributes, task.attribute_fallbacks, new_points->attributes_for_write());
|
||||
r_realized_geometry.replace_pointcloud(new_points);
|
||||
return;
|
||||
}
|
||||
|
||||
const RealizePointCloudTask &last_task = tasks.last();
|
||||
const PointCloud &last_pointcloud = *last_task.pointcloud_info->pointcloud;
|
||||
const int tot_points = last_task.start_index + last_pointcloud.totpoint;
|
||||
@@ -1491,6 +1543,19 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tasks.size() == 1) {
|
||||
const RealizeMeshTask &task = tasks.first();
|
||||
Mesh *new_mesh = BKE_mesh_copy_for_eval(*task.mesh_info->mesh);
|
||||
if (!skip_transform(task.transform)) {
|
||||
transform_positions(task.transform, new_mesh->vert_positions_for_write());
|
||||
new_mesh->tag_positions_changed();
|
||||
}
|
||||
add_instance_attributes_to_single_geometry(
|
||||
ordered_attributes, task.attribute_fallbacks, new_mesh->attributes_for_write());
|
||||
r_realized_geometry.replace_mesh(new_mesh);
|
||||
return;
|
||||
}
|
||||
|
||||
const RealizeMeshTask &last_task = tasks.last();
|
||||
const Mesh &last_mesh = *last_task.mesh_info->mesh;
|
||||
const int tot_vertices = last_task.start_indices.vertex + last_mesh.verts_num;
|
||||
@@ -1831,6 +1896,19 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tasks.size() == 1) {
|
||||
const RealizeCurveTask &task = tasks.first();
|
||||
Curves *new_curves = BKE_curves_copy_for_eval(task.curve_info->curves);
|
||||
if (!skip_transform(task.transform)) {
|
||||
new_curves->geometry.wrap().transform(task.transform);
|
||||
}
|
||||
add_instance_attributes_to_single_geometry(ordered_attributes,
|
||||
task.attribute_fallbacks,
|
||||
new_curves->geometry.wrap().attributes_for_write());
|
||||
r_realized_geometry.replace_curves(new_curves);
|
||||
return;
|
||||
}
|
||||
|
||||
const RealizeCurveTask &last_task = tasks.last();
|
||||
const Curves &last_curves = *last_task.curve_info->curves;
|
||||
const int points_num = last_task.start_indices.point + last_curves.geometry.point_num;
|
||||
@@ -2077,6 +2155,14 @@ static void execute_realize_grease_pencil_task(
|
||||
dst_attribute_writers);
|
||||
}
|
||||
|
||||
static void transform_grease_pencil_layers(Span<bke::greasepencil::Layer *> layers,
|
||||
const float4x4 &transform)
|
||||
{
|
||||
for (bke::greasepencil::Layer *layer : layers) {
|
||||
layer->set_local_transform(transform * layer->local_transform());
|
||||
}
|
||||
}
|
||||
|
||||
static void execute_realize_grease_pencil_tasks(
|
||||
const AllGreasePencilsInfo &all_grease_pencils_info,
|
||||
const Span<RealizeGreasePencilTask> tasks,
|
||||
@@ -2086,6 +2172,19 @@ static void execute_realize_grease_pencil_tasks(
|
||||
if (tasks.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tasks.size() == 1) {
|
||||
const RealizeGreasePencilTask &task = tasks.first();
|
||||
GreasePencil *new_gp = BKE_grease_pencil_copy_for_eval(task.grease_pencil_info->grease_pencil);
|
||||
if (!skip_transform(task.transform)) {
|
||||
transform_grease_pencil_layers(new_gp->layers_for_write(), task.transform);
|
||||
}
|
||||
add_instance_attributes_to_single_geometry(
|
||||
ordered_attributes, task.attribute_fallbacks, new_gp->attributes_for_write());
|
||||
r_realized_geometry.replace_grease_pencil(new_gp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate new grease pencil. */
|
||||
GreasePencil *dst_grease_pencil = BKE_grease_pencil_new_nomain();
|
||||
r_realized_geometry.replace_grease_pencil(dst_grease_pencil);
|
||||
|
||||
Reference in New Issue
Block a user