Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/mesh/editmesh_undo.c')
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c102
1 files changed, 86 insertions, 16 deletions
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 79385e28aa9..112de68b52c 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -93,6 +93,12 @@ typedef struct BArrayCustomData {
#endif
typedef struct UndoMesh {
+ /**
+ * This undo-meshes in `um_arraystore.local_links`.
+ * Not to be confused with the next and previous undo steps.
+ */
+ struct UndoMesh *local_next, *local_prev;
+
Mesh me;
int selectmode;
@@ -128,7 +134,10 @@ static struct {
struct BArrayStore_AtSize bs_stride;
int users;
- /* We could have the undo API pass in the previous state, for now store a local list */
+ /**
+ * A list of #UndoMesh items ordered from oldest to newest
+ * used to access previous undo data for a mesh.
+ */
ListBase local_links;
# ifdef USE_ARRAY_STORE_THREAD
@@ -520,11 +529,63 @@ static void um_arraystore_free(UndoMesh *um)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Array Store Utilities
+ * \{ */
+
+/**
+ * Create an array of #UndoMesh from `objects`.
+ *
+ * where each element in the resulting array is the most recently created
+ * undo-mesh for the object's mesh.
+ * When no undo-mesh can be found that array index is NULL.
+ *
+ * This is used for de-duplicating memory between undo steps,
+ * failure to find the undo step will store a full duplicate in memory.
+ * define `DEBUG_PRINT` to check memory is de-duplicating as expected.
+ */
+static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, int object_len)
+{
+ /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */
+ GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len);
+ UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__);
+ for (int i = 0; i < object_len; i++) {
+ const Mesh *me = object[i]->data;
+ BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]);
+ }
+ int uuid_map_len = object_len;
+
+ /* Loop backwards over all previous mesh undo data until either:
+ * - All elements have been found (where `um_references` we'll have every element set).
+ * - There are no undo steps left to look for. */
+ UndoMesh *um_iter = um_arraystore.local_links.last;
+ while (um_iter && (uuid_map_len != 0)) {
+ UndoMesh **um_p;
+ if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) {
+ *um_p = um_iter;
+ uuid_map_len--;
+ }
+ um_iter = um_iter->local_prev;
+ }
+ BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map));
+ BLI_ghash_free(uuid_map, NULL, NULL);
+ if (uuid_map_len == object_len) {
+ MEM_freeN(um_references);
+ um_references = NULL;
+ }
+ return um_references;
+}
+
+/** \} */
+
#endif /* USE_ARRAY_STORE */
/* for callbacks */
/* undo simply makes copies of a bmesh */
-static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
+/**
+ * \param um_ref: The reference to use for de-duplicating memory between undo-steps.
+ */
+static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref)
{
BLI_assert(BLI_array_is_zeroed(um, 1));
#ifdef USE_ARRAY_STORE_THREAD
@@ -560,14 +621,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
#ifdef USE_ARRAY_STORE
{
- /* We could be more clever here,
- * the previous undo state may be from a separate mesh. */
- const UndoMesh *um_ref = um_arraystore.local_links.last ?
- ((LinkData *)um_arraystore.local_links.last)->data :
- NULL;
-
/* Add ourselves. */
- BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um));
+ BLI_addtail(&um_arraystore.local_links, um);
# ifdef USE_ARRAY_STORE_THREAD
if (um_arraystore.task_pool == NULL) {
@@ -583,6 +638,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
um_arraystore_compact_with_info(um, um_ref);
# endif
}
+#else
+ UNUSED_VARS(um_ref);
#endif
return um;
@@ -682,11 +739,9 @@ static void undomesh_free_data(UndoMesh *um)
/* we need to expand so any allocations in custom-data are freed with the mesh */
um_arraystore_expand(um);
- {
- LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data));
- BLI_remlink(&um_arraystore.local_links, link);
- MEM_freeN(link);
- }
+ BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1);
+ BLI_remlink(&um_arraystore.local_links, um);
+
um_arraystore_free(um);
#endif
@@ -720,7 +775,6 @@ static Object *editmesh_object_from_context(bContext *C)
* \{ */
typedef struct MeshUndoStep_Elem {
- struct MeshUndoStep_Elem *next, *prev;
UndoRefID_Object obedit_ref;
UndoMesh data;
} MeshUndoStep_Elem;
@@ -749,6 +803,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
+ UndoMesh **um_references = NULL;
+
+#ifdef USE_ARRAY_STORE
+ um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len);
+#endif
+
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
MeshUndoStep_Elem *elem = &us->elems[i];
@@ -756,12 +816,22 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
elem->obedit_ref.ptr = ob;
Mesh *me = elem->obedit_ref.ptr->data;
BMEditMesh *em = me->edit_mesh;
- undomesh_from_editmesh(&elem->data, me->edit_mesh, me->key);
+ undomesh_from_editmesh(
+ &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL);
em->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
+
+#ifdef USE_ARRAY_STORE
+ /** As this is only data storage it is safe to set the session ID here. */
+ elem->data.me.id.session_uuid = me->id.session_uuid;
+#endif
}
MEM_freeN(objects);
+ if (um_references != NULL) {
+ MEM_freeN(um_references);
+ }
+
bmain->is_memfile_undo_flush_needed = true;
return true;