[Bf-blender-cvs] [2c2d52c] master: Optimize sculpt undo, avoid redundant updates

Campbell Barton noreply at git.blender.org
Sat Feb 13 09:08:00 CET 2016


Commit: 2c2d52c2dea1647b84f2d0808ee1834484a6d48d
Author: Campbell Barton
Date:   Sat Feb 13 17:58:44 2016 +1100
Branches: master
https://developer.blender.org/rB2c2d52c2dea1647b84f2d0808ee1834484a6d48d

Optimize sculpt undo, avoid redundant updates

On undo, sculpting regular meshes would update _all_ GPU VBO's.
Avoiding the update gives noticeably faster undo.

This is also a fix/workaround for strange behavior with NVidia's driver (T47232),
Where locking and unlocking all buffers for updating
slows down redraw speed permanently after the first undo.

However the problem isn't avoided entirely since a single brush stroke might modify most of the mesh.

===================================================================

M	source/blender/blenkernel/BKE_pbvh.h
M	source/blender/blenkernel/intern/pbvh.c
M	source/blender/editors/sculpt_paint/sculpt_undo.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 5d69cb5..927303f 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -349,6 +349,8 @@ void BKE_pbvh_node_get_bm_orco_data(
         PBVHNode *node,
         int (**r_orco_tris)[3], int *r_orco_tris_num, float (**r_orco_coords)[3]);
 
+bool BKE_pbvh_node_vert_update_check_any(PBVH *bvh, PBVHNode *node);
+
 //void BKE_pbvh_node_BB_reset(PBVHNode *node);
 //void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]);
 
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 00c22e1..e89873a 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1474,6 +1474,30 @@ void BKE_pbvh_node_get_bm_orco_data(
 	*r_orco_coords = node->bm_orco;
 }
 
+/**
+ * \note doing a full search on all vertices here seems expensive,
+ * however this is important to avoid having to recalculate boundbox & sync the buffers to the GPU
+ * (which is far more expensive!) See: T47232.
+ */
+bool BKE_pbvh_node_vert_update_check_any(PBVH *bvh, PBVHNode *node)
+{
+	BLI_assert(bvh->type == PBVH_FACES);
+	const int *verts = node->vert_indices;
+	const int totvert = node->uniq_verts + node->face_verts;
+
+	for (int i = 0; i < totvert; ++i) {
+		const int v = verts[i];
+		const MVert *mvert = &bvh->verts[v];
+
+		if (mvert->flag & ME_VERT_PBVH_UPDATE) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
 /********************************* Raycast ***********************************/
 
 typedef struct {
@@ -1914,8 +1938,11 @@ void BKE_pbvh_apply_vertCos(PBVH *pbvh, float (*vertCos)[3])
 		MVert *mvert = pbvh->verts;
 		/* copy new verts coords */
 		for (int a = 0; a < pbvh->totvert; ++a, ++mvert) {
-			copy_v3_v3(mvert->co, vertCos[a]);
-			mvert->flag |= ME_VERT_PBVH_UPDATE;
+			/* no need for float comparison here (memory is exactly equal or not) */
+			if (memcmp(mvert->co, vertCos[a], sizeof(float[3])) != 0) {
+				copy_v3_v3(mvert->co, vertCos[a]);
+				mvert->flag |= ME_VERT_PBVH_UPDATE;
+			}
 		}
 
 		/* coordinates are new -- normals should also be updated */
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 46cfc1c..3b7cd2f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -79,28 +79,57 @@ static void update_cb(PBVHNode *node, void *rebuild)
 	BKE_pbvh_node_fully_hidden_set(node, 0);
 }
 
-static void sculpt_undo_restore_deformed(const SculptSession *ss,
-                                         SculptUndoNode *unode,
-                                         int uindex, int oindex,
-                                         float coord[3])
+struct PartialUpdateData {
+	PBVH *pbvh;
+	bool rebuild;
+};
+
+/**
+ * A version of #update_cb that tests for 'ME_VERT_PBVH_UPDATE'
+ */
+static void update_cb_partial(PBVHNode *node, void *userdata)
+{
+	struct PartialUpdateData *data = userdata;
+	if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) {
+		update_cb(node, &(data->rebuild));
+	}
+}
+
+static bool test_swap_v3_v3(float a[3], float b[3])
+{
+	/* no need for float comparison here (memory is exactly equal or not) */
+	if (memcmp(a, b, sizeof(float[3])) != 0) {
+		swap_v3_v3(a, b);
+		return true;
+	}
+	else {
+		return false;
+	}
+}
+
+static bool sculpt_undo_restore_deformed(
+        const SculptSession *ss,
+        SculptUndoNode *unode,
+        int uindex, int oindex,
+        float coord[3])
 {
-	if (unode->orig_co) {
-		swap_v3_v3(coord, unode->orig_co[uindex]);
+	if (test_swap_v3_v3(coord, unode->orig_co[uindex])) {
 		copy_v3_v3(unode->co[uindex], ss->deform_cos[oindex]);
+		return true;
 	}
 	else {
-		swap_v3_v3(coord, unode->co[uindex]);
+		return false;
 	}
 }
 
-static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNode *unode)
+static bool sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNode *unode)
 {
 	Scene *scene = CTX_data_scene(C);
 	Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
 	Object *ob = CTX_data_active_object(C);
 	SculptSession *ss = ob->sculpt;
 	MVert *mvert;
-	int *index, i, j;
+	int *index;
 	
 	if (unode->maxvert) {
 		/* regular mesh restore */
@@ -123,6 +152,7 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo
 			}
 		}
 
