Page Menu
Home
Search
Configure Global Search
Log In
Paste
P2196
Experimenting with avoiding allocation in curve evaluation functions
Active
Public
Actions
Authored by
Hans Goudey (HooglyBoogly)
on Jun 22 2021, 6:11 AM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Tags
Geometry Nodes
Subscribers
None
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 38a6d41a4d3..1af2b06efc6 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -174,6 +174,9 @@ class Spline {
void sample_with_index_factors(const blender::fn::GVArray &src,
blender::Span<float> index_factors,
blender::fn::GMutableSpan dst) const;
+ void sample_with_index_factors(blender::fn::GSpan src,
+ blender::Span<float> index_factors,
+ blender::fn::GMutableSpan dst) const;
template<typename T>
void sample_with_index_factors(const blender::VArray<T> &src,
blender::Span<float> index_factors,
@@ -194,14 +197,32 @@ class Spline {
* Interpolate a virtual array of data with the size of the number of control points to the
* evaluated points. For poly splines, the lifetime of the returned virtual array must not
* exceed the lifetime of the input data.
+ *
+ * \param buffer: A container for temporary storage of result values, when allocating them is
+ * necessary (not a poly spline). The vector may be resized to fit the result data.
+ */
+ virtual blender::fn::GSpan interpolate_to_evaluated(const blender::fn::GVArray &src,
+ blender::Vector<int8_t> &buffer) const = 0;
+ blender::fn::GSpan interpolate_to_evaluated(blender::fn::GSpan src,
+ blender::Vector<int8_t> &buffer) const;
+ template<typename T>
+ blender::Span<T> interpolate_to_evaluated(blender::Span<T> src,
+ blender::Vector<int8_t> &buffer) const
+ {
+ return this->interpolate_to_evaluated(blender::fn::GSpan(src), buffer).typed<T>();
+ }
+
+ /**
+ * Like #interpolate_to_evaluated, but for situations where the result data is more permanent
+ * and must always be copied to a result span, even for poly splines when that is unnecessary.
*/
- virtual blender::fn::GVArrayPtr interpolate_to_evaluated(
- const blender::fn::GVArray &src) const = 0;
- blender::fn::GVArrayPtr interpolate_to_evaluated(blender::fn::GSpan data) const;
+ virtual void interpolate_to_evaluated_copy(const blender::fn::GVArray &src,
+ blender::fn::GMutableSpan dst) const = 0;
+ void interpolate_to_evaluated_copy(blender::fn::GSpan src, blender::fn::GMutableSpan dst) const;
template<typename T>
- blender::fn::GVArray_Typed<T> interpolate_to_evaluated(blender::Span<T> data) const
+ void interpolate_to_evaluated_copy(blender::Span<T> src, blender::Span<T> dst) const
{
- return blender::fn::GVArray_Typed<T>(this->interpolate_to_evaluated(blender::fn::GSpan(data)));
+ this->interpolate_to_evaluated_copy(blender::fn::GSpan(src), blender::fn::GMutableSpan(dst));
}
protected:
@@ -332,7 +353,10 @@ class BezierSpline final : public Spline {
};
InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
- virtual blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const;
+ blender::fn::GSpan interpolate_to_evaluated(const blender::fn::GVArray &src,
+ blender::Vector<int8_t> &buffer) const final;
+ void interpolate_to_evaluated_copy(const blender::fn::GVArray &src,
+ blender::fn::GMutableSpan dst) const final;
void evaluate_segment(const int index,
const int next_index,
@@ -454,7 +478,10 @@ class NURBSpline final : public Spline {
blender::Span<blender::float3> evaluated_positions() const final;
- blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
+ blender::fn::GSpan interpolate_to_evaluated(const blender::fn::GVArray &src,
+ blender::Vector<int8_t> &buffer) const final;
+ void interpolate_to_evaluated_copy(const blender::fn::GVArray &src,
+ blender::fn::GMutableSpan dst) const final;
protected:
void correct_end_tangents() const final;
@@ -503,7 +530,10 @@ class PolySpline final : public Spline {
blender::Span<blender::float3> evaluated_positions() const final;
- blender::fn::GVArrayPtr interpolate_to_evaluated(const blender::fn::GVArray &src) const final;
+ blender::fn::GSpan interpolate_to_evaluated(const blender::fn::GVArray &src,
+ blender::Vector<int8_t> &buffer) const final;
+ void interpolate_to_evaluated_copy(const blender::fn::GVArray &src,
+ blender::fn::GMutableSpan dst) const final;
protected:
void correct_end_tangents() const final;
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index c18f44e07b2..41366d347f0 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -28,6 +28,7 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
+using blender::Vector;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
@@ -331,9 +332,10 @@ Span<float3> Spline::evaluated_normals() const
}
/* Rotate the generated normals with the interpolated tilt data. */
- GVArray_Typed<float> tilts = this->interpolate_to_evaluated(this->tilts());
+ Vector<int8_t> buffer;
+ Span<float> interpolated_tilts = this->interpolate_to_evaluated(this->tilts(), buffer);
for (const int i : normals.index_range()) {
- normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
+ normals[i] = rotate_direction_around_axis(normals[i], tangents[i], interpolated_tilts[i]);
}
normal_cache_dirty_ = false;
@@ -438,11 +440,6 @@ void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated)
}
}
-GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const
-{
- return this->interpolate_to_evaluated(GVArray_For_GSpan(data));
-}
-
/**
* Sample any input data with a value for each evaluated point (already interpolated to evaluated
* points) to arbitrary parameters in between the evaluated points. The interpolation is quite
@@ -468,3 +465,20 @@ void Spline::sample_with_index_factors(const GVArray &src,
});
});
}
+
+void Spline::sample_with_index_factors(GSpan src,
+ Span<float> index_factors,
+ GMutableSpan dst) const
+{
+ this->sample_with_index_factors(GVArray_For_GSpan(src), index_factors, dst);
+}
+
+GSpan Spline::interpolate_to_evaluated(GSpan src, Vector<int8_t> &buffer) const
+{
+ return this->interpolate_to_evaluated(GVArray_For_GSpan(src), buffer);
+}
+
+void Spline::interpolate_to_evaluated_copy(GSpan src, GMutableSpan dst) const
+{
+ this->interpolate_to_evaluated_copy(GVArray_For_GSpan(src), dst);
+}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index daae03167ef..633d2a05cba 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -25,9 +25,10 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
+using blender::Vector;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
using blender::fn::GVArray;
-using blender::fn::GVArray_For_ArrayContainer;
-using blender::fn::GVArrayPtr;
SplinePtr BezierSpline::copy() const
{
@@ -567,28 +568,22 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline,
}
}
-GVArrayPtr BezierSpline::interpolate_to_evaluated(const GVArray &src) const
+void BezierSpline::interpolate_to_evaluated_copy(const GVArray &src, GMutableSpan dst) const
{
- BLI_assert(src.size() == this->size());
-
- if (src.is_single()) {
- return src.shallow_copy();
- }
-
- const int eval_size = this->evaluated_points_size();
- if (eval_size == 1) {
- return src.shallow_copy();
- }
-
- GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(eval_size);
- interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values);
- new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
+ interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), dst.typed<T>());
}
});
+}
+
+GSpan BezierSpline::interpolate_to_evaluated(const GVArray &src, Vector<int8_t> &buffer) const
+{
+ const int eval_size = this->evaluated_points_size();
+ buffer.resize(src.type().size() * eval_size);
+ GMutableSpan buffer_span{src.type(), buffer.data(), eval_size};
- return new_varray;
+ this->interpolate_to_evaluated_copy(src, buffer_span);
+ return buffer_span;
}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
index 31ac23589be..bc2ee6601dd 100644
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -26,10 +26,10 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
+using blender::Vector;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
using blender::fn::GVArray;
-using blender::fn::GVArray_For_ArrayContainer;
-using blender::fn::GVArray_Typed;
-using blender::fn::GVArrayPtr;
SplinePtr NURBSpline::copy() const
{
@@ -377,6 +377,27 @@ Span<NURBSpline::BasisCache> NURBSpline::calculate_basis_cache() const
return basis_cache_;
}
+Span<float3> NURBSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ Spline::interpolate_to_evaluated_copy(positions_.as_span(),
+ evaluated_position_cache_.as_mutable_span());
+
+ position_cache_dirty_ = false;
+ return evaluated_position_cache_;
+}
+
template<typename T>
void interpolate_to_evaluated_impl(Span<NURBSpline::BasisCache> weights,
const blender::VArray<T> &src,
@@ -398,47 +419,26 @@ void interpolate_to_evaluated_impl(Span<NURBSpline::BasisCache> weights,
mixer.finalize();
}
-GVArrayPtr NURBSpline::interpolate_to_evaluated(const GVArray &src) const
+void NURBSpline::interpolate_to_evaluated_copy(const GVArray &src, GMutableSpan dst) const
{
BLI_assert(src.size() == this->size());
- if (src.is_single()) {
- return src.shallow_copy();
- }
-
Span<BasisCache> basis_cache = this->calculate_basis_cache();
- GVArrayPtr new_varray;
blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(this->evaluated_points_size());
- interpolate_to_evaluated_impl<T>(basis_cache, src.typed<T>(), values);
- new_varray = std::make_unique<GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
+ interpolate_to_evaluated_impl<T>(basis_cache, src.typed<T>(), dst.typed<T>());
}
});
-
- return new_varray;
}
-Span<float3> NURBSpline::evaluated_positions() const
+GSpan NURBSpline::interpolate_to_evaluated(const GVArray &src, Vector<int8_t> &buffer) const
{
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
- std::lock_guard lock{position_cache_mutex_};
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
const int eval_size = this->evaluated_points_size();
- evaluated_position_cache_.resize(eval_size);
-
- /* TODO: Avoid copying the evaluated data from the temporary array. */
- GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span());
- evaluated->materialize(evaluated_position_cache_);
+ buffer.resize(src.type().size() * eval_size);
+ GMutableSpan buffer_span{src.type(), buffer.data(), eval_size};
- position_cache_dirty_ = false;
- return evaluated_position_cache_;
+ this->interpolate_to_evaluated_copy(src, buffer_span);
+ return buffer_span;
}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
index e344b8d4910..8b2fee3920f 100644
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -22,8 +22,10 @@
using blender::float3;
using blender::MutableSpan;
using blender::Span;
+using blender::Vector;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
using blender::fn::GVArray;
-using blender::fn::GVArrayPtr;
SplinePtr PolySpline::copy() const
{
@@ -111,15 +113,31 @@ Span<float3> PolySpline::evaluated_positions() const
return this->positions();
}
+void PolySpline::interpolate_to_evaluated_copy(const GVArray &src, GMutableSpan dst) const
+{
+ BLI_assert(src.size() == this->size());
+ BLI_assert(dst.size() == this->evaluated_points_size());
+ src.materialize(dst.data());
+}
+
/**
* Poly spline interpolation from control points to evaluated points is a special case, since
- * the result data is the same as the input data. This function returns a GVArray that points to
+ * the result data is the same as the input data. This function returns a GSpan that points to
* the original data. Therefore the lifetime of the returned virtual array must not be longer than
* the source data.
*/
-GVArrayPtr PolySpline::interpolate_to_evaluated(const GVArray &src) const
+GSpan PolySpline::interpolate_to_evaluated(const GVArray &src, Vector<int8_t> &buffer) const
{
BLI_assert(src.size() == this->size());
+ const int eval_size = this->evaluated_points_size();
+
+ /* If the source data is a span already, simply return that. */
+ if (src.is_span()) {
+ return src.get_internal_span();
+ }
- return src.shallow_copy();
+ /* Otherwise materialize the source data into the result buffer and return a reference to it. */
+ buffer.resize(src.type().size() * eval_size);
+ src.materialize(buffer.data());
+ return {src.type(), static_cast<void *>(buffer.data()), eval_size};
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index fc65d1754e9..6360b963745 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_array.hh"
+#include "BLI_enumerable_thread_specific.hh"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
@@ -76,7 +77,9 @@ struct SampleModeParam {
std::optional<int> count;
};
-static SplinePtr resample_spline(const Spline &input_spline, const int count)
+static SplinePtr resample_spline(const Spline &input_spline,
+ const int count,
+ Vector<int8_t> &buffer)
{
std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
output_spline->set_cyclic(input_spline.is_cyclic());
@@ -98,12 +101,12 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
input_spline.evaluated_positions(), uniform_samples, output_spline->positions());
input_spline.sample_with_index_factors<float>(
- input_spline.interpolate_to_evaluated(input_spline.radii()),
+ input_spline.interpolate_to_evaluated(input_spline.radii(), buffer),
uniform_samples,
output_spline->radii());
input_spline.sample_with_index_factors<float>(
- input_spline.interpolate_to_evaluated(input_spline.tilts()),
+ input_spline.interpolate_to_evaluated(input_spline.tilts(), buffer),
uniform_samples,
output_spline->tilts());
@@ -124,7 +127,7 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
}
input_spline.sample_with_index_factors(
- *input_spline.interpolate_to_evaluated(*input_attribute),
+ input_spline.interpolate_to_evaluated(*input_attribute, buffer),
uniform_samples,
*output_attribute);
@@ -144,11 +147,13 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
output_curve->resize(input_splines.size());
MutableSpan<SplinePtr> output_splines = output_curve->splines();
+ threading::EnumerableThreadSpecific<Vector<int8_t>> buffers;
+
if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
BLI_assert(mode_param.count);
- output_splines[i] = resample_spline(*input_splines[i], *mode_param.count);
+ output_splines[i] = resample_spline(*input_splines[i], *mode_param.count, buffers.local());
}
});
}
@@ -157,7 +162,7 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
for (const int i : range) {
const float length = input_splines[i]->length();
const int count = std::max(int(length / *mode_param.length), 1);
- output_splines[i] = resample_spline(*input_splines[i], count);
+ output_splines[i] = resample_spline(*input_splines[i], count, buffers.local());
}
});
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index c0d817385e2..8587f362561 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -181,7 +181,8 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
Span<float3> normals = spline.evaluated_normals();
Span<float3> profile_positions = profile_spline.evaluated_positions();
- GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii());
+ Vector<int8_t> buffer;
+ Span<float> radii = spline.interpolate_to_evaluated(spline.radii(), buffer);
for (const int i_ring : IndexRange(spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 2725c625913..0dee9adb9d5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_array.hh"
+#include "BLI_enumerable_thread_specific.hh"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
@@ -71,6 +72,7 @@ namespace blender::nodes {
*/
static void evaluate_splines(Span<SplinePtr> splines)
{
+
threading::parallel_for_each(splines, [](const SplinePtr &spline) {
/* These functions fill the corresponding caches on each spline. */
spline->evaluated_positions();
@@ -85,6 +87,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms,
const CurveEval &curve,
const Span<SplinePtr> splines)
{
+
const int size = curve.splines().size();
switch (mode) {
case GEO_NODE_CURVE_SAMPLE_COUNT: {
@@ -199,8 +202,8 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
const int size = offsets[i + 1] - offsets[i];
data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
- spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size));
- spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size));
+ spline.interpolate_to_evaluated_copy(spline.radii(), data.radii.slice(offset, size));
+ spline.interpolate_to_evaluated_copy(spline.tilts(), data.tilts.slice(offset, size));
for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
const StringRef name = item.key;
@@ -209,8 +212,7 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
- spline.interpolate_to_evaluated(spline_span)
- ->materialize(point_span.slice(offset, size).data());
+ spline.interpolate_to_evaluated_copy(spline_span, point_span.slice(offset, size));
}
data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
@@ -223,7 +225,10 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
Span<int> offsets,
ResultAttributes &data)
{
+ threading::EnumerableThreadSpecific<Vector<int8_t>> buffers;
+
threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
+ Vector<int8_t> buffer = buffers.local();
for (const int i : range) {
const Spline &spline = *splines[i];
const int offset = offsets[i];
@@ -234,16 +239,16 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
const Array<float> uniform_samples = spline.sample_uniform_index_factors(size);
- spline.sample_with_index_factors<float3>(
+ spline.sample_with_index_factors(
spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size));
- spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
- uniform_samples,
- data.radii.slice(offset, size));
+ spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.radii(), buffer),
+ uniform_samples,
+ data.radii.slice(offset, size));
- spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.tilts()),
- uniform_samples,
- data.tilts.slice(offset, size));
+ spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.tilts(), buffer),
+ uniform_samples,
+ data.tilts.slice(offset, size));
for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
const StringRef name = item.key;
@@ -252,18 +257,18 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
BLI_assert(spline.attributes.get_for_read(name));
GSpan spline_span = *spline.attributes.get_for_read(name);
- spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span),
+ spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span, buffer),
uniform_samples,
point_span.slice(offset, size));
}
- spline.sample_with_index_factors<float3>(
+ spline.sample_with_index_factors(
spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size));
for (float3 &tangent : data.tangents) {
tangent.normalize();
}
- spline.sample_with_index_factors<float3>(
+ spline.sample_with_index_factors(
spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size));
for (float3 &normals : data.normals) {
normals.normalize();
Event Timeline
Hans Goudey (HooglyBoogly)
created this paste.
Jun 22 2021, 6:11 AM
Hans Goudey (HooglyBoogly)
changed the title of this paste from
Command-Line Input
to
Experimenting with avoiding allocation in curve evaluation functions
.
Jun 22 2021, 6:11 AM
Hans Goudey (HooglyBoogly)
updated the paste's language from
autodetect
to
diff
.
Hans Goudey (HooglyBoogly)
added a project:
Geometry Nodes
.
Hans Goudey (HooglyBoogly)
mentioned this in
T89336: Optimize allocations when sampling and evaluating splines
.
Jun 22 2021, 6:15 AM
Log In to Comment