Blender  V2.93
memfile_undo.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include "BLI_sys_types.h"
24 #include "BLI_utildefines.h"
25 
26 #include "BLI_ghash.h"
27 #include "BLI_listbase.h"
28 
29 #include "DNA_ID.h"
30 #include "DNA_collection_types.h"
31 #include "DNA_node_types.h"
32 #include "DNA_object_enums.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35 
36 #include "BKE_blender_undo.h"
37 #include "BKE_context.h"
38 #include "BKE_lib_id.h"
39 #include "BKE_lib_query.h"
40 #include "BKE_main.h"
41 #include "BKE_node.h"
42 #include "BKE_scene.h"
43 #include "BKE_undo_system.h"
44 
45 #include "../depsgraph/DEG_depsgraph.h"
46 
47 #include "WM_api.h"
48 #include "WM_types.h"
49 
50 #include "ED_object.h"
51 #include "ED_undo.h"
52 #include "ED_util.h"
53 
54 #include "../blenloader/BLO_undofile.h"
55 
56 #include "undo_intern.h"
57 
58 #include <stdio.h>
59 
60 /* -------------------------------------------------------------------- */
64 typedef struct MemFileUndoStep {
68 
70 {
71  /* other poll functions must run first, this is a catch-all. */
72 
73  if ((U.uiflag & USER_GLOBALUNDO) == 0) {
74  return false;
75  }
76 
77  /* Allow a single memfile undo step (the first). */
78  UndoStack *ustack = ED_undo_stack_get();
79  if ((ustack->step_active != NULL) && (ED_undo_is_memfile_compatible(C) == false)) {
80  return false;
81  }
82  return true;
83 }
84 
86  struct Main *bmain,
87  UndoStep *us_p)
88 {
89  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
90 
91  /* Important we only use 'main' from the context (see: BKE_undosys_stack_init_from_main). */
92  UndoStack *ustack = ED_undo_stack_get();
93 
94  if (bmain->is_memfile_undo_flush_needed) {
95  ED_editors_flush_edits_ex(bmain, false, true);
96  }
97 
98  /* can be NULL, use when set. */
100  ustack, BKE_UNDOSYS_TYPE_MEMFILE);
101  us->data = BKE_memfile_undo_encode(bmain, us_prev ? us_prev->data : NULL);
102  us->step.data_size = us->data->undo_size;
103 
104  /* Store the fact that we should not re-use old data with that undo step, and reset the Main
105  * flag. */
107  bmain->use_memfile_full_barrier = false;
108 
109  return true;
110 }
111 
113 {
114  ID *id_self = cb_data->id_self;
115  ID **id_pointer = cb_data->id_pointer;
116  BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0);
117 
118  ID *id = *id_pointer;
119  if (id != NULL && id->lib == NULL && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) {
120  bool do_stop_iter = true;
121  if (GS(id_self->name) == ID_OB) {
122  Object *ob_self = (Object *)id_self;
123  if (ob_self->type == OB_ARMATURE) {
124  if (ob_self->data == id) {
125  BLI_assert(GS(id->name) == ID_AR);
126  if (ob_self->pose != NULL) {
127  /* We have a changed/re-read armature used by an unchanged armature object: our beloved
128  * Bone pointers from the object's pose need their usual special treatment. */
129  ob_self->pose->flag |= POSE_RECALC;
130  }
131  }
132  else {
133  /* Cannot stop iteration until we checked ob_self->data pointer... */
134  do_stop_iter = false;
135  }
136  }
137  }
138 
139  return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP;
140  }
141 
142  return IDWALK_RET_NOP;
143 }
144 
146  struct Main *bmain,
147  UndoStep *us_p,
148  const eUndoStepDir undo_direction,
149  bool UNUSED(is_final))
150 {
151  BLI_assert(undo_direction != STEP_INVALID);
152 
153  bool use_old_bmain_data = true;
154 
155  if (USER_EXPERIMENTAL_TEST(&U, use_undo_legacy) || !(U.uiflag & USER_GLOBALUNDO)) {
156  use_old_bmain_data = false;
157  }
158  else if (undo_direction == STEP_REDO) {
159  /* The only time we should have to force a complete redo is when current step is tagged as a
160  * redo barrier.
161  * If previous step was not a memfile one should not matter here, current data in old bmain
162  * should still always be valid for unchanged data-blocks. */
163  if (us_p->use_old_bmain_data == false) {
164  use_old_bmain_data = false;
165  }
166  }
167  else if (undo_direction == STEP_UNDO) {
168  /* Here we do not care whether current step is an undo barrier, since we are coming from
169  * 'the future' we can still re-use old data. However, if *next* undo step
170  * (i.e. the one immediately in the future, the one we are coming from)
171  * is a barrier, then we have to force a complete undo.
172  * Note that non-memfile undo steps **should** not be an issue anymore, since we handle
173  * fine-grained update flags now.
174  */
175  UndoStep *us_next = us_p->next;
176  if (us_next != NULL) {
177  if (us_next->use_old_bmain_data == false) {
178  use_old_bmain_data = false;
179  }
180  }
181  }
182 
183  /* Extract depsgraphs from current bmain (which may be freed during undo step reading),
184  * and store them for re-use. */
185  GHash *depsgraphs = NULL;
186  if (use_old_bmain_data) {
187  depsgraphs = BKE_scene_undo_depsgraphs_extract(bmain);
188  }
189 
190  ED_editors_exit(bmain, false);
191 
192  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
193  BKE_memfile_undo_decode(us->data, undo_direction, use_old_bmain_data, C);
194 
195  for (UndoStep *us_iter = us_p->next; us_iter; us_iter = us_iter->next) {
196  if (BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(us_iter->type)) {
197  continue;
198  }
199  us_iter->is_applied = false;
200  }
201  for (UndoStep *us_iter = us_p; us_iter; us_iter = us_iter->prev) {
202  if (BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(us_iter->type)) {
203  continue;
204  }
205  us_iter->is_applied = true;
206  }
207 
208  /* bmain has been freed. */
209  bmain = CTX_data_main(C);
211 
212  if (use_old_bmain_data) {
213  /* Restore previous depsgraphs into current bmain. */
214  BKE_scene_undo_depsgraphs_restore(bmain, depsgraphs);
215 
216  /* We need to inform depsgraph about re-used old IDs that would be using newly read
217  * data-blocks, at least COW evaluated copies need to be updated... */
218  ID *id = NULL;
219  FOREACH_MAIN_ID_BEGIN (bmain, id) {
223  }
224 
225  /* Tag depsgraph to update data-block for changes that happened between the
226  * current and the target state, see direct_link_id_restore_recalc(). */
227  if (id->recalc) {
228  DEG_id_tag_update_ex(bmain, id, id->recalc);
229  }
230  }
232 
233  FOREACH_MAIN_ID_BEGIN (bmain, id) {
234  /* Clear temporary tag. */
235  id->tag &= ~LIB_TAG_UNDO_OLD_ID_REUSED;
236 
237  /* We only start accumulating from this point, any tags set up to here
238  * are already part of the current undo state. This is done in a second
239  * loop because DEG_id_tag_update may set tags on other datablocks. */
240  id->recalc_after_undo_push = 0;
241  bNodeTree *nodetree = ntreeFromID(id);
242  if (nodetree != NULL) {
243  nodetree->id.recalc_after_undo_push = 0;
244  }
245  if (GS(id->name) == ID_SCE) {
246  Scene *scene = (Scene *)id;
247  if (scene->master_collection != NULL) {
249  }
250  }
251  }
253  }
254 
256 }
257 
259 {
260  /* To avoid unnecessary slow down, free backwards
261  * (so we don't need to merge when clearing all). */
262  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
263  if (us_p->next != NULL) {
264  UndoStep *us_next_p = BKE_undosys_step_same_type_next(us_p);
265  if (us_next_p != NULL) {
266  MemFileUndoStep *us_next = (MemFileUndoStep *)us_next_p;
267  BLO_memfile_merge(&us->data->memfile, &us_next->data->memfile);
268  }
269  }
270 
272 }
273 
274 /* Export for ED_undo_sys. */
276 {
277  ut->name = "Global Undo";
282 
283  ut->flags = 0;
284 
285  ut->step_size = sizeof(MemFileUndoStep);
286 }
287 
290 /* -------------------------------------------------------------------- */
299 {
300  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
301  return &us->data->memfile;
302 }
303 
305 {
307  if (us) {
308  return ed_undosys_step_get_memfile(us);
309  }
310  return NULL;
311 }
312 
329 {
330  UndoStep *us = ustack->step_active;
331  if (id == NULL || us == NULL || us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
332  return;
333  }
334 
335  MemFile *memfile = &((MemFileUndoStep *)us)->data->memfile;
336  LISTBASE_FOREACH (MemFileChunk *, mem_chunk, &memfile->chunks) {
337  if (mem_chunk->id_session_uuid == id->session_uuid) {
338  mem_chunk->is_identical_future = false;
339  break;
340  }
341  }
342 }
343 
struct MemFileUndoData * BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev)
Definition: blender_undo.c:114
void BKE_memfile_undo_free(struct MemFileUndoData *mfu)
Definition: blender_undo.c:153
bool BKE_memfile_undo_decode(struct MemFileUndoData *mfu, const enum eUndoStepDir undo_direction, const bool use_old_bmain_data, struct bContext *C)
Definition: blender_undo.c:65
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
@ IDWALK_READONLY
void BKE_library_foreach_ID_link(struct Main *bmain, struct ID *id, LibraryIDLinkCallback callback, void *user_data, int flag)
Definition: lib_query.c:322
@ IDWALK_RET_STOP_ITER
Definition: BKE_lib_query.h:99
@ IDWALK_RET_NOP
Definition: BKE_lib_query.h:97
#define FOREACH_MAIN_ID_END
Definition: BKE_main.h:250
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition: BKE_main.h:244
struct bNodeTree * ntreeFromID(struct ID *id)
Definition: node.cc:3147
struct GHash * BKE_scene_undo_depsgraphs_extract(struct Main *bmain)
Definition: scene.c:3549
void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract)
Definition: scene.c:3579
const UndoType * BKE_UNDOSYS_TYPE_MEMFILE
Definition: undo_system.c:101
eUndoStepDir
@ STEP_INVALID
@ STEP_UNDO
@ STEP_REDO
#define BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(ty)
UndoStep * BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut)
Definition: undo_system.c:381
UndoStep * BKE_undosys_step_same_type_next(UndoStep *us)
Definition: undo_system.c:619
UndoStep * BKE_undosys_step_find_by_type(UndoStack *ustack, const UndoType *ut)
Definition: undo_system.c:667
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define UNUSED(x)
void BLO_memfile_merge(MemFile *first, MemFile *second)
Definition: undofile.c:73
void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag)
ID and Library types, which are fundamental for sdna.
@ LIB_TAG_UNDO_OLD_ID_REUSED
Definition: DNA_ID.h:580
@ ID_AR
Definition: DNA_ID_enums.h:78
@ ID_SCE
Definition: DNA_ID_enums.h:57
@ ID_OB
Definition: DNA_ID_enums.h:59
@ POSE_RECALC
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ USER_GLOBALUNDO
#define USER_EXPERIMENTAL_TEST(userdef, member)
bool ED_undo_is_memfile_compatible(const struct bContext *C)
struct UndoStack * ED_undo_stack_get(void)
Definition: ed_undo.c:501
void ED_editors_init_for_undo(struct Main *bmain)
Definition: ed_util.c:68
void ED_editors_exit(struct Main *bmain, bool do_undo_system)
Definition: ed_util.c:181
bool ED_editors_flush_edits_ex(struct Main *bmain, bool for_render, bool check_needs_flush)
Definition: ed_util.c:278
#define C
Definition: RandGen.cpp:39
#define NC_SCENE
Definition: WM_types.h:279
#define ND_LAYER_CONTENT
Definition: WM_types.h:354
unsigned int U
Definition: btGjkEpa3.h:78
Scene scene
#define GS(x)
Definition: iris.c:241
struct MemFileUndoStep MemFileUndoStep
void ED_undosys_stack_memfile_id_changed_tag(UndoStack *ustack, ID *id)
Definition: memfile_undo.c:328
static struct MemFile * ed_undosys_step_get_memfile(UndoStep *us_p)
Definition: memfile_undo.c:298
static void memfile_undosys_step_free(UndoStep *us_p)
Definition: memfile_undo.c:258
static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
Definition: memfile_undo.c:112
struct MemFile * ED_undosys_stack_memfile_get_active(UndoStack *ustack)
Definition: memfile_undo.c:304
static bool memfile_undosys_poll(bContext *C)
Definition: memfile_undo.c:69
static bool memfile_undosys_step_encode(struct bContext *UNUSED(C), struct Main *bmain, UndoStep *us_p)
Definition: memfile_undo.c:85
static void memfile_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, const eUndoStepDir undo_direction, bool UNUSED(is_final))
Definition: memfile_undo.c:145
void ED_memfile_undosys_type(UndoType *ut)
Definition: memfile_undo.c:275
Definition: DNA_ID.h:273
int tag
Definition: DNA_ID.h:292
struct Library * lib
Definition: DNA_ID.h:277
int recalc_after_undo_push
Definition: DNA_ID.h:306
int recalc
Definition: DNA_ID.h:295
unsigned int session_uuid
Definition: DNA_ID.h:312
char name[66]
Definition: DNA_ID.h:283
Definition: BKE_main.h:116
char is_memfile_undo_flush_needed
Definition: BKE_main.h:130
char use_memfile_full_barrier
Definition: BKE_main.h:135
MemFileUndoData * data
Definition: memfile_undo.c:66
ListBase chunks
Definition: BLO_undofile.h:47
struct bPose * pose
void * data
struct Collection * master_collection
struct UndoStep * step_active
const struct UndoType * type
size_t data_size
struct UndoStep * prev
bool use_old_bmain_data
struct UndoStep * next
size_t step_size
void(* step_decode)(struct bContext *C, struct Main *bmain, UndoStep *us, const eUndoStepDir dir, bool is_final)
bool(* step_encode)(struct bContext *C, struct Main *bmain, UndoStep *us)
const char * name
void(* step_free)(UndoStep *us)
bool(* poll)(struct bContext *C)
short flag
void WM_event_add_notifier(const bContext *C, uint type, void *reference)