+		/* no need for float comparison here (memory is exactly equal or not) */
 		index = unode->index;
 		mvert = ss->mvert;
 
@@ -130,13 +160,21 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo
 			float (*vertCos)[3];
 			vertCos = BKE_keyblock_convert_to_vertcos(ob, ss->kb);
 
-			for (i = 0; i < unode->totvert; i++) {
+			if (unode->orig_co) {
 				if (ss->modifiers_active) {
-					sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]);
+					for (int i = 0; i < unode->totvert; i++) {
+						sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]);
+					}
 				}
 				else {
-					if (unode->orig_co) swap_v3_v3(vertCos[index[i]], unode->orig_co[i]);
-					else swap_v3_v3(vertCos[index[i]], unode->co[i]);
+					for (int i = 0; i < unode->totvert; i++) {
+						swap_v3_v3(vertCos[index[i]], unode->orig_co[i]);
+					}
+				}
+			}
+			else {
+				for (int i = 0; i < unode->totvert; i++) {
+					swap_v3_v3(vertCos[index[i]], unode->co[i]);
 				}
 			}
 
@@ -150,15 +188,28 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo
 			MEM_freeN(vertCos);
 		}
 		else {
-			for (i = 0; i < unode->totvert; i++) {
+			if (unode->orig_co) {
 				if (ss->modifiers_active) {
-					sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co);
+					for (int i = 0; i < unode->totvert; i++) {
+						if (sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co)) {
+							mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+						}
+					}
 				}
 				else {
-					if (unode->orig_co) swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]);
-					else swap_v3_v3(mvert[index[i]].co, unode->co[i]);
+					for (int i = 0; i < unode->totvert; i++) {
+						if (test_swap_v3_v3(mvert[index[i]].co, unode->orig_co[i])) {
+							mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+						}
+					}
+				}
+			}
+			else {
+				for (int i = 0; i < unode->totvert; i++) {
+					if (test_swap_v3_v3(mvert[index[i]].co, unode->co[i])) {
+						mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+					}
 				}
-				mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
 			}
 		}
 	}
@@ -174,19 +225,21 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo
 		dm->getGridKey(dm, &key);
 
 		co = unode->co;
