BLI: refactor IndexMask for better performance and memory usage #104629

Merged
Jacques Lucke merged 254 commits from JacquesLucke/blender:index-mask-refactor into main 2023-05-24 18:11:47 +02:00
12 changed files with 89 additions and 102 deletions
Showing only changes of commit a01d444ecd - Show all commits

View File

@ -213,6 +213,7 @@ class IndexMask {
template<typename T> void to_indices(MutableSpan<T> r_indices) const;
void to_bits(MutableBitSpan r_bits, int64_t offset = 0) const;
void to_bools(MutableSpan<bool> r_bools, int64_t offset = 0) const;
std::optional<IndexRange> to_range() const;
Vector<IndexRange> to_ranges() const;
Vector<IndexRange> to_ranges_invert(IndexRange universe) const;

View File

@ -881,6 +881,12 @@ void IndexMask::to_bits(MutableBitSpan r_bits, int64_t offset) const
index_mask_to_bits(*this, offset, r_bits);
}
void IndexMask::to_bools(MutableSpan<bool> r_bools, int64_t offset = 0) const
{
r_bools.fill(false);
this->foreach_index_optimized([&](const int64_t i) { r_bools[i - offset] = true; });
}
std::optional<IndexRange> IndexMask::to_range() const
{
if (data_.indices_num == 0) {

View File

@ -48,6 +48,7 @@
using blender::Array;
using blender::IndexMask;
using blender::IndexMaskMemory;
using blender::Span;
using blender::Vector;
@ -64,18 +65,14 @@ static Span<MDeformVert> get_vertex_group(const Mesh &mesh, const int defgrp_ind
return {vertex_group, mesh.totvert};
}
static Vector<int64_t> selected_indices_from_vertex_group(Span<MDeformVert> vertex_group,
const int index,
const bool invert)
static IndexMask selected_indices_from_vertex_group(Span<MDeformVert> vertex_group,
const int index,
const bool invert,
IndexMaskMemory &memory)
{
Vector<int64_t> selected_indices;
for (const int i : vertex_group.index_range()) {
const bool found = BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f;
if (found != invert) {
selected_indices.append(i);
}
}
return selected_indices;
return IndexMask::from_predicate(vertex_group.index_range(), 512, memory, [&](const int i) {
return (BKE_defvert_find_weight(&vertex_group[i], index) > 0.0f) != invert;
});
}
static Array<bool> selection_array_from_vertex_group(Span<MDeformVert> vertex_group,
@ -98,8 +95,9 @@ static std::optional<Mesh *> calculate_weld(const Mesh &mesh, const WeldModifier
if (wmd.mode == MOD_WELD_MODE_ALL) {
if (!vertex_group.is_empty()) {
Vector<int64_t> selected_indices = selected_indices_from_vertex_group(
vertex_group, defgrp_index, invert);
IndexMaskMemory memory;
const IndexMask selected_indices = selected_indices_from_vertex_group(
vertex_group, defgrp_index, invert, memory);
return blender::geometry::mesh_merge_by_distance_all(
mesh, IndexMask(selected_indices), wmd.merge_dist);
}

View File

@ -282,13 +282,13 @@ class SampleCurveFunction : public mf::MultiFunction {
auto return_default = [&]() {
if (!sampled_positions.is_empty()) {
sampled_positions.fill_indices(mask, {0, 0, 0});
index_mask::masked_fill(sampled_positions, {0, 0, 0}, mask);
}
if (!sampled_tangents.is_empty()) {
sampled_tangents.fill_indices(mask, {0, 0, 0});
index_mask::masked_fill(sampled_tangents, {0, 0, 0}, mask);
}
if (!sampled_normals.is_empty()) {
sampled_normals.fill_indices(mask, {0, 0, 0});
index_mask::masked_fill(sampled_normals, {0, 0, 0}, mask);
}
};
@ -325,18 +325,18 @@ class SampleCurveFunction : public mf::MultiFunction {
auto fill_invalid = [&](const IndexMask mask) {
if (!sampled_positions.is_empty()) {
sampled_positions.fill_indices(mask, float3(0));
index_mask::masked_fill(sampled_positions, float3(0), mask);
}
if (!sampled_tangents.is_empty()) {
sampled_tangents.fill_indices(mask, float3(0));
index_mask::masked_fill(sampled_tangents, float3(0), mask);
}
if (!sampled_normals.is_empty()) {
sampled_normals.fill_indices(mask, float3(0));
index_mask::masked_fill(sampled_normals, float3(0), mask);
}
if (!sampled_values.is_empty()) {
attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
using T = decltype(dummy);
sampled_values.typed<T>().fill_indices(mask, {});
index_mask::masked_fill<T>(sampled_values.typed<T>(), {}, mask);
});
}
};
@ -367,16 +367,14 @@ class SampleCurveFunction : public mf::MultiFunction {
if (!sampled_tangents.is_empty()) {
length_parameterize::interpolate_to_masked<float3>(
evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
for (const int64_t i : mask) {
sampled_tangents[i] = math::normalize(sampled_tangents[i]);
}
mask.foreach_index(
[&](const int64_t i) { sampled_tangents[i] = math::normalize(sampled_tangents[i]); });
}
if (!sampled_normals.is_empty()) {
length_parameterize::interpolate_to_masked<float3>(
evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
for (const int64_t i : mask) {
sampled_normals[i] = math::normalize(sampled_normals[i]);
}
mask.foreach_index(
[&](const int64_t i) { sampled_normals[i] = math::normalize(sampled_normals[i]); });
}
if (!sampled_values.is_empty()) {
const IndexRange points = points_by_curve[curve_i];
@ -406,7 +404,7 @@ class SampleCurveFunction : public mf::MultiFunction {
Vector<int64_t> invalid_indices;
MultiValueMap<int, int64_t> indices_per_curve;
devirtualize_varray(curve_indices, [&](const auto curve_indices) {
for (const int64_t i : mask) {
mask.foreach_index([&](const int64_t i) {
const int curve_i = curve_indices[i];
if (curves.curves_range().contains(curve_i)) {
indices_per_curve.add(curve_i, i);
@ -414,13 +412,15 @@ class SampleCurveFunction : public mf::MultiFunction {
else {
invalid_indices.append(i);
}
}
});
});
IndexMaskMemory memory;
for (const int curve_i : indices_per_curve.keys()) {
sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
sample_curve(curve_i,
IndexMask::from_indices<int64_t>(indices_per_curve.lookup(curve_i), memory));
}
fill_invalid(IndexMask(invalid_indices));
fill_invalid(IndexMask::from_indices<int64_t>(invalid_indices, memory));
}
}

View File

@ -63,10 +63,12 @@ static void set_handle_type(bke::CurvesGeometry &curves,
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
curves.handle_types_left_for_write().fill_indices(selection, new_handle_type);
index_mask::masked_fill<int8_t>(
curves.handle_types_left_for_write(), new_handle_type, selection);
}
if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
curves.handle_types_right_for_write().fill_indices(selection, new_handle_type);
index_mask::masked_fill<int8_t>(
curves.handle_types_right_for_write(), new_handle_type, selection);
}
/* Eagerly calculate automatically derived handle positions if necessary. */

View File

@ -74,16 +74,9 @@ static void save_selection_as_attribute(Mesh &mesh,
MutableAttributeAccessor attributes = mesh.attributes_for_write();
BLI_assert(!attributes.contains(id));
SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain);
/* Rely on the new attribute being zeroed by default. */
BLI_assert(!attribute.span.as_span().contains(true));
if (selection.is_range()) {
attribute.span.slice(selection.as_range()).fill(true);
}
else {
attribute.span.fill_indices(selection, true);
}
SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_only_span<bool>(id,
domain);
selection.to_bools(attribute.span);
attribute.finish();
}
@ -374,16 +367,19 @@ static void extrude_mesh_edges(Mesh &mesh,
if (!edge_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets);
for (const int i_edge : edge_selection) {
edge_selection.foreach_index([&](const int i_edge) {
const MEdge &edge = orig_edges[i_edge];
const float3 offset = edge_offsets[i_edge];
mixer.mix_in(edge.v1, offset);
mixer.mix_in(edge.v2, offset);
}
});
mixer.finalize();
}
const VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, edge_selection.indices());
Vector<int> edge_selection_indices(edge_selection.size());
edge_selection.to_indices(edge_selection_indices.as_mutable_span());
const VectorSet<int> new_vert_indices = vert_indices_from_edges<int>(mesh,
edge_selection_indices);
const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
/* The extruded edges connect the original and duplicate edges. */
@ -642,10 +638,8 @@ static void extrude_mesh_face_regions(Mesh &mesh,
return;
}
Array<bool> poly_selection_array(orig_polys.size(), false);
for (const int i_poly : poly_selection) {
poly_selection_array[i_poly] = true;
}
Array<bool> poly_selection_array(orig_polys.size());
poly_selection.to_bools(poly_selection_array);
/* Mix the offsets from the face domain to the vertex domain. Evaluate on the face domain above
* in order to be consistent with the selection, and to use the face normals rather than vertex
@ -654,13 +648,13 @@ static void extrude_mesh_face_regions(Mesh &mesh,
if (!poly_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets);
for (const int i_poly : poly_selection) {
poly_selection.foreach_index([&](const int i_poly) {
const MPoly &poly = orig_polys[i_poly];
const float3 offset = poly_offsets[i_poly];
for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
mixer.mix_in(loop.v, offset);
}
}
});
mixer.finalize();
}
@ -672,12 +666,12 @@ static void extrude_mesh_face_regions(Mesh &mesh,
* Start the size at one vert per poly to reduce unnecessary reallocation. */
VectorSet<int> all_selected_verts;
all_selected_verts.reserve(orig_polys.size());
for (const int i_poly : poly_selection) {
poly_selection.foreach_index([&](const int i_poly) {
const MPoly &poly = orig_polys[i_poly];
for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
all_selected_verts.add(loop.v);
}
}
});
/* Edges inside of an extruded region that are also attached to deselected edges. They must be
* duplicated in order to leave the old edge attached to the unchanged deselected faces. */
@ -806,7 +800,7 @@ static void extrude_mesh_face_regions(Mesh &mesh,
}
/* Connect the selected faces to the extruded or duplicated edges and the new vertices. */
for (const int i_poly : poly_selection) {
poly_selection.foreach_index([&](const int i_poly) {
const MPoly &poly = polys[i_poly];
for (MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
const int i_new_vert = new_vert_indices.index_of_try(loop.v);
@ -824,7 +818,7 @@ static void extrude_mesh_face_regions(Mesh &mesh,
loop.e = new_inner_edge_range[i_new_inner_edge];
}
}
}
});
/* Create the faces on the sides of extruded regions. */
for (const int i : boundary_edge_indices.index_range()) {

View File

@ -244,7 +244,7 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet<int> disjoint_set(mesh.totvert);
for (const int poly_index : face_selection) {
face_selection.foreach_index([&](const int poly_index) {
const MPoly &poly = polys[poly_index];
const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const int loop_index : IndexRange(poly.totloop - 1)) {
@ -253,7 +253,7 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
disjoint_set.join(v1, v2);
}
disjoint_set.join(poly_loops.first().v, poly_loops.last().v);
}
});
VectorSet<int> island_ids;
Vector<ElementIsland> islands;
@ -261,7 +261,7 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
islands.reserve(face_selection.size());
/* Gather all of the face indices in each island into separate vectors. */
for (const int poly_index : face_selection) {
face_selection.foreach_index([&](const int poly_index) {
const MPoly &poly = polys[poly_index];
const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
const int island_id = disjoint_set.find_root(poly_loops[0].v);
@ -271,7 +271,7 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
}
ElementIsland &island = islands[island_index];
island.element_indices.append(poly_index);
}
});
return islands;
}
@ -340,10 +340,10 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet<int> disjoint_set(mesh.totvert);
for (const int edge_index : edge_selection) {
edge_selection.foreach_index([&](const int edge_index) {
const MEdge &edge = edges[edge_index];
disjoint_set.join(edge.v1, edge.v2);
}
});
VectorSet<int> island_ids;
Vector<ElementIsland> islands;
@ -351,7 +351,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
islands.reserve(edge_selection.size());
/* Gather all of the edge indices in each island into separate vectors. */
for (const int edge_index : edge_selection) {
edge_selection.foreach_index([&](const int edge_index) {
const MEdge &edge = edges[edge_index];
const int island_id = disjoint_set.find_root(edge.v1);
const int island_index = island_ids.index_of_or_add(island_id);
@ -360,7 +360,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
}
ElementIsland &island = islands[island_index];
island.element_indices.append(edge_index);
}
});
return islands;
}

View File

@ -45,7 +45,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
}
}
}
const int grain_size = 10000;
const GrainSize grain_size{10000};
switch (component.type()) {
case GEO_COMPONENT_TYPE_CURVE: {
@ -62,16 +62,13 @@ static void set_computed_position_and_offset(GeometryComponent &component,
MutableVArraySpan<float3> out_positions_span = positions.varray;
devirtualize_varray2(
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
const float3 new_position = in_positions[i] + in_offsets[i];
const float3 delta = new_position - out_positions_span[i];
handle_right_attribute.span[i] += delta;
handle_left_attribute.span[i] += delta;
out_positions_span[i] = new_position;
}
});
selection.foreach_index_optimized(grain_size, [&](const int i) {
const float3 new_position = in_positions[i] + in_offsets[i];
const float3 delta = new_position - out_positions_span[i];
handle_right_attribute.span[i] += delta;
handle_left_attribute.span[i] += delta;
out_positions_span[i] = new_position;
});
});
out_positions_span.save();
@ -90,23 +87,16 @@ static void set_computed_position_and_offset(GeometryComponent &component,
MutableVArraySpan<float3> out_positions_span = positions.varray;
if (positions_are_original) {
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
out_positions_span[i] += in_offsets[i];
}
});
selection.foreach_index_optimized(
grain_size, [&](const int i) { out_positions_span[i] += in_offsets[i]; });
});
}
else {
devirtualize_varray2(
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
out_positions_span[i] = in_positions[i] + in_offsets[i];
}
});
selection.foreach_index_optimized(grain_size, [&](const int i) {
out_positions_span[i] = in_positions[i] + in_offsets[i];
});
});
}
out_positions_span.save();

