diff options
author | Hans Goudey <h.goudey@me.com> | 2022-02-22 20:43:53 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-02-22 20:44:15 +0300 |
commit | 59343ee1627f4c369e237cea201015b979da1540 (patch) | |
tree | e73f50012470e9d305593bc99767b2edf2840b71 /source/blender/blenkernel/intern/mesh.cc | |
parent | ee9949a85f77ec3bec9e40978770e6e7a36cd457 (diff) |
Fix T95839: Data race when lazily creating mesh normal layers
Currently, when normals are calculated for a const mesh, a custom data
layer might be added if it doesn't already exist. Adding a custom data
layer to a mesh is not thread-safe, so this can be a problem in some
situations.
This commit moves derived mesh normals for polygons and
vertices out of `CustomData` to `Mesh_Runtime`. Most of the
hard work for this was already done by rBcfa53e0fbeed7178.
Some changes to logic elsewhere are necessary/helpful:
- No need to call both `BKE_mesh_runtime_clear_cache` and
`BKE_mesh_normals_tag_dirty`, since the former also does the latter.
- Cleanup/simplify mesh conversion and copying since normals are
handled with other runtime data.
Storing these normals like other runtime data clarifies their status
as derived data, meaning custom data moves more towards storing
original/editable data. This means normals won't automatically benefit
from the planned copy-on-write refactor (T95845), so it will have to be
added manually like for the other runtime data.
Differential Revision: https://developer.blender.org/D14154
Diffstat (limited to 'source/blender/blenkernel/intern/mesh.cc')
-rw-r--r-- | source/blender/blenkernel/intern/mesh.cc | 21 |
1 files changed, 10 insertions, 11 deletions
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 351535a6f78..09d9b19330a 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -75,6 +75,8 @@ #include "BLO_read_write.h" +using blender::float3; + static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -1111,16 +1113,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, mesh_tessface_clear_intern(me_dst, false); } - me_dst->runtime.cd_dirty_poly = me_src->runtime.cd_dirty_poly; - me_dst->runtime.cd_dirty_vert = me_src->runtime.cd_dirty_vert; - - /* Ensure that when no normal layers exist, they are marked dirty, because - * normals might not have been included in the mask of copied layers. */ - if (!CustomData_has_layer(&me_dst->vdata, CD_NORMAL) || - !CustomData_has_layer(&me_dst->pdata, CD_NORMAL)) { - BKE_mesh_normals_tag_dirty(me_dst); - } - /* The destination mesh should at least have valid primary CD layers, * even in cases where the source mesh does not. */ mesh_ensure_cdlayers_primary(me_dst, do_tessface); @@ -2150,6 +2142,10 @@ static void split_faces_split_new_verts(Mesh *mesh, MVert *mvert = mesh->mvert; float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh); + /* Normals were already calculated at the beginning of this operation, we rely on that to update + * them partially here. */ + BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh)); + /* Remember new_verts is a single linklist, so its items are in reversed order... */ MVert *new_mv = &mvert[mesh->totvert - 1]; for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) { @@ -2160,7 +2156,6 @@ static void split_faces_split_new_verts(Mesh *mesh, copy_v3_v3(vert_normals[i], new_verts->vnor); } } - BKE_mesh_vertex_normals_clear_dirty(mesh); } /* Perform actual split of edges. */ @@ -2231,6 +2226,10 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) /* Update pointers to a newly allocated memory. */ BKE_mesh_update_customdata_pointers(mesh, false); + /* Update normals manually to avoid recalculation after this operation. */ + mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals, + sizeof(float[3]) * mesh->totvert); + /* Perform actual split of vertices and edges. */ split_faces_split_new_verts(mesh, new_verts, num_new_verts); if (do_edges) { |