From 0cd34967c0a844c7cebe6f835a3c2303a5e53ef6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 20 Jun 2021 18:42:02 -0500 Subject: 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. --- source/blender/blenkernel/intern/curve_eval.cc | 168 ++++++++++++++++--------- 1 file changed, 111 insertions(+), 57 deletions(-) (limited to 'source/blender/blenkernel/intern/curve_eval.cc') 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 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 spline = std::make_unique(); + spline->set_resolution(nurb.resolu); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span src_points{nurb.bezt, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan positions = spline->positions(); + MutableSpan handle_positions_left = spline->handle_positions_left(); + MutableSpan handle_positions_right = spline->handle_positions_right(); + MutableSpan handle_types_left = spline->handle_types_left(); + MutableSpan handle_types_right = spline->handle_types_right(); + MutableSpan radii = spline->radii(); + MutableSpan 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 spline = std::make_unique(); + 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 src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan positions = spline->positions(); + MutableSpan weights = spline->weights(); + MutableSpan radii = spline->radii(); + MutableSpan 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 spline = std::make_unique(); + spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); + + Span src_points{nurb.bp, nurb.pntsu}; + spline->resize(src_points.size()); + MutableSpan positions = spline->positions(); + MutableSpan radii = spline->radii(); + MutableSpan 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 curve_eval_from_dna_curve(const Curve &dna_curve) { + Vector nurbs(*BKE_curve_nurbs_get(&const_cast(dna_curve))); + std::unique_ptr curve = std::make_unique(); + curve->resize(nurbs.size()); + MutableSpan splines = curve->splines(); - const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast(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 spline = std::make_unique(); - 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 spline = std::make_unique(); - 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 spline = std::make_unique(); - 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()) { -- cgit v1.2.3