Commit: d6c9cd445cb41480b40fc7a7c29bbf982a2a6446 Author: Hans Goudey Date: Thu Jan 26 12:34:28 2023 -0600 Branches: master https://developer.blender.org/rBd6c9cd445cb41480b40fc7a7c29bbf982a2a6446
Geometry Nodes: Skip sorting in topology nodes if possible When the sort weights are a single value, they have no effect, so sorting the relevant indices for the element will be wasted work. The sorting is expensive compared to the rest of the node. In my simple test of the points of curve node, it became 6 times faster when the weights are a single value. =================================================================== M source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc M source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc M source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc M source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc =================================================================== diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc index 0f9c14b0fee..8d9e4a07a76 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc @@ -49,6 +49,8 @@ class PointsOfCurveInput final : public bke::CurvesFieldInput { const eAttrDomain domain, const IndexMask mask) const final { + const OffsetIndices points_by_curve = curves.points_by_curve(); + const bke::CurvesFieldContext context{curves, domain}; fn::FieldEvaluator evaluator{context, &mask}; evaluator.add(curve_index_); @@ -62,7 +64,7 @@ class PointsOfCurveInput final : public bke::CurvesFieldInput { point_evaluator.add(sort_weight_); point_evaluator.evaluate(); const VArray<float> all_sort_weights = point_evaluator.get_evaluated<float>(0); - const OffsetIndices points_by_curve = curves.points_by_curve(); + const bool use_sorting = !all_sort_weights.is_single(); Array<int> point_of_curve(mask.min_array_size()); threading::parallel_for(mask.index_range(), 256, [&](const IndexRange range) { @@ -77,25 +79,29 @@ class PointsOfCurveInput final : public bke::CurvesFieldInput { point_of_curve[selection_i] = 0; continue; } - const IndexRange points = points_by_curve[curve_i]; - /* Retrieve the weights for each point. */ - sort_weights.reinitialize(points.size()); - all_sort_weights.materialize_compressed(IndexMask(points), sort_weights.as_mutable_span()); - - /* Sort a separate array of compressed indices corresponding to the compressed weights. - * This allows using `materialize_compressed` to avoid virtual function call overhead - * when accessing values in the sort weights. However, it means a separate array of - * indices within the compressed array is necessary for sorting. */ - sort_indices.reinitialize(points.size()); - std::iota(sort_indices.begin(), sort_indices.end(), 0); - std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { - return sort_weights[a] < sort_weights[b]; - }); - const int index_in_sort_wrapped = mod_i(index_in_sort, points.size()); - point_of_curve[selection_i] = points[sort_indices[index_in_sort_wrapped]]; + if (use_sorting) { + /* Retrieve the weights for each point. */ + sort_weights.reinitialize(points.size()); + all_sort_weights.materialize_compressed(IndexMask(points), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(points.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + point_of_curve[selection_i] = points[sort_indices[index_in_sort_wrapped]]; + } + else { + point_of_curve[selection_i] = points[index_in_sort_wrapped]; + } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc index d5b4b4869c1..55c70095236 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc @@ -64,6 +64,7 @@ class CornersOfFaceInput final : public bke::MeshFieldInput { corner_evaluator.add(sort_weight_); corner_evaluator.evaluate(); const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0); + const bool use_sorting = !all_sort_weights.is_single(); Array<int> corner_of_face(mask.min_array_size()); threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { @@ -82,23 +83,27 @@ class CornersOfFaceInput final : public bke::MeshFieldInput { const MPoly &poly = polys[poly_i]; const IndexRange corners(poly.loopstart, poly.totloop); - /* Retrieve the weights for each corner. */ - sort_weights.reinitialize(corners.size()); - all_sort_weights.materialize_compressed(IndexMask(corners), - sort_weights.as_mutable_span()); - - /* Sort a separate array of compressed indices corresponding to the compressed weights. - * This allows using `materialize_compressed` to avoid virtual function call overhead - * when accessing values in the sort weights. However, it means a separate array of - * indices within the compressed array is necessary for sorting. */ - sort_indices.reinitialize(corners.size()); - std::iota(sort_indices.begin(), sort_indices.end(), 0); - std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { - return sort_weights[a] < sort_weights[b]; - }); - const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size()); - corner_of_face[selection_i] = corners[sort_indices[index_in_sort_wrapped]]; + if (use_sorting) { + /* Retrieve the weights for each corner. */ + sort_weights.reinitialize(corners.size()); + all_sort_weights.materialize_compressed(IndexMask(corners), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(corners.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + corner_of_face[selection_i] = corners[sort_indices[index_in_sort_wrapped]]; + } + else { + corner_of_face[selection_i] = corners[index_in_sort_wrapped]; + } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc index 12310d6df15..8d7ebb4e105 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc @@ -77,6 +77,7 @@ class CornersOfVertInput final : public bke::MeshFieldInput { corner_evaluator.add(sort_weight_); corner_evaluator.evaluate(); const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0); + const bool use_sorting = !all_sort_weights.is_single(); Array<int> corner_of_vertex(mask.min_array_size()); threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { @@ -99,27 +100,31 @@ class CornersOfVertInput final : public bke::MeshFieldInput { continue; } - /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ - corner_indices.reinitialize(corners.size()); - convert_span(corners, corner_indices); - - /* Retrieve a compressed array of weights for each edge. */ - sort_weights.reinitialize(corners.size()); - all_sort_weights.materialize_compressed(IndexMask(corner_indices), - sort_weights.as_mutable_span()); - - /* Sort a separate array of compressed indices corresponding to the compressed weights. - * This allows using `materialize_compressed` to avoid virtual function call overhead - * when accessing values in the sort weights. However, it means a separate array of - * indices within the compressed array is necessary for sorting. */ - sort_indices.reinitialize(corners.size()); - std::iota(sort_indices.begin(), sort_indices.end(), 0); - std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { - return sort_weights[a] < sort_weights[b]; - }); - const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size()); - corner_of_vertex[selection_i] = corner_indices[sort_indices[index_in_sort_wrapped]]; + if (use_sorting) { + /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ + corner_indices.reinitialize(corners.size()); + convert_span(corners, corner_indices); + + /* Retrieve a compressed array of weights for each edge. */ + sort_weights.reinitialize(corners.size()); + all_sort_weights.materialize_compressed(IndexMask(corner_indices), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(corners.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_ind @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs