Mesh: Add cached edge to corner topology map

Similar to other topology maps that are already cached. This is the
Mesh equivalent of iterating the radial cycle around a BMEdge, which is
needed for porting code from BMesh to Mesh. Caching the map will avoid
performance regressions during that transition.

Pull Request: https://projects.blender.org/blender/blender/pulls/164059
This commit is contained in:
Hans Goudey 2026-09-17 16:04:38 +02:00 • committed by Hans Goudey
parent b988d827b6
commit ceaeebc06e
4 changed files with 31 additions and 0 deletions

View file

@ -261,6 +261,10 @@ struct MeshRuntime {
SharedCache<Array<int>> vert_to_corner_map_cache;
/** Cache of face indices for each face corner. */
SharedCache<Array<int>> corner_to_face_map_cache;
/** Cache of offsets for the edge to corner map. */
SharedCache<Array<int>> edge_to_corner_offset_cache;
/** Cache of indices for the edge to corner map. */
SharedCache<Array<int>> edge_to_corner_map_cache;
/** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */
SharedCache<LooseGeomCache> loose_edges_cache;
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */

View file

@ -159,6 +159,8 @@ static void mesh_copy_data(Main *bmain,
mesh_dst->runtime->vert_to_face_map_cache = mesh_src->runtime->vert_to_face_map_cache;
mesh_dst->runtime->vert_to_corner_map_cache = mesh_src->runtime->vert_to_corner_map_cache;
mesh_dst->runtime->corner_to_face_map_cache = mesh_src->runtime->corner_to_face_map_cache;
mesh_dst->runtime->edge_to_corner_offset_cache = mesh_src->runtime->edge_to_corner_offset_cache;
mesh_dst->runtime->edge_to_corner_map_cache = mesh_src->runtime->edge_to_corner_map_cache;
mesh_dst->runtime->bvh_cache_edges = mesh_src->runtime->bvh_cache_edges;
mesh_dst->runtime->bvh_cache_faces = mesh_src->runtime->bvh_cache_faces;
mesh_dst->runtime->bvh_cache_corner_tris = mesh_src->runtime->bvh_cache_corner_tris;

View file

@ -146,6 +146,20 @@ GroupedSpan<int> Mesh::vert_to_corner_map() const
return {offsets, this->runtime->vert_to_corner_map_cache.data()};
}
GroupedSpan<int> Mesh::edge_to_corner_map() const
{
this->runtime->edge_to_corner_offset_cache.ensure([&](Array<int> &r_data) {
r_data = Array<int>(this->edges_num + 1, 0);
offset_indices::build_reverse_offsets(this->corner_edges(), r_data);
});
const OffsetIndices<int> offsets(this->runtime->edge_to_corner_offset_cache.data());
this->runtime->edge_to_corner_map_cache.ensure([&](Array<int> &r_data) {
r_data.reinitialize(offsets.total_size());
offset_indices::reverse_indices_in_groups(this->corner_edges(), offsets, r_data);
});
return {offsets, this->runtime->edge_to_corner_map_cache.data()};
}
const IndexMask &Mesh::loose_verts() const
{
using namespace blender::bke;
@ -310,6 +324,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
mesh->runtime->vert_to_face_map_cache.tag_dirty();
mesh->runtime->vert_to_corner_map_cache.tag_dirty();
mesh->runtime->corner_to_face_map_cache.tag_dirty();
mesh->runtime->edge_to_corner_offset_cache.tag_dirty();
mesh->runtime->edge_to_corner_map_cache.tag_dirty();
mesh->runtime->vert_normals_cache.tag_dirty();
mesh->runtime->vert_normals_true_cache.tag_dirty();
mesh->runtime->face_normals_cache.tag_dirty();
@ -338,6 +354,8 @@ void Mesh::tag_edges_split()
this->runtime->vert_to_face_offset_cache.tag_dirty();
this->runtime->vert_to_face_map_cache.tag_dirty();
this->runtime->vert_to_corner_map_cache.tag_dirty();
this->runtime->edge_to_corner_offset_cache.tag_dirty();
this->runtime->edge_to_corner_map_cache.tag_dirty();
if (this->runtime->loose_edges_cache.is_cached() &&
!this->runtime->loose_edges_cache.data().mask.is_empty())
{
@ -380,6 +398,9 @@ void Mesh::tag_face_winding_changed()
this->runtime->face_normals_true_cache.tag_dirty();
this->runtime->corner_normals_cache.tag_dirty();
this->runtime->vert_to_corner_map_cache.tag_dirty();
/* Reversing faces swaps edges stored for each of its corners, but the number of corners using
* each edge doesn't change, so only the indices are recalculated, not the offsets. */
this->runtime->edge_to_corner_map_cache.tag_dirty();
this->runtime->shrinkwrap_boundary_cache.tag_dirty();
}

View file

@ -465,6 +465,10 @@ struct Mesh {
* Cached map from each vertex to the faces using it.
*/
GroupedSpan<int> vert_to_face_map() const;
/**
* Cached map from each edge to the face corners using it.
*/
GroupedSpan<int> edge_to_corner_map() const;
/**
* Cached information about loose edges, calculated lazily when necessary.