Sculpt: Reuse existing mesh triangles cache in sculpt mode #123638

Merged
Hans Goudey merged 3 commits from HooglyBoogly/blender:sculpt-triangles-freeze into main 2024-06-26 03:05:06 +02:00
5 changed files with 58 additions and 15 deletions

View File

@ -94,6 +94,19 @@ struct LooseEdgeCache : public LooseGeomCache {};
*/
struct LooseVertCache : public LooseGeomCache {};
struct TrianglesCache {
SharedCache<Array<int3>> data;
bool frozen = false;
bool dirty_while_frozen = false;
/** Delay applying dirty tags from #tag_dirty() until #unfreeze is called. */
void freeze();
/** Apply dirty tags from after #freeze, and make future dirty tags apply immediately. */
void unfreeze();
/** Call instead of `data.tag_dirty()`. */
void tag_dirty();
};
struct MeshRuntime {
/**
* "Evaluated" mesh owned by this mesh. Used for objects which don't have effective modifiers, so
@ -141,7 +154,7 @@ struct MeshRuntime {
void *batch_cache = nullptr;
/** Cache for derived triangulation of the mesh, accessed with #Mesh::corner_tris(). */
SharedCache<Array<int3>> corner_tris_cache;
TrianglesCache corner_tris_cache;
/** Cache for triangle to original face index map, accessed with #Mesh::corner_tri_faces(). */
SharedCache<Array<int>> corner_tri_faces_cache;

View File

@ -226,9 +226,38 @@ void Mesh::tag_overlapping_none()
this->flag |= ME_NO_OVERLAPPING_TOPOLOGY;
}
namespace blender::bke {
void TrianglesCache::freeze()
{
this->frozen = true;
this->dirty_while_frozen = false;
}
void TrianglesCache::unfreeze()
{
this->frozen = false;
if (this->dirty_while_frozen) {
this->data.tag_dirty();
}
this->dirty_while_frozen = false;
}
void TrianglesCache::tag_dirty()
{
if (this->frozen) {
this->dirty_while_frozen = true;
}
else {
this->data.tag_dirty();
}
}
} // namespace blender::bke
blender::Span<blender::int3> Mesh::corner_tris() const
{
this->runtime->corner_tris_cache.ensure([&](blender::Array<blender::int3> &r_data) {
this->runtime->corner_tris_cache.data.ensure([&](blender::Array<blender::int3> &r_data) {
const Span<float3> positions = this->vert_positions();
const blender::OffsetIndices faces = this->faces();
const Span<int> corner_verts = this->corner_verts();
@ -244,7 +273,7 @@ blender::Span<blender::int3> Mesh::corner_tris() const
}
});
return this->runtime->corner_tris_cache.data();
return this->runtime->corner_tris_cache.data.data();
}
blender::Span<int> Mesh::corner_tri_faces() const
@ -296,7 +325,7 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
mesh->runtime->corner_tris_cache.tag_dirty();
mesh->runtime->corner_tris_cache.data.tag_dirty();
mesh->runtime->corner_tri_faces_cache.tag_dirty();
mesh->runtime->shrinkwrap_boundary_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();

View File

@ -654,14 +654,10 @@ std::unique_ptr<PBVH> build_mesh(Mesh *mesh)
pbvh->header.type = PBVH_FACES;
const int totvert = mesh->verts_num;
const int corner_tris_num = poly_to_tri_count(mesh->faces_num, mesh->corners_num);
MutableSpan<float3> vert_positions = mesh->vert_positions_for_write();
const OffsetIndices<int> faces = mesh->faces();
const Span<int> corner_verts = mesh->corner_verts();
HooglyBoogly marked this conversation as resolved Outdated

const OffsetIndices<int> faces = mesh->faces(); is now unused.

`const OffsetIndices<int> faces = mesh->faces();` is now unused.
pbvh->corner_tris.reinitialize(corner_tris_num);
mesh::corner_tris_calc(vert_positions, faces, corner_verts, pbvh->corner_tris);
const Span<int3> corner_tris = pbvh->corner_tris;
const Span<int3> corner_tris = mesh->corner_tris();
pbvh->corner_tris = corner_tris;
pbvh->mesh = mesh;
@ -681,7 +677,7 @@ std::unique_ptr<PBVH> build_mesh(Mesh *mesh)
#endif
/* For each face, store the AABB and the AABB centroid */
Array<Bounds<float3>> prim_bounds(corner_tris_num);
Array<Bounds<float3>> prim_bounds(corner_tris.size());
const Bounds<float3> cb = threading::parallel_reduce(
corner_tris.index_range(),
1024,
@ -701,7 +697,7 @@ std::unique_ptr<PBVH> build_mesh(Mesh *mesh)
},
[](const Bounds<float3> &a, const Bounds<float3> &b) { return bounds::merge(a, b); });
if (corner_tris_num) {
if (!corner_tris.is_empty()) {
const AttributeAccessor attributes = mesh->attributes();
const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", AttrDomain::Face);
const VArraySpan material_index = *attributes.lookup<int>("material_index", AttrDomain::Face);
@ -716,7 +712,7 @@ std::unique_ptr<PBVH> build_mesh(Mesh *mesh)
vert_bitmap,
&cb,
prim_bounds,
corner_tris_num);
corner_tris.size());
#ifdef TEST_PBVH_FACE_SPLIT
test_face_boundaries(pbvh, tri_faces);

View File

@ -154,8 +154,7 @@ struct PBVH {
/** Only valid for polygon meshes. */
blender::OffsetIndices<int> faces;
blender::Span<int> corner_verts;
/* Owned by the #PBVH, because after deformations they have to be recomputed. */
blender::Array<blender::int3> corner_tris;
blender::Span<blender::int3> corner_tris;
/* Grid Data */
CCGKey gridkey;

View File

@ -355,6 +355,10 @@ void ED_object_sculptmode_enter_ex(Main &bmain,
const int mode_flag = OB_MODE_SCULPT;
Mesh *mesh = BKE_mesh_from_object(&ob);
/* Re-triangulating the mesh for position changes in sculpt mode isn't worth the performance
* impact, so delay triangulation updates until the user exits sculpt mode. */
mesh->runtime->corner_tris_cache.freeze();
/* Enter sculpt mode. */
ob.mode |= mode_flag;
@ -450,6 +454,8 @@ void ED_object_sculptmode_exit_ex(Main &bmain, Depsgraph &depsgraph, Scene &scen
const int mode_flag = OB_MODE_SCULPT;
Mesh *mesh = BKE_mesh_from_object(&ob);
mesh->runtime->corner_tris_cache.unfreeze();
multires_flush_sculpt_updates(&ob);
/* Not needed for now. */