Did some profiling with VTune.

* Sculpt code seems to be memory bandwidth bound.
  * Some key topology loops will have to be written manually
    instead of using BM_ITER.

I wrote a function to re-allocate a bmesh with elements ordered by
PBVH leaf nodes, SCULPT_reorder_bmesh.  It's currently disabled.

This is going to take more profiling, but my original proxy refactor
idea might be worth revisiting.  Might be more cache efficient.

The good news is that the worst case is the smooth code, which I can speed
up significantly by keeping a bit of state around.
This commit is contained in:
Joseph Eagar 2021-05-14 15:56:04 -07:00
parent 32ceaa7919
commit 6be2c079c1
10 changed files with 424 additions and 56 deletions

View File

@ -715,6 +715,8 @@ struct BMVert *BKE_pbvh_vert_create_bmesh(
PBVH *pbvh, float co[3], float no[3], PBVHNode *node, struct BMVert *v_example);
PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f);
struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh);
#ifdef __cplusplus
}
#endif

View File

@ -35,6 +35,7 @@
#include "DNA_meshdata_types.h"
#include "BLI_bitmap.h"
#include "BLI_compiler_attrs.h"
#include "BLI_endian_switch.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
@ -2943,15 +2944,17 @@ bool CustomData_is_referenced_layer(struct CustomData *data, int type)
return (layer->flag & CD_FLAG_NOFREE) != 0;
}
void CustomData_unmark_temporary_nocopy(CustomData *data) {
for (int i=0; i<data->totlayer; i++) {
void CustomData_unmark_temporary_nocopy(CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].flag & CD_FLAG_TEMPORARY) {
data->layers[i].flag &= ~CD_FLAG_NOCOPY;
}
}
}
void CustomData_mark_temporary_nocopy(CustomData *data) {
void CustomData_mark_temporary_nocopy(CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].flag & CD_FLAG_TEMPORARY) {
data->layers[i].flag |= CD_FLAG_NOCOPY;
@ -3915,7 +3918,7 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source,
}
}
for (int dest_i=0; dest_i < dest->totlayer; dest_i++) {
for (int dest_i = 0; dest_i < dest->totlayer; dest_i++) {
CustomData_bmesh_set_default_n(dest, dest_block, dest_i);
dest_i++;
}

View File

@ -82,6 +82,7 @@
// XXX todo: work our bad module cross ref
void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me);
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
void SCULPT_reorder_bmesh(SculptSession *ss);
static void palette_init_data(ID *id)
{
@ -1383,9 +1384,10 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
if (ss->bm) {
if (ob->data) {
if (reorder) {
if (reorder && ss->bm_log) {
BM_log_mesh_elems_reorder(ss->bm, ss->bm_log);
}
BM_mesh_bm_to_me(NULL,
NULL,
ss->bm,
@ -1470,8 +1472,8 @@ void BKE_sculptsession_free(Object *ob)
if (ob && ob->sculpt) {
SculptSession *ss = ob->sculpt;
if (ss->bm_log) {
BM_log_free(ss->bm_log, true);
if (ss->bm_log && BM_log_free(ss->bm_log, true)) {
ss->bm_log = NULL;
}
/*try to save current mesh*/
@ -2229,6 +2231,10 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
if (ob->sculpt->bm != NULL) {
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
pbvh = build_pbvh_for_dynamic_topology(ob);
ob->sculpt->pbvh = pbvh;
//reorder mesh elements to improve memory cache performance
SCULPT_reorder_bmesh(ob->sculpt);
}
else {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);

View File

@ -711,6 +711,10 @@ void BKE_pbvh_free(PBVH *pbvh)
BLI_table_gset_free(node->bm_other_verts, NULL);
}
if (node->tribuf) {
BKE_pbvh_bmesh_free_tris(pbvh, node);
}
#ifdef PROXY_ADVANCED
BKE_pbvh_free_proxyarray(pbvh, node);
#endif

View File

@ -74,7 +74,6 @@ Topology rake:
/* Avoid skinny faces */
#define USE_EDGEQUEUE_EVEN_SUBDIV
/* How much longer we need to be to consider for subdividing
* (avoids subdividing faces which are only *slightly* skinny) */
#define EVEN_EDGELEN_THRESHOLD 1.2f
@ -82,14 +81,14 @@ Topology rake:
* (avoids performing subdivisions too far away). */
#define EVEN_GENERATION_SCALE 1.1f
//recursion depth to start applying front face test
// recursion depth to start applying front face test
#define DEPTH_START_LIMIT 5
//#define FANCY_EDGE_WEIGHTS
#define SKINNY_EDGE_FIX
//slightly relax geometry by this factor along surface tangents
//to improve convergence of remesher
// slightly relax geometry by this factor along surface tangents
// to improve convergence of remesher
#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
@ -228,7 +227,7 @@ BLI_INLINE void surface_smooth_v_safe(BMVert *v)
mul_v3_fl(co, 1.0f / tot);
float x = v->co[0], y = v->co[1], z = v->co[2];
//conflicts here should be pretty rare.
// conflicts here should be pretty rare.
atomic_cas_float(&v->co[0], x, x + co[0] * DYNTOPO_SAFE_SMOOTH_FAC);
atomic_cas_float(&v->co[1], y, y + co[1] * DYNTOPO_SAFE_SMOOTH_FAC);
atomic_cas_float(&v->co[2], z, z + co[2] * DYNTOPO_SAFE_SMOOTH_FAC);
@ -1188,7 +1187,7 @@ BLI_INLINE float calc_weighted_edge_collapse(EdgeQueueContext *eq_ctx, BMVert *v
#ifdef FANCY_EDGE_WEIGHTS
float l = len_squared_v3v3(v1->co, v2->co);
float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2);
val = MAX2(val*0.5 - 6.0f, 1.0f);
val = MAX2(val * 0.5 - 6.0f, 1.0f);
val = powf(val, 0.5);
l /= val;
@ -1728,8 +1727,12 @@ static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
}
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
static void long_edge_queue_edge_add_recursive(
EdgeQueueContext *eq_ctx, BMLoop *l_edge, BMLoop *l_end, const float len_sq, float limit_len, int depth)
static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx,
BMLoop *l_edge,
BMLoop *l_end,
const float len_sq,
float limit_len,
int depth)
{
BLI_assert(len_sq > square_f(limit_len));
@ -1766,8 +1769,12 @@ static void long_edge_queue_edge_add_recursive(
float len_sq_other = BM_edge_calc_length_squared(l_adjacent[i]->e);
if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
// edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
long_edge_queue_edge_add_recursive(
eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len, depth+1);
long_edge_queue_edge_add_recursive(eq_ctx,
l_adjacent[i]->radial_next,
l_adjacent[i],
len_sq_other,
limit_len,
depth + 1);
}
}
} while ((l_iter = l_iter->radial_next) != l_end);
@ -1806,8 +1813,13 @@ static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f, bool i
#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
const float len_sq = BM_edge_calc_length_squared(l_iter->e);
if (len_sq > eq_ctx->q->limit_len_squared) {
long_edge_queue_edge_add_recursive(
eq_ctx, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, DEPTH_START_LIMIT+1);//ignore_frontface ? 0 : DEPTH_START_LIMIT+1);
long_edge_queue_edge_add_recursive(eq_ctx,
l_iter->radial_next,
l_iter,
len_sq,
eq_ctx->q->limit_len,
DEPTH_START_LIMIT +
1); // ignore_frontface ? 0 : DEPTH_START_LIMIT+1);
}
#else
long_edge_queue_edge_add(eq_ctx, l_iter->e);
@ -1838,13 +1850,12 @@ static void short_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f)
}
}
static void short_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
BMLoop *l_edge,
BMLoop *l_end,
const float len_sq,
float limit_len,
int depth)
BMLoop *l_edge,
BMLoop *l_end,
const float len_sq,
float limit_len,
int depth)
{
BLI_assert(len_sq > square_f(limit_len));
@ -1884,19 +1895,23 @@ static void short_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
// edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
short_edge_queue_edge_add_recursive_2(tdata,
l_adjacent[i]->radial_next,
l_adjacent[i],
len_sq_other,
limit_len,
depth + 1);
l_adjacent[i]->radial_next,
l_adjacent[i],
len_sq_other,
limit_len,
depth + 1);
}
}
} while ((l_iter = l_iter->radial_next) != l_end);
}
}
static void long_edge_queue_edge_add_recursive_2(
EdgeQueueThreadData *tdata, BMLoop *l_edge, BMLoop *l_end, const float len_sq, float limit_len, int depth)
static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
BMLoop *l_edge,
BMLoop *l_end,
const float len_sq,
float limit_len,
int depth)
{
BLI_assert(len_sq > square_f(limit_len));
@ -1935,8 +1950,12 @@ static void long_edge_queue_edge_add_recursive_2(
if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
// edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
long_edge_queue_edge_add_recursive_2(
tdata, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len, depth+1);
long_edge_queue_edge_add_recursive_2(tdata,
l_adjacent[i]->radial_next,
l_adjacent[i],
len_sq_other,
limit_len,
depth + 1);
}
}
} while ((l_iter = l_iter->radial_next) != l_end);
@ -2153,7 +2172,6 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx,
BLI_array_free(tdata);
}
/* Create a priority queue containing vertex pairs connected by a
* short edge as defined by PBVH.bm_min_edge_len.
*
@ -2295,7 +2313,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
void *vsrcs[2] = {e->v1->head.data, e->v2->head.data};
float vws[2] = {0.5f, 0.5f};
CustomData_bmesh_interp(&pbvh->bm->vdata, (const void**)vsrcs, (float*)vws, NULL, 2, v_new->head.data);
CustomData_bmesh_interp(
&pbvh->bm->vdata, (const void **)vsrcs, (float *)vws, NULL, 2, v_new->head.data);
if (boundary) {
MDynTopoVert *mv_new = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_new);
@ -2391,7 +2410,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
void *lsrcs[2] = {l1->head.data, l2->head.data};
float lws[2] = {0.5f, 0.5f};
CustomData_bmesh_interp(&pbvh->bm->ldata, (const void**)lsrcs, lws, lws, 2, f_new->l_first->next->head.data);
CustomData_bmesh_interp(
&pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 2, f_new->l_first->next->head.data);
lsrcs[0] = l1->head.data;
lws[0] = 1.0f;
@ -2497,7 +2517,6 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx,
continue;
}
#ifdef USE_EDGEQUEUE_TAG
EDGE_QUEUE_DISABLE(e);
#endif
@ -2509,7 +2528,7 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx,
continue;
}
#else
//BLI_assert(calc_weighted_edge_split(eq_ctx, v1->co, v2->co) > eq_ctx->q->limit_len_squared);
// BLI_assert(calc_weighted_edge_split(eq_ctx, v1->co, v2->co) > eq_ctx->q->limit_len_squared);
#endif
/* Check that the edge's vertices are still in the PBVH. It's
@ -3841,14 +3860,14 @@ static bool cleanup_valence_3_4(PBVH *pbvh,
/* Collapse short edges, subdivide long edges */
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
PBVHTopologyUpdateMode mode,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected,
int sym_axis,
bool updatePBVH)
PBVHTopologyUpdateMode mode,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected,
int sym_axis,
bool updatePBVH)
{
/*
if (sym_axis >= 0 &&
@ -3901,7 +3920,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
short_edge_queue_create(
&eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
#ifdef SKINNY_EDGE_FIX
# ifdef SKINNY_EDGE_FIX
// prevent remesher thrashing by throttling edge splitting in pathological case of skinny edges
float avg_elen = eq_ctx.avg_elen;
if (eq_ctx.totedge > 0.0f) {
@ -3918,7 +3937,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
ratio = MIN2(ratio, 5.0f);
}
}
#endif
# endif
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
@ -3958,7 +3977,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
ratio = MIN2(ratio, 1.0f);
}
}
#endif
# endif
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
@ -4419,6 +4438,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh)
n3->bm_unique_verts = BLI_table_gset_new("bm_unique_verts");
n3->bm_other_verts = BLI_table_gset_new("bm_other_verts");
n3->bm_faces = BLI_table_gset_new("bm_faces");
n3->tribuf = NULL;
}
else if ((n1->flag & PBVH_Delete) && (n2->flag & PBVH_Delete)) {
n->children_offset = 0;
@ -4429,6 +4449,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh)
n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts");
n->bm_other_verts = BLI_table_gset_new("bm_other_verts");
n->bm_faces = BLI_table_gset_new("bm_faces");
n->tribuf = NULL;
}
}
}
@ -5049,3 +5070,238 @@ static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge)
BLI_array_free(faces);
BLI_array_free(fmap);
}
BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh)
{
if (BKE_pbvh_type(pbvh) != PBVH_BMESH || pbvh->totnode == 0) {
return pbvh->bm;
}
// try to group memory allocations by node
struct {
BMEdge **edges;
int totedge;
BMVert **verts;
int totvert;
BMFace **faces;
int totface;
} *nodedata = MEM_callocN(sizeof(*nodedata) * pbvh->totnode, "nodedata");
BMIter iter;
int types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH};
#define VISIT_TAG BM_ELEM_TAG
BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT | BM_EDGE | BM_FACE);
BM_mesh_elem_table_ensure(pbvh->bm, BM_VERT | BM_EDGE | BM_FACE);
for (int i = 0; i < 3; i++) {
BMHeader *elem;
BM_ITER_MESH (elem, &iter, pbvh->bm, types[i]) {
elem->hflag &= ~VISIT_TAG;
}
}
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
BMVert **verts = nodedata[i].verts;
BMEdge **edges = nodedata[i].edges;
BMFace **faces = nodedata[i].faces;
BLI_array_declare(verts);
BLI_array_declare(edges);
BLI_array_declare(faces);
BMVert *v;
BMFace *f;
TGSET_ITER (v, node->bm_unique_verts) {
if (v->head.hflag & VISIT_TAG) {
continue;
}
v->head.hflag |= VISIT_TAG;
BLI_array_append(verts, v);
BMEdge *e = v->e;
do {
if (!(e->head.hflag & VISIT_TAG)) {
e->head.hflag |= VISIT_TAG;
BLI_array_append(edges, e);
}
e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
} while (e != v->e);
}
TGSET_ITER_END;
TGSET_ITER (f, node->bm_faces) {
if (f->head.hflag & VISIT_TAG) {
continue;
}
BLI_array_append(faces, f);
f->head.hflag |= VISIT_TAG;
}
TGSET_ITER_END;
nodedata[i].verts = verts;
nodedata[i].edges = edges;
nodedata[i].faces = faces;
nodedata[i].totvert = BLI_array_len(verts);
nodedata[i].totedge = BLI_array_len(edges);
nodedata[i].totface = BLI_array_len(faces);
}
BMAllocTemplate templ = {
pbvh->bm->totvert, pbvh->bm->totedge, pbvh->bm->totloop, pbvh->bm->totface};
struct BMeshCreateParams params = {0};
BMesh *bm2 = BM_mesh_create(&templ, &params);
CustomData_copy_all_layout(&pbvh->bm->vdata, &bm2->vdata);
CustomData_copy_all_layout(&pbvh->bm->edata, &bm2->edata);
CustomData_copy_all_layout(&pbvh->bm->ldata, &bm2->ldata);
CustomData_copy_all_layout(&pbvh->bm->pdata, &bm2->pdata);
CustomData_bmesh_init_pool(&bm2->vdata, pbvh->bm->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm2->edata, pbvh->bm->totedge, BM_EDGE);
CustomData_bmesh_init_pool(&bm2->ldata, pbvh->bm->totloop, BM_LOOP);
CustomData_bmesh_init_pool(&bm2->pdata, pbvh->bm->totface, BM_FACE);
BMVert **verts = NULL;
BMEdge **edges = NULL;
BMFace **faces = NULL;
BLI_array_declare(verts);
BLI_array_declare(edges);
BLI_array_declare(faces);
for (int i = 0; i < pbvh->totnode; i++) {
for (int j = 0; j < nodedata[i].totvert; j++) {
BMVert *v1 = nodedata[i].verts[j];
BMVert *v2 = BM_vert_create(bm2, v1->co, NULL, BM_CREATE_SKIP_CD);
BM_elem_attrs_copy_ex(pbvh->bm, bm2, v1, v2, 0, 0L);
v2->head.index = v1->head.index = BLI_array_len(verts);
BLI_array_append(verts, v2);
}
}
for (int i = 0; i < pbvh->totnode; i++) {
for (int j = 0; j < nodedata[i].totedge; j++) {
BMEdge *e1 = nodedata[i].edges[j];
BMEdge *e2 = BM_edge_create(
bm2, verts[e1->v1->head.index], verts[e1->v2->head.index], NULL, BM_CREATE_SKIP_CD);
BM_elem_attrs_copy_ex(pbvh->bm, bm2, e1, e2, 0, 0L);
e2->head.index = e1->head.index = BLI_array_len(edges);
BLI_array_append(edges, e2);
}
}
BMVert **fvs = NULL;
BMEdge **fes = NULL;
BLI_array_declare(fvs);
BLI_array_declare(fes);
for (int i = 0; i < pbvh->totnode; i++) {
for (int j = 0; j < nodedata[i].totface; j++) {
BMFace *f1 = nodedata[i].faces[j];
BLI_array_clear(fvs);
BLI_array_clear(fes);
int totloop = 0;
BMLoop *l1 = f1->l_first;
do {
BLI_array_append(fvs, verts[l1->v->head.index]);
BLI_array_append(fes, edges[l1->e->head.index]);
l1 = l1->next;
totloop++;
} while (l1 != f1->l_first);
BMFace *f2 = BM_face_create(bm2, fvs, fes, totloop, NULL, BM_CREATE_SKIP_CD);
f1->head.index = f2->head.index = BLI_array_len(faces);
BLI_array_append(faces, f2);
// CustomData_bmesh_copy_data(&pbvh->bm->pdata, &bm2->pdata, f1->head.data, &f2->head.data);
BM_elem_attrs_copy_ex(pbvh->bm, bm2, f1, f2, 0, 0L);
BMLoop *l2 = f2->l_first;
do {
BM_elem_attrs_copy_ex(pbvh->bm, bm2, l1, l2, 0, 0L);
l1 = l1->next;
l2 = l2->next;
} while (l2 != f2->l_first);
}
}
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
int totunique = node->bm_unique_verts->length;
int totother = node->bm_other_verts->length;
int totface = node->bm_faces->length;
TableGSet *bm_faces = BLI_table_gset_new_ex("bm_faces", totface);
TableGSet *bm_other_verts = BLI_table_gset_new_ex("bm_other_verts", totunique);
TableGSet *bm_unique_verts = BLI_table_gset_new_ex("bm_unique_verts", totother);
BMVert *v;
BMFace *f;
TGSET_ITER (v, node->bm_unique_verts) {
BLI_table_gset_insert(bm_unique_verts, verts[v->head.index]);
}
TGSET_ITER_END;
TGSET_ITER (v, node->bm_other_verts) {
BLI_table_gset_insert(bm_other_verts, verts[v->head.index]);
}
TGSET_ITER_END;
TGSET_ITER (f, node->bm_faces) {
BLI_table_gset_insert(bm_faces, faces[f->head.index]);
}
TGSET_ITER_END;
BLI_table_gset_free(node->bm_faces, NULL);
BLI_table_gset_free(node->bm_other_verts, NULL);
BLI_table_gset_free(node->bm_unique_verts, NULL);
node->bm_faces = bm_faces;
node->bm_other_verts = bm_other_verts;
node->bm_unique_verts = bm_unique_verts;
node->flag |= PBVH_UpdateTris | PBVH_UpdateRedraw;
}
MEM_SAFE_FREE(fvs);
MEM_SAFE_FREE(fes);
for (int i = 0; i < pbvh->totnode; i++) {
MEM_SAFE_FREE(nodedata[i].verts);
MEM_SAFE_FREE(nodedata[i].edges);
MEM_SAFE_FREE(nodedata[i].faces);
}
MEM_SAFE_FREE(verts);
MEM_SAFE_FREE(edges);
MEM_SAFE_FREE(faces);
MEM_freeN(nodedata);
BM_mesh_free(pbvh->bm);
pbvh->bm = bm2;
return bm2;
}

View File

@ -782,6 +782,10 @@ void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert)
log->cd_dyn_vert = cd_dyn_vert;
}
void BM_log_set_bm(BMesh *bm, BMLog *log) {
log->bm = bm;
}
/* Allocate, initialize, and assign a new BMLog */
BMLog *BM_log_create(BMesh *bm, int cd_dyn_vert)
{
@ -913,7 +917,7 @@ BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry)
/* Free all the data in a BMLog including the log itself
* safe_mode means log->refcount will be checked, and if nonzero log will not be freed
*/
void BM_log_free(BMLog *log, bool safe_mode)
bool BM_log_free(BMLog *log, bool safe_mode)
{
BMLogEntry *entry;
@ -927,7 +931,7 @@ void BM_log_free(BMLog *log, bool safe_mode)
bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh);
return;
return false;
}
BLI_rw_mutex_end(&log->lock);
@ -951,6 +955,8 @@ void BM_log_free(BMLog *log, bool safe_mode)
}
MEM_freeN(log);
return true;
}
/* Get the number of log entries */
@ -1636,6 +1642,10 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log)
{
BMLogEntry *entry = log->current_entry;
if (!entry) {
entry = BM_log_entry_add_ex(bm, log, false);
}
bool add = BLI_ghash_len(entry->added_faces) > 0;
add |= BLI_ghash_len(entry->modified_verts) > 0;
add |= BLI_ghash_len(entry->modified_faces) > 0;

View File

@ -36,10 +36,12 @@ void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert);
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry);
/* Free all the data in a BMLog including the log itself */
void BM_log_free(BMLog *log, bool safe_mode);
bool BM_log_free(BMLog *log, bool safe_mode);
BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry);
void BM_log_set_bm(BMesh *bm, BMLog *log);
/* Get the number of log entries */
int BM_log_length(const BMLog *log);

