diff options
author | Hans Goudey <h.goudey@me.com> | 2021-06-21 02:42:02 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-06-21 02:42:02 +0300 |
commit | 0cd34967c0a844c7cebe6f835a3c2303a5e53ef6 (patch) | |
tree | 1f037883b621726c700480db431bc4ba6be744b2 | |
parent | 6afafc46f639c49a0d12a22c3ce298c6b6396ec9 (diff) |
Curves: Multithread Curve to CurveEval conversion
A different data structure / implementation is used for curves in the
node tree currently. Converting from the DNA `Curve` structure to this
wasn't slow, but it's nice to decrease overhead. In a test of 14000
splines with 128000 points, this halves the runtime from about 5ms
to about 2.5ms.
-rw-r--r-- | source/blender/blenkernel/intern/curve_eval.cc | 168 |
1 files changed, 111 insertions, 57 deletions
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index c9408cf4fcd..0a6e4458a35 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -15,10 +15,13 @@ */ #include "BLI_array.hh" +#include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" #include "DNA_curve_types.h" @@ -28,9 +31,12 @@ using blender::Array; using blender::float3; using blender::float4x4; +using blender::IndexRange; using blender::Map; +using blender::MutableSpan; using blender::Span; using blender::StringRefNull; +using blender::Vector; blender::Span<SplinePtr> CurveEval::splines() const { @@ -168,70 +174,118 @@ static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) return NURBSpline::KnotsMode::Normal; } +static SplinePtr spline_from_dna_bezier(const Nurb &nurb) +{ + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->set_resolution(nurb.resolu); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> handle_positions_left = spline->handle_positions_left(); + MutableSpan<float3> handle_positions_right = spline->handle_positions_right(); + MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left(); + MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BezTriple &bezt = src_points[i]; + positions[i] = bezt.vec[1]; + handle_positions_left[i] = bezt.vec[0]; + handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1); + handle_positions_right[i] = bezt.vec[2]; + handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2); + radii[i] = bezt.radius; + tilts[i] = bezt.tilt; + } + }); + + return spline; +} + +static SplinePtr spline_from_dna_nurbs(const Nurb &nurb) +{ + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->set_resolution(nurb.resolu); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + spline->set_order(nurb.orderu); + spline->knots_mode = knots_mode_from_dna_nurb(nurb.flagu); + + Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float> weights = spline->weights(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BPoint &bp = src_points[i]; + positions[i] = bp.vec; + weights[i] = bp.vec[3]; + radii[i] = bp.radius; + tilts[i] = bp.tilt; + } + }); + + return spline; +} + +static SplinePtr spline_from_dna_poly(const Nurb &nurb) +{ + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span<const BPoint> src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan<float3> positions = spline->positions(); + MutableSpan<float> radii = spline->radii(); + MutableSpan<float> tilts = spline->tilts(); + + blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const BPoint &bp = src_points[i]; + positions[i] = bp.vec; + radii[i] = bp.radius; + tilts[i] = bp.tilt; + } + }); + + return spline; +} + std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) { + Vector<const Nurb *> nurbs(*BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve))); + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + curve->resize(nurbs.size()); + MutableSpan<SplinePtr> splines = curve->splines(); - const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve)); - - /* TODO: Optimize by reserving the correct points size. */ - LISTBASE_FOREACH (const Nurb *, nurb, nurbs) { - switch (nurb->type) { - case CU_BEZIER: { - std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); - spline->set_resolution(nurb->resolu); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - - for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) { - spline->add_point(bezt.vec[1], - handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1), - bezt.vec[0], - handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2), - bezt.vec[2], - bezt.radius, - bezt.tilt); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - case CU_NURBS: { - std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); - spline->set_resolution(nurb->resolu); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - spline->set_order(nurb->orderu); - spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu); - - for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { - spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - case CU_POLY: { - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); - - for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { - spline->add_point(bp.vec, bp.radius, bp.tilt); - } - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - break; - } - default: { - BLI_assert_unreachable(); - break; + blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + switch (nurbs[i]->type) { + case CU_BEZIER: + splines[i] = spline_from_dna_bezier(*nurbs[i]); + break; + case CU_NURBS: + splines[i] = spline_from_dna_nurbs(*nurbs[i]); + break; + case CU_POLY: + splines[i] = spline_from_dna_poly(*nurbs[i]); + break; + default: + BLI_assert_unreachable(); + break; } } - } - - /* Though the curve has no attributes, this is necessary to properly set the custom data size. */ - curve->attributes.reallocate(curve->splines().size()); + }); - /* Note: Normal mode is stored separately in each spline to facilitate combining splines - * from multiple curve objects, where the value may be different. */ + /* Normal mode is stored separately in each spline to facilitate combining + * splines from multiple curve objects, where the value may be different. */ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve( dna_curve.twist_mode); for (SplinePtr &spline : curve->splines()) { |