View File

@ -159,10 +159,10 @@ class ClampWrapperFunction : public mf::MultiFunction {
/* This has actually been initialized in the call above. */
MutableSpan<float> results = params.uninitialized_single_output<float>(output_param_index);
for (const int i : mask) {
mask.foreach_index_optimized([&](const int i) {
float &value = results[i];
CLAMP(value, 0.0f, 1.0f);
}
});
}
};

View File

@ -394,22 +394,20 @@ class MixColorFunction : public mf::MultiFunction {
3, "Result");
if (clamp_factor_) {
for (int64_t i : mask) {
mask.foreach_index_optimized([&](const int64_t i) {
results[i] = col1[i];
ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
}
});
}
else {
for (int64_t i : mask) {
mask.foreach_index_optimized([&](const int64_t i) {
results[i] = col1[i];
ramp_blend(blend_type_, results[i], fac[i], col2[i]);
}
});
}
if (clamp_result_) {
for (int64_t i : mask) {
clamp_v3(results[i], 0.0f, 1.0f);
}
mask.foreach_index_optimized([&](const int64_t i) { clamp_v3(results[i], 0.0f, 1.0f); });
}
}
};

View File

@ -121,15 +121,13 @@ class MixRGBFunction : public mf::MultiFunction {
MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
3, "Color");
for (int64_t i : mask) {
mask.foreach_index([&](const int64_t i) {
results[i] = col1[i];
ramp_blend(type_, results[i], clamp_f(fac[i], 0.0f, 1.0f), col2[i]);
}
});
if (clamp_) {
for (int64_t i : mask) {
clamp_v3(results[i], 0.0f, 1.0f);
}
mask.foreach_index([&](const int64_t i) { clamp_v3(results[i], 0.0f, 1.0f); });
}
}
};

View File

@ -51,12 +51,12 @@ class SeparateRGBFunction : public mf::MultiFunction {
MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G");
MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B");
for (int64_t i : mask) {
mask.foreach_index([&](const int64_t i) {
ColorGeometry4f color = colors[i];
rs[i] = color.r;
gs[i] = color.g;
bs[i] = color.b;
}
});
}
};