View File

@ -167,6 +167,10 @@ const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex)
const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index)
{
if (ss->bm) {
return ((BMVert*)index.i)->co;
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
@ -1091,6 +1095,34 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
iter->size++;
}
static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter,
SculptVertRef neighbor,
int neighbor_index)
{
if (iter->size >= iter->capacity) {
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array");
iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(SculptVertRef) * iter->size);
memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
iter->neighbors, iter->capacity * sizeof(SculptVertRef), "neighbor array");
iter->neighbor_indices = MEM_reallocN_id(
iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
}
}
iter->neighbors[iter->size] = neighbor;
iter->neighbor_indices[iter->size] = neighbor_index;
iter->size++;
}
static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
SculptVertRef index,
SculptVertexNeighborIter *iter)
@ -1107,7 +1139,22 @@ static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->i = 0;
#if 1 // note that BM_EDGES_OF_VERT should be faster then BM_LOOPS_OF_VERT
#if 1
BMEdge *e = v->e;
do {
if (v == e->v1) {
sculpt_vertex_neighbor_add_nocheck(
iter, BKE_pbvh_make_vref((intptr_t)e->v2), BM_elem_index_get(e->v2));
e = e->v1_disk_link.next;
} else {
sculpt_vertex_neighbor_add_nocheck(
iter, BKE_pbvh_make_vref((intptr_t)e->v1), BM_elem_index_get(e->v1));
e = e->v2_disk_link.next;
}
} while (e != v->e);
#elif 0 // note that BM_EDGES_OF_VERT should be faster then BM_LOOPS_OF_VERT
BMEdge *e;
BM_ITER_ELEM (e, &liter, v, BM_EDGES_OF_VERT) {
BMVert *v_other = BM_edge_other_vert(e, v);

View File

@ -82,6 +82,43 @@
#include <math.h>
#include <stdlib.h>
/*
Copies the bmesh, but orders the elements
according to PBVH node to improve memory locality
*/
void SCULPT_reorder_bmesh(SculptSession *ss)
{
#if 0
SCULPT_face_random_access_ensure(ss);
SCULPT_vertex_random_access_ensure(ss);
int actv = ss->active_vertex_index.i ? BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_vertex_index) : -1;
int actf =ss->active_face_index.i ? BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index) : -1;
if (ss->bm_log) {
BM_log_full_mesh(ss->bm, ss->bm_log);
}
ss->bm = BKE_pbvh_reorder_bmesh(ss->pbvh);
SCULPT_face_random_access_ensure(ss);
SCULPT_vertex_random_access_ensure(ss);
if (actv >= 0) {
ss->active_vertex_index = BKE_pbvh_table_index_to_vertex(ss->pbvh, actv);
}
if (actf >= 0) {
ss->active_face_index = BKE_pbvh_table_index_to_face(ss->pbvh, actf);
}
SCULPT_dyntopo_node_layers_update_offsets(ss);
if (ss->bm_log) {
BM_log_set_bm(ss->bm, ss->bm_log);
}
#endif
}
void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
{
if (bm->totloop == bm->totface * 3) {

View File

@ -407,8 +407,8 @@ float *SCULPT_boundary_automasking_init(Object *ob,
/* Returns an array indexed by vertex index containing the geodesic distance to the closest vertex
in the initial vertex set. The caller is responsible for freeing the array.
Geodesic distances will only work when used with PBVH_FACES or PBVH_BMESH, for other types of PBVH it will
fallback to euclidean distances to one of the initial vertices in the set. */
Geodesic distances will only work when used with PBVH_FACES or PBVH_BMESH, for other types of PBVH
it will fallback to euclidean distances to one of the initial vertices in the set. */
float *SCULPT_geodesic_distances_create(struct Object *ob,
struct GSet *initial_vertices,
const float limit_radius);
@ -1518,3 +1518,4 @@ void SCULPT_dyntopo_save_persistent_base(SculptSession *ss);
int SCULPT_get_symmetry_pass(const SculptSession *ss);
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
void SCULPT_reorder_bmesh(SculptSession *ss);