Sculpt-dev: fix thread contention

in weighted smooth
* Cached face areas are now updated
  in a double buffered fashion;
  all threads read from one side of
  the buffer while the other is written
  to by the threads that own a given
  face; the buffers are swapped on
  each iteration of a tool that uses
  face areas.
* Fixes smooth flickering.
This commit is contained in:
Joseph Eagar 2021-11-28 02:23:06 -08:00
parent e21c21bbf9
commit 628925a5c6
7 changed files with 89 additions and 17 deletions

View File

@ -2449,7 +2449,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
BKE_sculptsession_check_mdyntopo(ob->sculpt, pbvh, me->totvert);
MEM_SAFE_FREE(ss->face_areas);
ss->face_areas = MEM_calloc_arrayN(me->totpoly, sizeof(float), "ss->face_areas");
ss->face_areas = MEM_calloc_arrayN(me->totpoly, sizeof(float) * 2, "ss->face_areas");
BKE_pbvh_build_mesh(pbvh,
me,
@ -2496,7 +2496,7 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
int totgridfaces = base_mesh->totpoly * (key.grid_size - 1) * (key.grid_size - 1);
MEM_SAFE_FREE(ss->face_areas);
ss->face_areas = MEM_calloc_arrayN(totgridfaces, sizeof(float), "ss->face_areas");
ss->face_areas = MEM_calloc_arrayN(totgridfaces, sizeof(float) * 2, "ss->face_areas");
BKE_pbvh_build_grids(pbvh,
subdiv_ccg->grids,
@ -2944,7 +2944,7 @@ void BKE_sculptsession_bmesh_add_layers(Object *ob)
BMCustomLayerReq flayers[] = {
{CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY},
{CD_PROP_FLOAT, dyntopop_faces_areas_layer_id, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY},
{CD_PROP_FLOAT2, dyntopop_faces_areas_layer_id, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY},
};
BM_data_layers_ensure(ss->bm, &ss->bm->pdata, flayers, 2);

View File

@ -4248,8 +4248,17 @@ void BKE_pbvh_node_mark_update_tri_area(PBVHNode *node)
node->flag |= PBVH_UpdateTriAreas;
}
/* must be called outside of threads */
void BKE_pbvh_face_areas_begin(PBVH *pbvh)
{
pbvh->face_area_i ^= 1;
}
void BKE_pbvh_update_all_tri_areas(PBVH *pbvh)
{
/* swap read/write face area buffers */
pbvh->face_area_i ^= 1;
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (node->flag & PBVH_Leaf) {
@ -4279,6 +4288,8 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
BKE_pbvh_bmesh_check_tris(pbvh, node);
}
const int cur_i = pbvh->face_area_i ^ 1;
switch (BKE_pbvh_type(pbvh)) {
case PBVH_FACES: {
for (int i = 0; i < node->totprim; i++) {
@ -4289,7 +4300,7 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
continue;
}
pbvh->face_areas[lt->poly] = 0.0f;
pbvh->face_areas[lt->poly * 2 + cur_i] = 0.0f;
}
for (int i = 0; i < node->totprim; i++) {
@ -4304,7 +4315,14 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
MVert *mv2 = pbvh->verts + pbvh->mloop[lt->tri[1]].v;
MVert *mv3 = pbvh->verts + pbvh->mloop[lt->tri[2]].v;
pbvh->face_areas[lt->poly] += area_tri_v3(mv1->co, mv2->co, mv3->co);
float area = area_tri_v3(mv1->co, mv2->co, mv3->co);
pbvh->face_areas[lt->poly * 2 + cur_i] += area;
/* sanity check on read side of read write buffer */
if (pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] == 0.0f) {
pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] = pbvh->face_areas[lt->poly * 2 + cur_i];
}
}
break;
}
@ -4313,7 +4331,8 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
const int cd_face_area = pbvh->cd_face_area;
TGSET_ITER (f, node->bm_faces) {
BM_ELEM_CD_SET_FLOAT(f, cd_face_area, 0.0f);
float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area);
areabuf[cur_i] = 0.0f;
}
TGSET_ITER_END;
@ -4325,10 +4344,12 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node)
BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i);
BMFace *f = (BMFace *)tri->f.i;
float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area);
float area = area_tri_v3(v1->co, v2->co, v3->co);
float farea = BM_ELEM_CD_GET_FLOAT(f, cd_face_area);
BM_ELEM_CD_SET_FLOAT(f, cd_face_area, farea + area);
areabuf[cur_i] = farea + area;
}
break;
}
@ -4439,6 +4460,8 @@ void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap)
void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_areas, int valence)
{
const int cur_i = pbvh->face_area_i;
switch (BKE_pbvh_type(pbvh)) {
case PBVH_FACES: {
int *edges = BLI_array_alloca(edges, 16);
@ -4470,10 +4493,10 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_are
}
}
for (int i = 0; i < len; i++) {
r_areas[i] = pbvh->face_areas[polys[i * 2]];
r_areas[i] = pbvh->face_areas[polys[i * 2] * 2 + cur_i];
if (polys[i * 2 + 1] != -1) {
r_areas[i] += pbvh->face_areas[polys[i * 2 + 1]];
r_areas[i] += pbvh->face_areas[polys[i * 2 + 1] * 2 + cur_i];
r_areas[i] *= 0.5f;
}
}
@ -4507,8 +4530,11 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_are
w = 0.0f;
}
else {
w += BM_ELEM_CD_GET_FLOAT(e->l->f, cd_face_area) * 0.5f;
w += BM_ELEM_CD_GET_FLOAT(e->l->radial_next->f, cd_face_area) * 0.5f;
float *a1 = BM_ELEM_CD_GET_VOID_P(e->l->f, cd_face_area);
float *a2 = BM_ELEM_CD_GET_VOID_P(e->l->radial_next->f, cd_face_area);
w += a1[cur_i] * 0.5f;
w += a2[cur_i] * 0.5f;
}
if (j >= valence) {

View File

@ -45,6 +45,7 @@ Topology rake:
#include "BLI_array.h"
#include "BLI_buffer.h"
#include "BLI_ghash.h"
#include "BLI_hash.h"
#include "BLI_heap_simple.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
@ -2463,6 +2464,31 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
}
pbvh_print_mem_size(pbvh);
/* update face areas */
const int cd_face_area = pbvh->cd_face_area;
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
BKE_pbvh_bmesh_check_tris(pbvh, node);
node->flag |= PBVH_UpdateTriAreas;
BKE_pbvh_check_tri_areas(pbvh, node);
int area_src_i = pbvh->face_area_i ^ 1;
int area_dst_i = pbvh->face_area_i;
/* make sure read side of double buffer is set too */
TGSET_ITER (f, node->bm_faces) {
float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area);
areabuf[area_dst_i] = areabuf[area_src_i];
}
TGSET_ITER_END;
}
}
void BKE_pbvh_set_bm_log(PBVH *pbvh, struct BMLog *log)
@ -2808,7 +2834,8 @@ static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], i
key ^= (uintptr_t)l->v;
if (cd_fset >= 0) {
key ^= BM_ELEM_CD_GET_INT(l->f, cd_fset);
// key ^= (uintptr_t)BLI_hash_int(BM_ELEM_CD_GET_INT(l->f, cd_fset));
key ^= (uintptr_t)BM_ELEM_CD_GET_INT(l->f, cd_fset);
}
for (int i = 0; i < totuv; i++) {

View File

@ -172,7 +172,8 @@ struct PBVH {
int face_sets_color_seed;
int face_sets_color_default;
int *face_sets;
float *face_areas;
float *face_areas; /* float2 vector, double buffered to avoid thread contention */
int face_area_i;
/* Grid Data */
CCGKey gridkey;

View File

@ -412,6 +412,8 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
ss->cache->automasking->settings.flags &= ~BRUSH_AUTOMASKING_FACE_SETS;
}
bool modified = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
MeshElemMap *vert_map = &ss->pmap[vd.index];
@ -501,6 +503,7 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (ok) {
ss->face_sets[vert_map->indices[j]] = new_fset;
modified = true;
}
}
}
@ -590,6 +593,7 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (ok) {
BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, new_fset);
modified = true;
}
}
}
@ -621,12 +625,17 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (!use_fset_strength || fade > test_limit) {
SCULPT_vertex_face_set_set(ss, vd.vertex, new_fset);
modified = true;
}
}
}
}
BKE_pbvh_vertex_iter_end;
if (modified) {
BKE_pbvh_node_mark_update_triangulation(data->nodes[n]);
}
// restore automasking flag
if (set_active_faceset) {
ss->cache->automasking->settings.flags |= automasking_fset_flag;

View File

@ -781,6 +781,10 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
if (ELEM(filter_type, MESH_FILTER_SMOOTH) && ss->filter_cache->weighted_smooth) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,

View File

@ -1655,10 +1655,15 @@ void SCULPT_smooth(Sculpt *sd,
return;
}
if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
ss->cache->brush->boundary_smooth_factor > 0.0f)) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
if ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
ss->cache->brush->boundary_smooth_factor > 0.0f) {
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
else {
void BKE_pbvh_face_areas_begin(PBVH * pbvh);
BKE_pbvh_face_areas_begin(ss->pbvh);
}
}
SculptLayerParams params = {.permanent = false, .simple_array = false};