Cleanup: Extract utility for counting indices

This utility counts the number of occurrences of each index in an array.
This is used for building mesh topology maps offsets, or for counting
the number of connected elements. Some users are geometry nodes,
the subdivision draw cache, and mesh to curve conversion.

See #109628
This commit is contained in:
Hans Goudey
2023-07-03 18:47:03 -04:00
parent 91d15a3613
commit a3bfd6e20d
14 changed files with 60 additions and 102 deletions

View File

@@ -301,10 +301,7 @@ namespace blender::bke::mesh {
static Array<int> create_reverse_offsets(const Span<int> indices, const int items_num)
{
Array<int> offsets(items_num + 1, 0);
for (const int i : indices) {
offsets[i]++;
}
offset_indices::accumulate_counts_to_offsets(offsets);
offset_indices::build_reverse_offsets(indices, offsets);
return offsets;
}

View File

@@ -145,6 +145,16 @@ inline void gather(const VArray<T> &src,
});
}
/**
* Count the number of occurences of each index.
* \param indices: The indices to count.
* \param counts: The number of occurrences of each index. Typically initialized to zero.
* Must be large enough to contain the maximum index.
*
* \note The memory referenced by the two spans must not overlap.
*/
void count_indices(Span<int> indices, MutableSpan<int> counts);
void invert_booleans(MutableSpan<bool> span);
enum class BooleanMix {

View File

@@ -4,6 +4,8 @@
#include "BLI_array_utils.hh"
#include "atomic_ops.h"
namespace blender::array_utils {
void copy(const GVArray &src, GMutableSpan dst, const int64_t grain_size)
@@ -45,6 +47,13 @@ void gather(const GSpan src, const IndexMask &indices, GMutableSpan dst, const i
gather(GVArray::ForSpan(src), indices, dst, grain_size);
}
void count_indices(const Span<int> indices, MutableSpan<int> counts)
{
for (const int i : indices) {
counts[i]++;
}
}
void invert_booleans(MutableSpan<bool> span)
{
threading::parallel_for(span.index_range(), 4096, [&](IndexRange range) {

View File

@@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_offset_indices.hh"
#include "BLI_task.hh"
@@ -63,9 +64,7 @@ void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
void build_reverse_offsets(const Span<int> indices, MutableSpan<int> offsets)
{
BLI_assert(std::all_of(offsets.begin(), offsets.end(), [](int value) { return value == 0; }));
for (const int i : indices) {
offsets[i]++;
}
array_utils::count_indices(indices, offsets);
offset_indices::accumulate_counts_to_offsets(offsets);
}

View File

@@ -1133,20 +1133,13 @@ static void build_vertex_face_adjacency_maps(DRWSubdivCache *cache)
cache->subdiv_vertex_face_adjacency_offsets = gpu_vertbuf_create_from_format(
get_origindex_format(), cache->num_subdiv_verts + 1);
int *vertex_offsets = (int *)GPU_vertbuf_get_data(cache->subdiv_vertex_face_adjacency_offsets);
memset(vertex_offsets, 0, sizeof(int) * cache->num_subdiv_verts + 1);
blender::MutableSpan<int> vertex_offsets(
static_cast<int *>(GPU_vertbuf_get_data(cache->subdiv_vertex_face_adjacency_offsets)),
cache->num_subdiv_verts + 1);
vertex_offsets.fill(0);
for (int i = 0; i < cache->num_subdiv_loops; i++) {
vertex_offsets[cache->subdiv_loop_subdiv_vert_index[i]]++;
}
int ofs = vertex_offsets[0];
vertex_offsets[0] = 0;
for (uint i = 1; i < cache->num_subdiv_verts + 1; i++) {
int tmp = vertex_offsets[i];
vertex_offsets[i] = ofs;
ofs += tmp;
}
blender::offset_indices::build_reverse_offsets(
{cache->subdiv_loop_subdiv_vert_index, cache->num_subdiv_loops}, vertex_offsets);
cache->subdiv_vertex_face_adjacency = gpu_vertbuf_create_from_format(get_origindex_format(),
cache->num_subdiv_loops);

View File

@@ -13,6 +13,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_blenlib.h"
#include "BLI_dial_2d.h"
#include "BLI_ghash.h"
@@ -6158,20 +6159,12 @@ void SCULPT_boundary_info_ensure(Object *object)
}
Mesh *base_mesh = BKE_mesh_from_object(object);
const blender::Span<int2> edges = base_mesh->edges();
const OffsetIndices polys = base_mesh->polys();
const Span<int> corner_edges = base_mesh->corner_edges();
ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
int *adjacent_faces_edge_count = static_cast<int *>(
MEM_calloc_arrayN(base_mesh->totedge, sizeof(int), "Adjacent face edge count"));
for (const int i : polys.index_range()) {
for (const int edge : corner_edges.slice(polys[i])) {
adjacent_faces_edge_count[edge]++;
}
}
Array<int> adjacent_faces_edge_count(base_mesh->totedge, 0);
array_utils::count_indices(base_mesh->corner_edges(), adjacent_faces_edge_count);
const blender::Span<int2> edges = base_mesh->edges();
for (const int e : edges.index_range()) {
if (adjacent_faces_edge_count[e] < 2) {
const int2 &edge = edges[e];
@@ -6179,8 +6172,6 @@ void SCULPT_boundary_info_ensure(Object *object)
BLI_BITMAP_SET(ss->vertex_info.boundary, edge[1], true);
}
}
MEM_freeN(adjacent_faces_edge_count);
}
void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)

View File

@@ -115,10 +115,7 @@ BLI_NOINLINE static CurveFromEdgesOutput edges_to_curve_point_indices(const int
{
/* Compute the number of edges connecting to each vertex. */
Array<int> neighbor_offsets_data(verts_num + 1, 0);
for (const int vert : edges.cast<int>()) {
neighbor_offsets_data[vert]++;
}
offset_indices::accumulate_counts_to_offsets(neighbor_offsets_data);
offset_indices::build_reverse_offsets(edges.cast<int>(), neighbor_offsets_data);
const OffsetIndices<int> neighbor_offsets(neighbor_offsets_data);
/* Use as an index into the "neighbor group" for each vertex. */

View File

@@ -42,6 +42,7 @@
#include "BLI_utildefines.h"
#include "BLI_array_utils.hh"
#include "BLI_bitmap.h"
#include "BLI_heap_simple.h"
#include "BLI_math.h"
@@ -911,10 +912,7 @@ static Mesh *subdivide_base(const Mesh *orig)
/* Get degree of all vertices */
blender::Array<int> degree(orig_vert_num, 0);
for (i = 0; i < orig_edge_num; i++) {
degree[orig_edges[i][0]]++;
degree[orig_edges[i][1]]++;
}
blender::array_utils::count_indices(orig_edges.cast<int>(), degree);
/* Per edge, store how many subdivisions are needed */
blender::Array<int> edge_subd(orig_edge_num, 0);

View File

@@ -2,8 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_array_utils.hh"
#include "BKE_mesh.hh"
@@ -30,14 +29,10 @@ class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput {
const eAttrDomain domain,
const IndexMask & /*mask*/) const final
{
const Span<int> corner_edges = mesh.corner_edges();
Array<int> face_count(mesh.totedge, 0);
for (const int edge : corner_edges) {
face_count[edge]++;
}
Array<int> counts(mesh.totedge, 0);
array_utils::count_indices(mesh.corner_edges(), counts);
return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
VArray<int>::ForContainer(std::move(counts)), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override

View File

@@ -2,8 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_array_utils.hh"
#include "BKE_mesh.hh"
@@ -27,9 +26,7 @@ static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttr
const Span<int> corner_edges = mesh.corner_edges();
Array<int> edge_count(mesh.totedge, 0);
for (const int edge : corner_edges) {
edge_count[edge]++;
}
array_utils::count_indices(corner_edges, edge_count);
Array<int> poly_count(polys.size(), 0);
for (const int poly_index : polys.index_range()) {

View File

@@ -2,8 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_array_utils.hh"
#include "BKE_mesh.hh"
@@ -23,20 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
.description("Number of faces that contain the vertex");
}
static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
const Span<int2> edges = mesh.edges();
if (domain == ATTR_DOMAIN_POINT) {
Array<int> counts(mesh.totvert, 0);
for (const int i : edges.index_range()) {
counts[edges[i][0]]++;
counts[edges[i][1]]++;
}
return VArray<int>::ForContainer(std::move(counts));
}
return {};
}
class VertexCountFieldInput final : public bke::MeshFieldInput {
public:
VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
@@ -48,7 +33,12 @@ class VertexCountFieldInput final : public bke::MeshFieldInput {
const eAttrDomain domain,
const IndexMask & /*mask*/) const final
{
return construct_vertex_count_gvarray(mesh, domain);
if (domain != ATTR_DOMAIN_POINT) {
return {};
}
Array<int> counts(mesh.totvert, 0);
array_utils::count_indices(mesh.edges().cast<int>(), counts);
return VArray<int>::ForContainer(std::move(counts));
}
uint64_t hash() const override
@@ -68,19 +58,6 @@ class VertexCountFieldInput final : public bke::MeshFieldInput {
}
};
static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
const Span<int> corner_verts = mesh.corner_verts();
if (domain == ATTR_DOMAIN_POINT) {
Array<int> vertices(mesh.totvert, 0);
for (const int vert : corner_verts) {
vertices[vert]++;
}
return VArray<int>::ForContainer(std::move(vertices));
}
return {};
}
class VertexFaceCountFieldInput final : public bke::MeshFieldInput {
public:
VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
@@ -92,7 +69,12 @@ class VertexFaceCountFieldInput final : public bke::MeshFieldInput {
const eAttrDomain domain,
const IndexMask & /*mask*/) const final
{
return construct_face_count_gvarray(mesh, domain);
if (domain != ATTR_DOMAIN_POINT) {
return {};
}
Array<int> counts(mesh.totvert, 0);
array_utils::count_indices(mesh.corner_verts(), counts);
return VArray<int>::ForContainer(std::move(counts));
}
uint64_t hash() const override

View File

@@ -5,7 +5,7 @@
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.h"
#include "BLI_task.hh"
#include "BLI_array_utils.hh"
#include "node_geometry_util.hh"
@@ -151,11 +151,8 @@ class CornersOfEdgeCountInput final : public bke::MeshFieldInput {
if (domain != ATTR_DOMAIN_EDGE) {
return {};
}
const Span<int> corner_edges = mesh.corner_edges();
Array<int> counts(mesh.totedge, 0);
for (const int i : corner_edges.index_range()) {
counts[corner_edges[i]]++;
}
array_utils::count_indices(mesh.corner_edges(), counts);
return VArray<int>::ForContainer(std::move(counts));
}

View File

@@ -5,7 +5,7 @@
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.h"
#include "BLI_task.hh"
#include "BLI_array_utils.hh"
#include "node_geometry_util.hh"
@@ -158,11 +158,8 @@ class CornersOfVertCountInput final : public bke::MeshFieldInput {
if (domain != ATTR_DOMAIN_POINT) {
return {};
}
const Span<int> corner_verts = mesh.corner_verts();
Array<int> counts(mesh.totvert, 0);
for (const int i : corner_verts.index_range()) {
counts[corner_verts[i]]++;
}
array_utils::count_indices(mesh.corner_verts(), counts);
return VArray<int>::ForContainer(std::move(counts));
}

View File

@@ -5,7 +5,7 @@
#include "BKE_mesh.hh"
#include "BKE_mesh_mapping.h"
#include "BLI_task.hh"
#include "BLI_array_utils.hh"
#include "node_geometry_util.hh"
@@ -160,12 +160,8 @@ class EdgesOfVertCountInput final : public bke::MeshFieldInput {
if (domain != ATTR_DOMAIN_POINT) {
return {};
}
const Span<int2> edges = mesh.edges();
Array<int> counts(mesh.totvert, 0);
for (const int i : edges.index_range()) {
counts[edges[i][0]]++;
counts[edges[i][1]]++;
}
array_utils::count_indices(mesh.edges().cast<int>(), counts);
return VArray<int>::ForContainer(std::move(counts));
}