-		for (j = 0; j < unode->totgrid; j++) {
+		for (int j = 0; j < unode->totgrid; j++) {
 			grid = grids[unode->grids[j]];
 
-			for (i = 0; i < gridsize * gridsize; i++, co++)
+			for (int i = 0; i < gridsize * gridsize; i++, co++) {
 				swap_v3_v3(CCG_elem_offset_co(&key, grid, i), co[0]);
+			}
 		}
 	}
 
 	return 1;
 }
 
-static int sculpt_undo_restore_hidden(bContext *C, DerivedMesh *dm,
-                                      SculptUndoNode *unode)
+static bool sculpt_undo_restore_hidden(
+        bContext *C, DerivedMesh *dm,
+        SculptUndoNode *unode)
 {
 	Object *ob = CTX_data_active_object(C);
 	SculptSession *ss = ob->sculpt;
@@ -197,16 +250,11 @@ static int sculpt_undo_restore_hidden(bContext *C, DerivedMesh *dm,
 		
 		for (i = 0; i < unode->totvert; i++) {
 			MVert *v = &mvert[unode->index[i]];
-			int uval = BLI_BITMAP_TEST(unode->vert_hidden, i);
-
-			BLI_BITMAP_SET(unode->vert_hidden, i,
-			                  v->flag & ME_HIDE);
-			if (uval)
-				v->flag |= ME_HIDE;
-			else
-				v->flag &= ~ME_HIDE;
-			
-			v->flag |= ME_VERT_PBVH_UPDATE;
+			if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) {
+				BLI_BITMAP_FLIP(unode->vert_hidden, i);
+				v->flag ^= ME_HIDE;
+				v->flag |= ME_VERT_PBVH_UPDATE;
+			}
 		}
 	}
 	else if (unode->maxgrid && dm->getGridData) {
@@ -223,7 +271,7 @@ static int sculpt_undo_restore_hidden(bContext *C, DerivedMesh *dm,
 	return 1;
 }
 
-static int sculpt_undo_restore_mask(bContext *C, DerivedMesh *dm, SculptUndoNode *unode)
+static bool sculpt_undo_restore_mask(bContext *C, DerivedMesh *dm, SculptUndoNode *unode)
 {
 	Object *ob = CTX_data_active_object(C);
 	SculptSession *ss = ob->sculpt;
@@ -239,8 +287,10 @@ static int sculpt_undo_restore_mask(bContext *C, DerivedMesh *dm, SculptUndoNode
 		vmask = ss->vmask;
 
 		for (i = 0; i < unode->totvert; i++) {
-			SWAP(float, vmask[index[i]], unode->mask[i]);
-			mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+			if (vmask[index[i]] != unode->mask[i]) {
+				SWAP(float, vmask[index[i]], unode->mask[i]);
+				mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+			}
 		}
 	}
 	else if (unode->maxgrid && dm->getGridData) {
@@ -404,6 +454,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
 	SculptUndoNode *unode;
 	bool update = false, rebuild = false;
 	bool need_mask = false;
+	bool partial_update = true;
 
 	for (unode = lb->first; unode; unode = unode->next) {
 		if (STREQ(unode->idname, ob->id.name)) {
@@ -440,6 +491,9 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
 			{
 				continue;
 			}
+
+			/* multi-res can't do partial updates since it doesn't flag edited vertices */
+			partial_update = false;
 		}
 
 		switch (unode->type) {
@@ -469,7 +523,16 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
 		/* we update all nodes still, should be more clever, but also
 		 * needs to work correct when exiting/entering sculpt mode and
 		 * the nodes get recreated, though in that case it could do all */
-		BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
+		if (partial_update) {
+			struct PartialUpdateData data = {
+				.rebuild = rebuild,
+				.pbvh = ss->pbvh,
+			};
+			BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data);
+		}
+		else {
+			BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
+		}
 		BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_Updat

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list