From 25b4e41875bb7c062f14a49849aa43ca079f49ed Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 13 Mar 2022 14:42:56 -0500 Subject: [PATCH] Curve: Store NURBS basis cache as a single vector Instead of allocating a vector of the basis weights cache for each evaluated point, allocate a single vector for all of the weights. This should reduce memory usage by avoiding the overhead of storing many vectors. I noticed a small performance improvement to evaluated position calculation with an order of 5, which is larger than `Vector`'s default inline buffer capacity. This change is possible because of previous commits that made the basis cache for each evaluated point always have the same "order" size. --- source/blender/blenkernel/BKE_spline.hh | 11 +++-- .../blender/blenkernel/intern/spline_nurbs.cc | 49 ++++++++++--------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 1e3134020c6..817bbf60f54 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -453,12 +453,15 @@ class NURBSpline final : public Spline { KnotsMode knots_mode; struct BasisCache { - /** The influence at each control point `i + #start_index`. */ + /** + * For each evaluated point, the weight for alls control points that influences it. + * The vector's size is the evaluated point count multiplied by the spline's order. + */ blender::Vector weights; /** * An offset for the start of #weights: the first control point index with a non-zero weight. */ - int start_index; + blender::Vector start_indices; }; private: @@ -484,7 +487,7 @@ class NURBSpline final : public Spline { mutable bool knots_dirty_ = true; /** Cache of control point influences on each evaluated point. */ - mutable blender::Vector basis_cache_; + mutable BasisCache basis_cache_; mutable std::mutex basis_cache_mutex_; mutable bool basis_cache_dirty_ = true; @@ -547,7 +550,7 @@ class NURBSpline final : public Spline { void reverse_impl() override; void calculate_knots() const; - blender::Span calculate_basis_cache() const; + const BasisCache &calculate_basis_cache() const; }; /** diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index e8a324ccb5e..1138250cffc 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -273,7 +273,7 @@ static void calculate_basis_for_point(const float parameter, r_start_index = start; } -Span NURBSpline::calculate_basis_cache() const +const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const { if (!basis_cache_dirty_) { return basis_cache_; @@ -286,18 +286,22 @@ Span NURBSpline::calculate_basis_cache() const const int size = this->size(); const int eval_size = this->evaluated_points_size(); - if (eval_size == 0) { - return {}; - } - - basis_cache_.resize(eval_size); const int order = this->order(); const int degree = order - 1; - Span control_weights = this->weights(); - Span knots = this->knots(); - MutableSpan basis_cache(basis_cache_); + basis_cache_.weights.resize(eval_size * order); + basis_cache_.start_indices.resize(eval_size); + + if (eval_size == 0) { + return basis_cache_; + } + + MutableSpan basis_weights(basis_cache_.weights); + MutableSpan basis_start_indices(basis_cache_.start_indices); + + const Span control_weights = this->weights(); + const Span knots = this->knots(); const float start = knots[degree]; const float end = is_cyclic_ ? knots[size + degree] : knots[size]; @@ -306,18 +310,18 @@ Span NURBSpline::calculate_basis_cache() const /* Clamp parameter due to floating point inaccuracy. */ const float parameter = std::clamp(start + step * i, knots[0], knots[size + degree]); - BasisCache &basis = basis_cache[i]; - basis.weights.resize(order); + MutableSpan point_weights = basis_weights.slice(i * order, order); + calculate_basis_for_point(parameter, size + (is_cyclic_ ? degree : 0), degree, knots, - basis.weights, - basis.start_index); + point_weights, + basis_start_indices[i]); - for (const int j : basis.weights.index_range()) { - const int point_index = (basis.start_index + j) % size; - basis.weights[j] *= control_weights[point_index]; + for (const int j : point_weights.index_range()) { + const int point_index = (basis_start_indices[i] + j) % size; + point_weights[j] *= control_weights[point_index]; } } @@ -326,17 +330,18 @@ Span NURBSpline::calculate_basis_cache() const } template -void interpolate_to_evaluated_impl(Span weights, +void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, + const int order, const blender::VArray &src, MutableSpan dst) { const int size = src.size(); - BLI_assert(dst.size() == weights.size()); blender::attribute_math::DefaultMixer mixer(dst); for (const int i : dst.index_range()) { - Span point_weights = weights[i].weights; - const int start_index = weights[i].start_index; + Span point_weights = basis_cache.weights.as_span().slice(i * order, order); + const int start_index = basis_cache.start_indices[i]; + for (const int j : point_weights.index_range()) { const int point_index = (start_index + j) % size; mixer.mix_in(i, src[point_index], point_weights[j]); @@ -354,14 +359,14 @@ GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const return src; } - Span basis_cache = this->calculate_basis_cache(); + const BasisCache &basis_cache = this->calculate_basis_cache(); GVArray new_varray; blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(this->evaluated_points_size()); - interpolate_to_evaluated_impl(basis_cache, src.typed(), values); + interpolate_to_evaluated_impl(basis_cache, this->order(), src.typed(), values); new_varray = VArray::ForContainer(std::move(values)); } });