From 4e94db97e2baafd6e05c91ec0ac7744f73b95509 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 30 Aug 2023 23:41:59 +0200 Subject: [PATCH] Mesh: Add three cached topology maps Add three cached topology maps to `Mesh`, to avoid computations when mesh data isn't changed. Choosing the right maps to cache is a bit arbitrary, but generally we have to start somewhere. The limiting factor is memory usage (all the new caches combined have a comparable footprint to a UV map). For now, the caches added are: - Vertex to face corner - Vertex to face - Face corner to face These caches are used in quite a few places already; - Face corner normal calculation - UV value merging - Setting sharp edges from face angles - Data transfer modifier - Voxel remesh attribute remapping - Sculpt mode painting - Sculpt mode normal calculation - Vertex paint mode - Split edges geometry node - Mesh topology geometry nodes Caching topology maps means they don't have to be rebuilt every time they're used. Meshes copied but without topology changes can share the cache, further reducing re-computations. For example, FPS with a large mesh using the "Corners of Vertex" node went from 1.8 to 2.3. Entering sculpt mode is slightly faster too. There is some obvious work for future commits: - Use caches in attribute domain interpolation - More multithreading of second phase of map building - Update/build caches eagerly in some geometry nodes Pull Request: https://projects.blender.org/blender/blender/pulls/107816 --- source/blender/blenkernel/BKE_mesh.hh | 2 +- source/blender/blenkernel/BKE_mesh_mapping.hh | 5 ++ source/blender/blenkernel/BKE_mesh_types.hh | 11 ++++ source/blender/blenkernel/BKE_paint.hh | 9 +-- .../blenkernel/intern/data_transfer.cc | 2 +- source/blender/blenkernel/intern/key.cc | 2 +- source/blender/blenkernel/intern/mesh.cc | 6 +- source/blender/blenkernel/intern/mesh_fair.cc | 9 +-- .../blender/blenkernel/intern/mesh_mapping.cc | 29 ++++++--- .../intern/mesh_merge_customdata.cc | 5 +- .../blender/blenkernel/intern/mesh_mirror.cc | 2 +- .../blender/blenkernel/intern/mesh_normals.cc | 19 +----- .../blender/blenkernel/intern/mesh_remap.cc | 21 ++----- .../blenkernel/intern/mesh_remesh_voxel.cc | 18 +----- .../blender/blenkernel/intern/mesh_runtime.cc | 62 +++++++++++++++++++ source/blender/blenkernel/intern/multires.cc | 4 -- .../intern/multires_reshape_apply_base.cc | 9 +-- .../blenkernel/intern/multires_unsubdivide.cc | 3 +- .../blenkernel/intern/multires_unsubdivide.hh | 2 +- source/blender/blenkernel/intern/paint.cc | 27 +------- .../draw_cache_extract_mesh_render_data.cc | 2 +- source/blender/editors/mesh/mesh_data.cc | 2 + .../editors/sculpt_paint/paint_vertex.cc | 10 +-- .../geometry/intern/mesh_split_edges.cc | 7 +-- source/blender/makesdna/DNA_mesh_types.h | 17 +++++ .../modifiers/intern/MOD_normal_edit.cc | 2 +- .../modifiers/intern/MOD_weighted_normal.cc | 2 +- .../geometry/nodes/node_geo_dual_mesh.cc | 13 ++-- ...ode_geo_mesh_topology_corners_of_vertex.cc | 7 +-- .../node_geo_mesh_topology_edges_of_corner.cc | 8 +-- .../node_geo_mesh_topology_face_of_corner.cc | 13 ++-- ...geo_mesh_topology_offset_corner_in_face.cc | 4 +- 32 files changed, 172 insertions(+), 162 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.hh b/source/blender/blenkernel/BKE_mesh.hh index 728877a70c2..e2fa49146f2 100644 --- a/source/blender/blenkernel/BKE_mesh.hh +++ b/source/blender/blenkernel/BKE_mesh.hh @@ -146,7 +146,6 @@ short2 lnor_space_custom_normal_to_data(const CornerNormalSpace &lnor_space, * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry * (splitting edges). * - * \param loop_to_face_map: Optional pre-created map from corners to their face. * \param sharp_edges: Optional array of sharp edge tags, used to split the evaluated normals on * each side of the edge. * \param r_lnors_spacearr: Optional return data filled with information about the custom @@ -204,6 +203,7 @@ void edges_sharp_from_angle_set(OffsetIndices faces, Span corner_verts, Span corner_edges, Span face_normals, + Span loop_to_face, const bool *sharp_faces, const float split_angle, MutableSpan sharp_edges); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.hh b/source/blender/blenkernel/BKE_mesh_mapping.hh index 83dcfe09586..5c7d0220378 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.hh +++ b/source/blender/blenkernel/BKE_mesh_mapping.hh @@ -280,12 +280,17 @@ GroupedSpan build_vert_to_edge_map(Span edges, Array &r_offsets, Array &r_indices); +void build_vert_to_face_indices(OffsetIndices faces, + Span corner_verts, + OffsetIndices offsets, + MutableSpan face_indices); GroupedSpan build_vert_to_face_map(OffsetIndices faces, Span corner_verts, int verts_num, Array &r_offsets, Array &r_indices); +Array build_vert_to_corner_indices(Span corner_verts, OffsetIndices offsets); GroupedSpan build_vert_to_loop_map(Span corner_verts, int verts_num, Array &r_offsets, diff --git a/source/blender/blenkernel/BKE_mesh_types.hh b/source/blender/blenkernel/BKE_mesh_types.hh index 5dd07c2e268..416a4500a67 100644 --- a/source/blender/blenkernel/BKE_mesh_types.hh +++ b/source/blender/blenkernel/BKE_mesh_types.hh @@ -140,6 +140,17 @@ struct MeshRuntime { SharedCache> vert_normals_cache; SharedCache> face_normals_cache; + /** + * Cache of offsets for vert to face/corner maps. The same offsets array is used to group + * indices for both the vertex to face and vertex to corner maps. + */ + SharedCache> vert_to_face_offset_cache; + /** Cache of indices for vert to face map. */ + SharedCache> vert_to_face_map_cache; + /** Cache of indices for vert to corner map. */ + SharedCache> vert_to_corner_map_cache; + /** Cache of face indices for each face corner. */ + SharedCache> corner_to_face_map_cache; /** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */ SharedCache loose_edges_cache; /** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */ diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index b91a811fc37..228165afee2 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -267,12 +267,7 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain /** Used for both vertex color and weight paint. */ struct SculptVertexPaintGeomMap { - blender::Array vert_to_loop_offsets; - blender::Array vert_to_loop_indices; blender::GroupedSpan vert_to_loop; - - blender::Array vert_to_face_offsets; - blender::Array vert_to_face_indices; blender::GroupedSpan vert_to_face; }; @@ -591,9 +586,7 @@ struct SculptSession { float *vmask; /* Mesh connectivity maps. */ - /* Vertices to adjacent faces. */ - blender::Array vert_to_face_offsets; - blender::Array vert_to_face_indices; + /* Vertices to adjacent polys. */ blender::GroupedSpan pmap; /* Edges to adjacent faces. */ diff --git a/source/blender/blenkernel/intern/data_transfer.cc b/source/blender/blenkernel/intern/data_transfer.cc index ea74e99c56f..313b4851888 100644 --- a/source/blender/blenkernel/intern/data_transfer.cc +++ b/source/blender/blenkernel/intern/data_transfer.cc @@ -393,7 +393,7 @@ static void data_transfer_dtdata_type_preprocess(const Mesh *me_src, me_dst->faces(), me_dst->corner_verts(), me_dst->corner_edges(), - {}, + me_dst->corner_to_face_map(), me_dst->vert_normals(), me_dst->face_normals(), sharp_edges, diff --git a/source/blender/blenkernel/intern/key.cc b/source/blender/blenkernel/intern/key.cc index 2dcd8588c37..38b725f761d 100644 --- a/source/blender/blenkernel/intern/key.cc +++ b/source/blender/blenkernel/intern/key.cc @@ -2278,7 +2278,7 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb, faces, corner_verts, corner_edges, - {}, + mesh->corner_to_face_map(), {reinterpret_cast(vert_normals), mesh->totvert}, {reinterpret_cast(face_normals), faces.size()}, sharp_edges, diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 17598efdefd..14f6bcb0212 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -139,6 +139,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache; mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache; mesh_dst->runtime->looptri_faces_cache = mesh_src->runtime->looptri_faces_cache; + mesh_dst->runtime->vert_to_face_offset_cache = mesh_src->runtime->vert_to_face_offset_cache; + 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; /* Only do tessface if we have no faces. */ const bool do_tessface = ((mesh_src->totface_legacy != 0) && (mesh_src->faces_num == 0)); @@ -1778,7 +1782,7 @@ void BKE_mesh_calc_normals_split_ex(const Mesh *mesh, mesh->faces(), mesh->corner_verts(), mesh->corner_edges(), - {}, + mesh->corner_to_face_map(), mesh->vert_normals(), mesh->face_normals(), sharp_edges, diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index b4cd12098de..e589fb0f58f 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -204,8 +204,7 @@ class MeshFairingContext : public FairingContext { faces = mesh->faces(); corner_verts_ = mesh->corner_verts(); corner_edges_ = mesh->corner_edges(); - vlmap_ = blender::bke::mesh::build_vert_to_loop_map( - corner_verts_, positions.size(), vert_to_face_offsets_, vert_to_face_indices_); + vlmap_ = mesh->vert_to_corner_map(); /* Deformation coords. */ co_.reserve(mesh->totvert); @@ -220,7 +219,7 @@ class MeshFairingContext : public FairingContext { } } - loop_to_face_map_ = blender::bke::mesh::build_loop_to_face_map(faces); + loop_to_face_map_ = mesh->corner_to_face_map(); } void adjacents_coords_from_loop(const int loop, @@ -247,9 +246,7 @@ class MeshFairingContext : public FairingContext { Span corner_edges_; blender::OffsetIndices faces; Span edges_; - Array loop_to_face_map_; - Array vert_to_face_offsets_; - Array vert_to_face_indices_; + Span loop_to_face_map_; }; class BMeshFairingContext : public FairingContext { diff --git a/source/blender/blenkernel/intern/mesh_mapping.cc b/source/blender/blenkernel/intern/mesh_mapping.cc index 1b3220fe497..60a8f67399a 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.cc +++ b/source/blender/blenkernel/intern/mesh_mapping.cc @@ -379,6 +379,20 @@ GroupedSpan build_vert_to_edge_map(const Span edges, return {OffsetIndices(r_offsets), r_indices}; } +void build_vert_to_face_indices(const OffsetIndices faces, + const Span corner_verts, + const OffsetIndices offsets, + MutableSpan r_indices) +{ + Array counts(offsets.size(), 0); + for (const int64_t face_i : faces.index_range()) { + for (const int vert : corner_verts.slice(faces[face_i])) { + r_indices[offsets[vert].start() + counts[vert]] = int(face_i); + counts[vert]++; + } + } +} + GroupedSpan build_vert_to_face_map(const OffsetIndices faces, const Span corner_verts, const int verts_num, @@ -387,17 +401,16 @@ GroupedSpan build_vert_to_face_map(const OffsetIndices faces, { r_offsets = create_reverse_offsets(corner_verts, verts_num); r_indices.reinitialize(r_offsets.last()); - Array counts(verts_num, 0); - - for (const int64_t face_i : faces.index_range()) { - for (const int vert : corner_verts.slice(faces[face_i])) { - r_indices[r_offsets[vert] + counts[vert]] = int(face_i); - counts[vert]++; - } - } + build_vert_to_face_indices(faces, corner_verts, OffsetIndices(r_offsets), r_indices); return {OffsetIndices(r_offsets), r_indices}; } +Array build_vert_to_corner_indices(const Span corner_verts, + const OffsetIndices offsets) +{ + return reverse_indices_in_groups(corner_verts, offsets); +} + GroupedSpan build_vert_to_loop_map(const Span corner_verts, const int verts_num, Array &r_offsets, diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index cba83044d9b..82c0e311cf8 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -114,10 +114,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me) return; } - Array vert_to_loop_offsets; - Array vert_to_loop_indices; - const GroupedSpan vert_to_loop = bke::mesh::build_vert_to_loop_map( - me->corner_verts(), me->totvert, vert_to_loop_offsets, vert_to_loop_indices); + const GroupedSpan vert_to_loop = me->vert_to_corner_map(); Vector mloopuv_layers; mloopuv_layers.reserve(mloopuv_layers_num); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 6913490a884..61e66515697 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -414,7 +414,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, result_faces, result_corner_verts, result_corner_edges, - {}, + result->corner_to_face_map(), result->vert_normals(), result->face_normals(), sharp_edges, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 99d5ab7d7e3..b62079bbd2b 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -791,6 +791,7 @@ void edges_sharp_from_angle_set(const OffsetIndices faces, const Span corner_verts, const Span corner_edges, const Span face_normals, + const Span loop_to_face, const bool *sharp_faces, const float split_angle, MutableSpan sharp_edges) @@ -803,9 +804,6 @@ void edges_sharp_from_angle_set(const OffsetIndices faces, /* Mapping edge -> loops. See #bke::mesh::normals_calc_loop for details. */ Array edge_to_loops(sharp_edges.size(), int2(0)); - /* Simple mapping from a loop to its face index. */ - const Array loop_to_face = build_loop_to_face_map(faces); - mesh_edges_sharp_tag(faces, corner_verts, corner_edges, @@ -1257,17 +1255,6 @@ void normals_calc_loop(const Span vert_positions, * Note also that loose edges always have both values set to 0! */ Array edge_to_loops(edges.size(), int2(0)); - /* Simple mapping from a loop to its face index. */ - Span loop_to_face; - Array local_loop_to_face_map; - if (loop_to_face_map.is_empty()) { - local_loop_to_face_map = build_loop_to_face_map(faces); - loop_to_face = local_loop_to_face_map; - } - else { - loop_to_face = loop_to_face_map; - } - /* When using custom loop normals, disable the angle feature! */ const bool check_angle = (split_angle < float(M_PI)) && (clnors_data == nullptr); @@ -1293,7 +1280,7 @@ void normals_calc_loop(const Span vert_positions, common_data.corner_verts = corner_verts; common_data.corner_edges = corner_edges; common_data.edge_to_loops = edge_to_loops; - common_data.loop_to_face = loop_to_face; + common_data.loop_to_face = loop_to_face_map; common_data.face_normals = face_normals; common_data.vert_normals = vert_normals; @@ -1305,7 +1292,7 @@ void normals_calc_loop(const Span vert_positions, mesh_edges_sharp_tag(faces, corner_verts, corner_edges, - loop_to_face, + loop_to_face_map, face_normals, Span(sharp_faces, sharp_faces ? faces.size() : 0), Span(sharp_edges, sharp_edges ? edges.size() : 0), diff --git a/source/blender/blenkernel/intern/mesh_remap.cc b/source/blender/blenkernel/intern/mesh_remap.cc index b0bf2862a0e..702b479b35e 100644 --- a/source/blender/blenkernel/intern/mesh_remap.cc +++ b/source/blender/blenkernel/intern/mesh_remap.cc @@ -1290,12 +1290,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, blender::Array face_cents_src; - Array vert_to_loop_src_offsets; - Array vert_to_loop_src_indices; GroupedSpan vert_to_loop_map_src; - - Array vert_to_face_src_offsets; - Array vert_to_face_src_indices; GroupedSpan vert_to_face_map_src; Array edge_to_face_src_offsets; @@ -1306,7 +1301,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, int *face_to_looptri_map_src_buff = nullptr; /* Unlike above, those are one-to-one mappings, simpler! */ - blender::Array loop_to_face_map_src; + blender::Span loop_to_face_map_src; const blender::Span positions_src = me_src->vert_positions(); const int num_verts_src = me_src->totvert; @@ -1371,7 +1366,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, faces_dst, {corner_verts_dst, numloops_dst}, {corner_edges_dst, numloops_dst}, - {}, + mesh_dst->corner_to_face_map(), mesh_dst->vert_normals(), mesh_dst->face_normals(), sharp_edges, @@ -1397,15 +1392,9 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, } if (use_from_vert) { - vert_to_loop_map_src = bke::mesh::build_vert_to_loop_map( - corner_verts_src, num_verts_src, vert_to_loop_src_offsets, vert_to_loop_src_indices); - + vert_to_loop_map_src = me_src->vert_to_corner_map(); if (mode & MREMAP_USE_POLY) { - vert_to_face_map_src = bke::mesh::build_vert_to_face_map(faces_src, - corner_verts_src, - num_verts_src, - vert_to_face_src_offsets, - vert_to_face_src_indices); + vert_to_face_map_src = me_src->vert_to_face_map(); } } @@ -1417,7 +1406,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, edge_to_face_src_indices); if (use_from_vert) { - loop_to_face_map_src = blender::bke::mesh::build_loop_to_face_map(faces_src); + loop_to_face_map_src = me_src->corner_to_face_map(); face_cents_src.reinitialize(faces_src.size()); for (const int64_t i : faces_src.index_range()) { face_cents_src[i] = blender::bke::mesh::face_center_calc( diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 43f0ec28fe8..d3c2ade9794 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -386,27 +386,13 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) return; } - Array source_vert_to_loop_offsets; - Array source_vert_to_loop_indices; GroupedSpan source_lmap; - Array target_vert_to_loop_offsets; - Array target_vert_to_loop_indices; GroupedSpan target_lmap; BVHTreeFromMesh bvhtree = {nullptr}; threading::parallel_invoke( [&]() { BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); }, - [&]() { - source_lmap = mesh::build_vert_to_loop_map(source->corner_verts(), - source->totvert, - source_vert_to_loop_offsets, - source_vert_to_loop_indices); - }, - [&]() { - target_lmap = mesh::build_vert_to_loop_map(target->corner_verts(), - target->totvert, - target_vert_to_loop_offsets, - target_vert_to_loop_indices); - }); + [&]() { source_lmap = source->vert_to_corner_map(); }, + [&]() { target_lmap = target->vert_to_corner_map(); }); const Span target_positions = target->vert_positions(); Array nearest_src_verts(target_positions.size()); diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index b11f8f566ad..a9c49a20dc4 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -14,6 +14,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "BLI_array_utils.hh" #include "BLI_math_geom.h" #include "BLI_task.hh" #include "BLI_timeit.hh" @@ -22,6 +23,7 @@ #include "BKE_editmesh_cache.hh" #include "BKE_lib_id.h" #include "BKE_mesh.hh" +#include "BKE_mesh_mapping.hh" #include "BKE_mesh_runtime.hh" #include "BKE_shrinkwrap.h" #include "BKE_subdiv_ccg.hh" @@ -125,6 +127,58 @@ static void try_tag_verts_no_face_none(const Mesh &mesh) } // namespace blender::bke +blender::Span Mesh::corner_to_face_map() const +{ + using namespace blender; + this->runtime->corner_to_face_map_cache.ensure([&](Array &r_data) { + const OffsetIndices faces = this->faces(); + r_data = bke::mesh::build_loop_to_face_map(faces); + }); + return this->runtime->corner_to_face_map_cache.data(); +} + +blender::OffsetIndices Mesh::vert_to_face_map_offsets() const +{ + using namespace blender; + this->runtime->vert_to_face_offset_cache.ensure([&](Array &r_data) { + r_data = Array(this->totvert + 1, 0); + offset_indices::build_reverse_offsets(this->corner_verts(), r_data); + }); + return OffsetIndices(this->runtime->vert_to_face_offset_cache.data()); +} + +blender::GroupedSpan Mesh::vert_to_face_map() const +{ + using namespace blender; + const OffsetIndices offsets = this->vert_to_face_map_offsets(); + this->runtime->vert_to_face_map_cache.ensure([&](Array &r_data) { + r_data.reinitialize(this->totloop); + if (this->runtime->vert_to_corner_map_cache.is_cached() && + this->runtime->corner_to_face_map_cache.is_cached()) + { + /* The vertex to face cache can be built from the vertex to face corner + * and face corner to face maps if they are both already cached. */ + array_utils::gather(this->runtime->vert_to_corner_map_cache.data().as_span(), + this->runtime->corner_to_face_map_cache.data().as_span(), + r_data.as_mutable_span()); + } + else { + bke::mesh::build_vert_to_face_indices(this->faces(), this->corner_verts(), offsets, r_data); + } + }); + return {offsets, this->runtime->vert_to_face_map_cache.data()}; +} + +blender::GroupedSpan Mesh::vert_to_corner_map() const +{ + using namespace blender; + const OffsetIndices offsets = this->vert_to_face_map_offsets(); + this->runtime->vert_to_corner_map_cache.ensure([&](Array &r_data) { + r_data = bke::mesh::build_vert_to_corner_indices(this->corner_verts(), offsets); + }); + return {offsets, this->runtime->vert_to_corner_map_cache.data()}; +} + const blender::bke::LooseVertCache &Mesh::loose_verts() const { using namespace blender::bke; @@ -250,6 +304,10 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) free_bvh_cache(*mesh->runtime); free_subdiv_ccg(*mesh->runtime); mesh->runtime->bounds_cache.tag_dirty(); + mesh->runtime->vert_to_face_offset_cache.tag_dirty(); + 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->vert_normals_cache.tag_dirty(); mesh->runtime->face_normals_cache.tag_dirty(); mesh->runtime->loose_edges_cache.tag_dirty(); @@ -271,6 +329,9 @@ void BKE_mesh_tag_edges_split(Mesh *mesh) free_bvh_cache(*mesh->runtime); mesh->runtime->vert_normals_cache.tag_dirty(); free_subdiv_ccg(*mesh->runtime); + mesh->runtime->vert_to_face_offset_cache.tag_dirty(); + mesh->runtime->vert_to_face_map_cache.tag_dirty(); + mesh->runtime->vert_to_corner_map_cache.tag_dirty(); if (mesh->runtime->loose_edges_cache.is_cached() && mesh->runtime->loose_edges_cache.data().count != 0) { @@ -298,6 +359,7 @@ void BKE_mesh_tag_face_winding_changed(Mesh *mesh) { mesh->runtime->vert_normals_cache.tag_dirty(); mesh->runtime->face_normals_cache.tag_dirty(); + mesh->runtime->vert_to_corner_map_cache.tag_dirty(); } void BKE_mesh_tag_positions_changed(Mesh *mesh) diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index 9b02ace2097..56489d3626c 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -469,10 +469,6 @@ void multires_force_sculpt_rebuild(Object *object) BKE_pbvh_free(ss->pbvh); object->sculpt->pbvh = nullptr; } - - ss->vert_to_face_indices = {}; - ss->vert_to_face_offsets = {}; - ss->pmap = {}; } void multires_force_external_reload(Object *object) diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc index 7b31a3e6f6e..60679801ce8 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc @@ -72,14 +72,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape blender::MutableSpan base_positions = base_mesh->vert_positions_for_write(); /* Update the context in case the vertices were duplicated. */ reshape_context->base_positions = base_positions; - blender::Array vert_to_face_offsets; - blender::Array vert_to_face_indices; - const blender::GroupedSpan pmap = blender::bke::mesh::build_vert_to_face_map( - reshape_context->base_faces, - reshape_context->base_corner_verts, - base_mesh->totvert, - vert_to_face_offsets, - vert_to_face_indices); + const blender::GroupedSpan pmap = base_mesh->vert_to_face_map(); float(*origco)[3] = static_cast( MEM_calloc_arrayN(base_mesh->totvert, sizeof(float[3]), __func__)); diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.cc b/source/blender/blenkernel/intern/multires_unsubdivide.cc index 37c40a88d88..c291671705f 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.cc +++ b/source/blender/blenkernel/intern/multires_unsubdivide.cc @@ -925,7 +925,6 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( MultiresUnsubdivideContext *context) { Mesh *original_mesh = context->original_mesh; - const blender::OffsetIndices original_faces = original_mesh->faces(); Mesh *base_mesh = context->base_mesh; @@ -953,7 +952,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( BM_elem_flag_set(v, BM_ELEM_TAG, true); } - context->loop_to_face_map = blender::bke::mesh::build_loop_to_face_map(original_faces); + context->loop_to_face_map = original_mesh->corner_to_face_map(); } /** diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.hh b/source/blender/blenkernel/intern/multires_unsubdivide.hh index 077b27f4b46..896acb8a6ad 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.hh +++ b/source/blender/blenkernel/intern/multires_unsubdivide.hh @@ -52,7 +52,7 @@ struct MultiresUnsubdivideContext { /* Private data. */ BMesh *bm_original_mesh; - blender::Array loop_to_face_map; + blender::Span loop_to_face_map; const int *base_to_orig_vmap; }; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 7e024b19b42..9ef84f4a0aa 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1391,13 +1391,7 @@ void BKE_sculptsession_free_deformMats(SculptSession *ss) void BKE_sculptsession_free_vwpaint_data(SculptSession *ss) { - SculptVertexPaintGeomMap *gmap = nullptr; - if (ss->mode_type == OB_MODE_VERTEX_PAINT) { - gmap = &ss->mode.vpaint.gmap; - } - else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) { - gmap = &ss->mode.wpaint.gmap; - + if (ss->mode_type == OB_MODE_WEIGHT_PAINT) { MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight); if (ss->mode.wpaint.dvert_prev) { BKE_defvert_array_free_elems(ss->mode.wpaint.dvert_prev, ss->totvert); @@ -1405,15 +1399,6 @@ void BKE_sculptsession_free_vwpaint_data(SculptSession *ss) ss->mode.wpaint.dvert_prev = nullptr; } } - else { - return; - } - gmap->vert_to_loop_offsets = {}; - gmap->vert_to_loop_indices = {}; - gmap->vert_to_loop = {}; - gmap->vert_to_face_offsets = {}; - gmap->vert_to_face_indices = {}; - gmap->vert_to_face = {}; } /** @@ -1459,8 +1444,6 @@ static void sculptsession_free_pbvh(Object *object) ss->pbvh = nullptr; } - ss->vert_to_face_offsets = {}; - ss->vert_to_face_indices = {}; ss->pmap = {}; ss->edge_to_face_offsets = {}; ss->edge_to_face_indices = {}; @@ -1776,12 +1759,8 @@ static void sculpt_update_object( sculpt_attribute_update_refs(ob); sculpt_update_persistent_base(ob); - if (ob->type == OB_MESH && ss->pmap.is_empty()) { - ss->pmap = blender::bke::mesh::build_vert_to_face_map(me->faces(), - me->corner_verts(), - me->totvert, - ss->vert_to_face_offsets, - ss->vert_to_face_indices); + if (ob->type == OB_MESH) { + ss->pmap = me->vert_to_face_map(); } if (ss->pbvh) { diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 886cc7512d8..4a9c93a6b47 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -357,7 +357,7 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_ mr.faces, mr.corner_verts, mr.corner_edges, - {}, + mr.me->corner_to_face_map(), mr.vert_normals, mr.face_normals, sharp_edges, diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 2bbbad25154..6e23637089c 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -744,6 +744,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator me->corner_verts(), me->corner_edges(), me->face_normals(), + me->corner_to_face_map(), sharp_faces, me->smoothresh, sharp_edges.span); @@ -1165,6 +1166,7 @@ void ED_mesh_split_faces(Mesh *mesh) corner_verts, corner_edges, mesh->face_normals(), + mesh->corner_to_face_map(), sharp_faces, split_angle, sharp_edges); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 65dc7c9bfd0..4c23947e1b0 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -241,15 +241,9 @@ void init_session_data(const ToolSettings *ts, Object *ob) } Mesh *me = (Mesh *)ob->data; - const blender::OffsetIndices faces = me->faces(); - const Span corner_verts = me->corner_verts(); - if (gmap->vert_to_loop_indices.is_empty()) { - gmap->vert_to_loop = blender::bke::mesh::build_vert_to_loop_map( - corner_verts, me->totvert, gmap->vert_to_loop_offsets, gmap->vert_to_loop_indices); - gmap->vert_to_face = blender::bke::mesh::build_vert_to_face_map( - faces, corner_verts, me->totvert, gmap->vert_to_face_offsets, gmap->vert_to_face_indices); - } + gmap->vert_to_loop = me->vert_to_corner_map(); + gmap->vert_to_face = me->vert_to_face_map(); /* Create average brush arrays */ if (ob->mode == OB_MODE_WEIGHT_PAINT) { diff --git a/source/blender/geometry/intern/mesh_split_edges.cc b/source/blender/geometry/intern/mesh_split_edges.cc index 253d9ec2773..66a07ddfa49 100644 --- a/source/blender/geometry/intern/mesh_split_edges.cc +++ b/source/blender/geometry/intern/mesh_split_edges.cc @@ -517,10 +517,7 @@ void split_edges(Mesh &mesh, const BitVector<> selection_bits = selection_to_bit_vector(selected_edges, orig_edges.size()); const bke::LooseEdgeCache &loose_edges = mesh.loose_edges(); - Array vert_to_corner_offsets; - Array vert_to_corner_indices; - const GroupedSpan vert_to_corner_map = bke::mesh::build_vert_to_loop_map( - mesh.corner_verts(), orig_verts_num, vert_to_corner_offsets, vert_to_corner_indices); + const GroupedSpan vert_to_corner_map = mesh.vert_to_corner_map(); Array edge_to_corner_offsets; Array edge_to_corner_indices; @@ -535,7 +532,7 @@ void split_edges(Mesh &mesh, orig_edges, orig_verts_num, vert_to_edge_offsets, vert_to_edge_indices); } - const Array corner_to_face_map = bke::mesh::build_loop_to_face_map(mesh.faces()); + const Array corner_to_face_map = mesh.corner_to_face_map(); const Array> corner_groups = calc_all_corner_groups(faces, mesh.corner_verts(), diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 5134f8ac955..0e7eab367c2 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -316,6 +316,23 @@ typedef struct Mesh { /** Set cached mesh bounds to a known-correct value to avoid their lazy calculation later on. */ void bounds_set_eager(const blender::Bounds &bounds); + /** + * Cached map containing the index of the face using each face corner. + */ + blender::Span corner_to_face_map() const; + /** + * Offsets per vertex used to slice arrays containing data for connected faces or face corners. + */ + blender::OffsetIndices vert_to_face_map_offsets() const; + /** + * Cached map from each vertex to the corners using it. + */ + blender::GroupedSpan vert_to_corner_map() const; + /** + * Cached map from each vertex to the faces using it. + */ + blender::GroupedSpan vert_to_face_map() const; + /** * Cached information about loose edges, calculated lazily when necessary. */ diff --git a/source/blender/modifiers/intern/MOD_normal_edit.cc b/source/blender/modifiers/intern/MOD_normal_edit.cc index 64f72588442..954c722fadd 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.cc +++ b/source/blender/modifiers/intern/MOD_normal_edit.cc @@ -534,7 +534,7 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, faces, corner_verts, corner_edges, - {}, + result->corner_to_face_map(), result->vert_normals(), result->face_normals(), sharp_edges.span.data(), diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.cc b/source/blender/modifiers/intern/MOD_weighted_normal.cc index 81b6c6ff536..737eb4b3c59 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.cc +++ b/source/blender/modifiers/intern/MOD_weighted_normal.cc @@ -543,7 +543,7 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh int defgrp_index; MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index); - const Array loop_to_face_map = bke::mesh::build_loop_to_face_map(result->faces()); + const Span loop_to_face_map = result->corner_to_face_map(); bke::MutableAttributeAccessor attributes = result->attributes_for_write(); bke::SpanAttributeWriter sharp_edges = attributes.lookup_or_add_for_write_span( diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index bfc5fdfc267..cf1ada8e1ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -630,15 +630,8 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh, /* Stores the indices of the faces connected to the vertex. Because the faces are looped * over in order of their indices, the face's indices will be sorted in ascending order. * (This can change once they are sorted using `sort_vertex_faces`). */ - Array vert_to_face_offset_data; - Array vert_to_face_indices; - const GroupedSpan vert_to_face_map = bke::mesh::build_vert_to_face_map( - src_faces, - src_corner_verts, - src_positions.size(), - vert_to_face_offset_data, - vert_to_face_indices); - const OffsetIndices vert_to_face_offsets(vert_to_face_offset_data); + Array vert_to_face_indices = src_mesh.vert_to_face_map().data; + const OffsetIndices vert_to_face_offsets = src_mesh.vert_to_face_map().offsets; Array> vertex_shared_edges(src_mesh.totvert); Array> vertex_corners(src_mesh.totvert); @@ -692,6 +685,8 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh, } }); + const GroupedSpan vert_to_face_map(vert_to_face_offsets, vert_to_face_indices); + Vector vert_positions(src_mesh.faces_num); for (const int i : src_faces.index_range()) { const IndexRange face = src_faces[i]; 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 f9d4d70057b..45e6ea43ac6 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 @@ -49,10 +49,7 @@ class CornersOfVertInput final : public bke::MeshFieldInput { const IndexMask &mask) const final { const IndexRange vert_range(mesh.totvert); - Array map_offsets; - Array map_indices; - const GroupedSpan vert_to_loop_map = bke::mesh::build_vert_to_loop_map( - mesh.corner_verts(), mesh.totvert, map_offsets, map_indices); + const GroupedSpan vert_to_corner_map = mesh.vert_to_corner_map(); const bke::MeshFieldContext context{mesh, domain}; fn::FieldEvaluator evaluator{context, &mask}; @@ -83,7 +80,7 @@ class CornersOfVertInput final : public bke::MeshFieldInput { continue; } - const Span corners = vert_to_loop_map[vert_i]; + const Span corners = vert_to_corner_map[vert_i]; if (corners.is_empty()) { corner_of_vertex[selection_i] = 0; continue; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc index a2e14801ff9..ed1d82edf32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc @@ -75,12 +75,10 @@ class CornerPreviousEdgeFieldInput final : public bke::MeshFieldInput { } const OffsetIndices faces = mesh.faces(); const Span corner_edges = mesh.corner_edges(); - Array loop_to_face_map = bke::mesh::build_loop_to_face_map(faces); + const Span corner_to_face = mesh.corner_to_face_map(); return VArray::ForFunc( - mesh.totloop, - [faces, corner_edges, loop_to_face_map = std::move(loop_to_face_map)](const int corner_i) { - return corner_edges[bke::mesh::face_corner_prev(faces[loop_to_face_map[corner_i]], - corner_i)]; + corner_edges.size(), [faces, corner_edges, corner_to_face](const int corner) { + return corner_edges[bke::mesh::face_corner_prev(faces[corner_to_face[corner]], corner)]; }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc index 6474d30945d..a2c8b71ec08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc @@ -36,7 +36,7 @@ class CornerFaceIndexInput final : public bke::MeshFieldInput { if (domain != ATTR_DOMAIN_CORNER) { return {}; } - return VArray::ForContainer(bke::mesh::build_loop_to_face_map(mesh.faces())); + return VArray::ForSpan(mesh.corner_to_face_map()); } uint64_t hash() const final @@ -65,12 +65,11 @@ class CornerIndexInFaceInput final : public bke::MeshFieldInput { return {}; } const OffsetIndices faces = mesh.faces(); - Array loop_to_face_map = bke::mesh::build_loop_to_face_map(faces); - return VArray::ForFunc( - mesh.totloop, [faces, loop_to_face_map = std::move(loop_to_face_map)](const int corner_i) { - const int face_i = loop_to_face_map[corner_i]; - return corner_i - faces[face_i].start(); - }); + const Span corner_to_face = mesh.corner_to_face_map(); + return VArray::ForFunc(mesh.totloop, [faces, corner_to_face](const int corner) { + const int face_i = corner_to_face[corner]; + return corner - faces[face_i].start(); + }); } uint64_t hash() const final diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc index 4e96a2a6fd6..7cc09979da4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc @@ -52,7 +52,7 @@ class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput { const VArray corner_indices = evaluator.get_evaluated(0); const VArray offsets = evaluator.get_evaluated(1); - Array loop_to_face_map = bke::mesh::build_loop_to_face_map(faces); + const Span corner_to_face = mesh.corner_to_face_map(); Array offset_corners(mask.min_array_size()); mask.foreach_index_optimized(GrainSize(2048), [&](const int selection_i) { @@ -63,7 +63,7 @@ class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput { return; } - const IndexRange face = faces[loop_to_face_map[corner_i]]; + const IndexRange face = faces[corner_to_face[corner_i]]; offset_corners[selection_i] = apply_offset_in_cyclic_range(face, corner_i, offset); });