Curves: Improve sculpting performance by reducing allocations

The snake hook and grow/shrink brushes need some arrays for input
to the length paramterization code. These were allocated and freed
for every curve. Instead, use a local buffer for each task execution.

Differential Revision: https://developer.blender.org/D15964
This commit is contained in:
Hans Goudey 2022-09-14 11:18:20 -05:00
parent 390320a151
commit 460fe4a10c
4 changed files with 44 additions and 22 deletions

View File

@ -352,33 +352,37 @@ float transform_brush_radius(const float4x4 &transform,
return math::distance(new_position, new_offset_position);
}
void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position)
void move_last_point_and_resample(MoveAndResampleBuffers &buffer,
MutableSpan<float3> positions,
const float3 &new_last_position)
{
/* Find the accumulated length of each point in the original curve,
* treating it as a poly curve for performance reasons and simplicity. */
Array<float> orig_lengths(length_parameterize::segments_num(positions.size(), false));
length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths);
const float orig_total_length = orig_lengths.last();
buffer.orig_lengths.reinitialize(length_parameterize::segments_num(positions.size(), false));
length_parameterize::accumulate_lengths<float3>(positions, false, buffer.orig_lengths);
const float orig_total_length = buffer.orig_lengths.last();
/* Find the factor by which the new curve is shorter or longer than the original. */
const float new_last_segment_length = math::distance(positions.last(1), new_last_position);
const float new_total_length = orig_lengths.last(1) + new_last_segment_length;
const float new_total_length = buffer.orig_lengths.last(1) + new_last_segment_length;
const float length_factor = safe_divide(new_total_length, orig_total_length);
/* Calculate the lengths to sample the original curve with by scaling the original lengths. */
Array<float> new_lengths(positions.size() - 1);
new_lengths.first() = 0.0f;
for (const int i : new_lengths.index_range().drop_front(1)) {
new_lengths[i] = orig_lengths[i - 1] * length_factor;
buffer.new_lengths.reinitialize(positions.size() - 1);
buffer.new_lengths.first() = 0.0f;
for (const int i : buffer.new_lengths.index_range().drop_front(1)) {
buffer.new_lengths[i] = buffer.orig_lengths[i - 1] * length_factor;
}
Array<int> indices(positions.size() - 1);
Array<float> factors(positions.size() - 1);
length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors);
buffer.sample_indices.reinitialize(positions.size() - 1);
buffer.sample_factors.reinitialize(positions.size() - 1);
length_parameterize::sample_at_lengths(
buffer.orig_lengths, buffer.new_lengths, buffer.sample_indices, buffer.sample_factors);
Array<float3> new_positions(positions.size() - 1);
length_parameterize::interpolate<float3>(positions, indices, factors, new_positions);
positions.drop_back(1).copy_from(new_positions);
buffer.new_positions.reinitialize(positions.size() - 1);
length_parameterize::interpolate<float3>(
positions, buffer.sample_indices, buffer.sample_factors, buffer.new_positions);
positions.drop_back(1).copy_from(buffer.new_positions);
positions.last() = new_last_position;
}

View File

@ -141,11 +141,11 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
{
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
MoveAndResampleBuffers resample_buffer;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
const IndexRange points = curves.points_for_curve(curve_i);
if (points.size() <= 1) {
continue;
}
@ -157,7 +157,7 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point);
const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu;
move_last_point_and_resample(positions_cu.slice(points), new_last_pos_cu);
move_last_point_and_resample(resample_buffer, positions_cu.slice(points), new_last_pos_cu);
}
});
}

View File

@ -102,7 +102,23 @@ VArray<float> get_curves_selection(const Curves &curves_id);
*/
VArray<float> get_point_selection(const Curves &curves_id);
void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position);
/** See #move_last_point_and_resample. */
struct MoveAndResampleBuffers {
Array<float> orig_lengths;
Array<float> new_lengths;
Array<int> sample_indices;
Array<float> sample_factors;
Array<float3> new_positions;
};
/**
* \param buffer: Reused memory to avoid reallocations when the function is called many times.
*/
void move_last_point_and_resample(MoveAndResampleBuffers &buffer,
MutableSpan<float3> positions,
const float3 &new_last_position);
class CurvesSculptCommonContext {
public:

View File

@ -188,6 +188,7 @@ struct SnakeHookOperatorExecutor {
const float brush_radius_sq_re = pow2f(brush_radius_re);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
@ -221,8 +222,8 @@ struct SnakeHookOperatorExecutor {
const float3 translation_orig = deformation.translation_from_deformed_to_original(
last_point_i, translation_eval);
move_last_point_and_resample(positions_cu.slice(points),
positions_cu[last_point_i] + translation_orig);
const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
}
});
}
@ -268,6 +269,7 @@ struct SnakeHookOperatorExecutor {
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
@ -289,8 +291,8 @@ struct SnakeHookOperatorExecutor {
const float3 translation_orig = deformation.translation_from_deformed_to_original(
last_point_i, translation_eval);
move_last_point_and_resample(positions_cu.slice(points),
positions_cu[last_point_i] + translation_orig);
const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
}
});
}