Blender  V3.3
memfile_undo.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
9 #include "BLI_sys_types.h"
10 #include "BLI_utildefines.h"
11 
12 #include "BLI_ghash.h"
13 #include "BLI_listbase.h"
14 
15 #include "DNA_ID.h"
16 #include "DNA_collection_types.h"
17 #include "DNA_node_types.h"
18 #include "DNA_object_enums.h"
19 #include "DNA_object_types.h"
20 #include "DNA_scene_types.h"
21 
22 #include "BKE_blender_undo.h"
23 #include "BKE_context.h"
24 #include "BKE_icons.h"
25 #include "BKE_lib_id.h"
26 #include "BKE_lib_query.h"
27 #include "BKE_main.h"
28 #include "BKE_node.h"
29 #include "BKE_scene.h"
30 #include "BKE_undo_system.h"
31 
32 #include "../depsgraph/DEG_depsgraph.h"
33 
34 #include "WM_api.h"
35 #include "WM_types.h"
36 
37 #include "ED_object.h"
38 #include "ED_render.h"
39 #include "ED_undo.h"
40 #include "ED_util.h"
41 
42 #include "../blenloader/BLO_undofile.h"
43 
44 #include "undo_intern.h"
45 
46 #include <stdio.h>
47 
48 /* -------------------------------------------------------------------- */
52 typedef struct MemFileUndoStep {
56 
58 {
59  /* other poll functions must run first, this is a catch-all. */
60 
61  if ((U.uiflag & USER_GLOBALUNDO) == 0) {
62  return false;
63  }
64 
65  /* Allow a single memfile undo step (the first). */
66  UndoStack *ustack = ED_undo_stack_get();
67  if ((ustack->step_active != NULL) && (ED_undo_is_memfile_compatible(C) == false)) {
68  return false;
69  }
70  return true;
71 }
72 
74  struct Main *bmain,
75  UndoStep *us_p)
76 {
77  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
78 
79  /* Important we only use 'main' from the context (see: BKE_undosys_stack_init_from_main). */
80  UndoStack *ustack = ED_undo_stack_get();
81 
82  if (bmain->is_memfile_undo_flush_needed) {
83  ED_editors_flush_edits_ex(bmain, false, true);
84  }
85 
86  /* can be NULL, use when set. */
88  ustack, BKE_UNDOSYS_TYPE_MEMFILE);
89  us->data = BKE_memfile_undo_encode(bmain, us_prev ? us_prev->data : NULL);
90  us->step.data_size = us->data->undo_size;
91 
92  /* Store the fact that we should not re-use old data with that undo step, and reset the Main
93  * flag. */
95  bmain->use_memfile_full_barrier = false;
96 
97  return true;
98 }
99 
101 {
102  ID *id_self = cb_data->id_self;
103  ID **id_pointer = cb_data->id_pointer;
104  BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0);
105 
106  ID *id = *id_pointer;
107  if (id != NULL && !ID_IS_LINKED(id) && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) {
108  bool do_stop_iter = true;
109  if (GS(id_self->name) == ID_OB) {
110  Object *ob_self = (Object *)id_self;
111  if (ob_self->type == OB_ARMATURE) {
112  if (ob_self->data == id) {
113  BLI_assert(GS(id->name) == ID_AR);
114  if (ob_self->pose != NULL) {
115  /* We have a changed/re-read armature used by an unchanged armature object: our beloved
116  * Bone pointers from the object's pose need their usual special treatment. */
117  ob_self->pose->flag |= POSE_RECALC;
118  }
119  }
120  else {
121  /* Cannot stop iteration until we checked ob_self->data pointer... */
122  do_stop_iter = false;
123  }
124  }
125  }
126 
127  return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP;
128  }
129 
130  return IDWALK_RET_NOP;
131 }
132 
141 {
143  if (!preview) {
144  return;
145  }
146 
147  for (int i = 0; i < NUM_ICON_SIZES; i++) {
148  if (preview->flag[i] & PRV_USER_EDITED) {
149  /* Don't modify custom previews. */
150  continue;
151  }
152 
155  }
156  }
157 }
158 
160  struct Main *bmain,
161  UndoStep *us_p,
162  const eUndoStepDir undo_direction,
163  bool UNUSED(is_final))
164 {
165  BLI_assert(undo_direction != STEP_INVALID);
166 
167  bool use_old_bmain_data = true;
168 
169  if (USER_EXPERIMENTAL_TEST(&U, use_undo_legacy) || !(U.uiflag & USER_GLOBALUNDO)) {
170  use_old_bmain_data = false;
171  }
172  else if (undo_direction == STEP_REDO) {
173  /* The only time we should have to force a complete redo is when current step is tagged as a
174  * redo barrier.
175  * If previous step was not a memfile one should not matter here, current data in old bmain
176  * should still always be valid for unchanged data-blocks. */
177  if (us_p->use_old_bmain_data == false) {
178  use_old_bmain_data = false;
179  }
180  }
181  else if (undo_direction == STEP_UNDO) {
182  /* Here we do not care whether current step is an undo barrier, since we are coming from
183  * 'the future' we can still re-use old data. However, if *next* undo step
184  * (i.e. the one immediately in the future, the one we are coming from)
185  * is a barrier, then we have to force a complete undo.
186  * Note that non-memfile undo steps **should** not be an issue anymore, since we handle
187  * fine-grained update flags now.
188  */
189  UndoStep *us_next = us_p->next;
190  if (us_next != NULL) {
191  if (us_next->use_old_bmain_data == false) {
192  use_old_bmain_data = false;
193  }
194  }
195  }
196 
197  /* Extract depsgraphs from current bmain (which may be freed during undo step reading),
198  * and store them for re-use. */
199  GHash *depsgraphs = NULL;
200  if (use_old_bmain_data) {
201  depsgraphs = BKE_scene_undo_depsgraphs_extract(bmain);
202  }
203 
204  ED_editors_exit(bmain, false);
205  /* Ensure there's no preview job running. Unfinished previews will be scheduled for regeneration
206  * via #memfile_undosys_unfinished_id_previews_restart(). */
208 
209  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
210  BKE_memfile_undo_decode(us->data, undo_direction, use_old_bmain_data, C);
211 
212  for (UndoStep *us_iter = us_p->next; us_iter; us_iter = us_iter->next) {
213  if (BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(us_iter->type)) {
214  continue;
215  }
216  us_iter->is_applied = false;
217  }
218  for (UndoStep *us_iter = us_p; us_iter; us_iter = us_iter->prev) {
219  if (BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(us_iter->type)) {
220  continue;
221  }
222  us_iter->is_applied = true;
223  }
224 
225  /* bmain has been freed. */
226  bmain = CTX_data_main(C);
228 
229  if (use_old_bmain_data) {
230  /* Restore previous depsgraphs into current bmain. */
231  BKE_scene_undo_depsgraphs_restore(bmain, depsgraphs);
232 
233  /* We need to inform depsgraph about re-used old IDs that would be using newly read
234  * data-blocks, at least COW evaluated copies need to be updated... */
235  ID *id = NULL;
236  FOREACH_MAIN_ID_BEGIN (bmain, id) {
240  }
241 
242  /* Tag depsgraph to update data-block for changes that happened between the
243  * current and the target state, see direct_link_id_restore_recalc(). */
244  if (id->recalc != 0) {
245  DEG_id_tag_update_ex(bmain, id, id->recalc);
246  }
247 
248  bNodeTree *nodetree = ntreeFromID(id);
249  if (nodetree != NULL && nodetree->id.recalc != 0) {
250  DEG_id_tag_update_ex(bmain, &nodetree->id, nodetree->id.recalc);
251  }
252  if (GS(id->name) == ID_SCE) {
253  Scene *scene = (Scene *)id;
257  }
258  }
259 
260  /* Restart preview generation if the undo state was generating previews. */
262  }
264 
265  FOREACH_MAIN_ID_BEGIN (bmain, id) {
266  /* Clear temporary tag. */
267  id->tag &= ~LIB_TAG_UNDO_OLD_ID_REUSED;
268 
269  /* We only start accumulating from this point, any tags set up to here
270  * are already part of the current undo state. This is done in a second
271  * loop because DEG_id_tag_update may set tags on other datablocks. */
272  id->recalc_after_undo_push = 0;
273  bNodeTree *nodetree = ntreeFromID(id);
274  if (nodetree != NULL) {
275  nodetree->id.recalc_after_undo_push = 0;
276  }
277  if (GS(id->name) == ID_SCE) {
278  Scene *scene = (Scene *)id;
279  if (scene->master_collection != NULL) {
281  }
282  }
283  }
285  }
286  else {
287  ID *id = NULL;
288  FOREACH_MAIN_ID_BEGIN (bmain, id) {
289  /* Restart preview generation if the undo state was generating previews. */
291  }
293  }
294 
296 }
297 
299 {
300  /* To avoid unnecessary slow down, free backwards
301  * (so we don't need to merge when clearing all). */
302  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
303  if (us_p->next != NULL) {
304  UndoStep *us_next_p = BKE_undosys_step_same_type_next(us_p);
305  if (us_next_p != NULL) {
306  MemFileUndoStep *us_next = (MemFileUndoStep *)us_next_p;
307  BLO_memfile_merge(&us->data->memfile, &us_next->data->memfile);
308  }
309  }
310 
312 }
313 
315 {
316  ut->name = "Global Undo";
321 
322  ut->flags = 0;
323 
324  ut->step_size = sizeof(MemFileUndoStep);
325 }
326 
329 /* -------------------------------------------------------------------- */
338 {
339  MemFileUndoStep *us = (MemFileUndoStep *)us_p;
340  return &us->data->memfile;
341 }
342 
344 {
346  if (us) {
347  return ed_undosys_step_get_memfile(us);
348  }
349  return NULL;
350 }
351 
353 {
354  UndoStep *us = ustack->step_active;
355  if (id == NULL || us == NULL || us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
356  return;
357  }
358 
359  MemFile *memfile = &((MemFileUndoStep *)us)->data->memfile;
360  LISTBASE_FOREACH (MemFileChunk *, mem_chunk, &memfile->chunks) {
361  if (mem_chunk->id_session_uuid == id->session_uuid) {
362  mem_chunk->is_identical_future = false;
363  break;
364  }
365  }
366 }
367 
struct MemFileUndoData * BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev)
Definition: blender_undo.c:101
void BKE_memfile_undo_free(struct MemFileUndoData *mfu)
Definition: blender_undo.c:140
bool BKE_memfile_undo_decode(struct MemFileUndoData *mfu, enum eUndoStepDir undo_direction, bool use_old_bmain_data, struct bContext *C)
Definition: blender_undo.c:51
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct PreviewImage * BKE_previewimg_id_get(const struct ID *id)
bool BKE_previewimg_is_finished(const struct PreviewImage *prv, int size)
void BKE_library_foreach_ID_link(struct Main *bmain, struct ID *id, LibraryIDLinkCallback callback, void *user_data, int flag)
Definition: lib_query.c:350
@ IDWALK_READONLY
@ IDWALK_RET_STOP_ITER
Definition: BKE_lib_query.h:85
@ IDWALK_RET_NOP
Definition: BKE_lib_query.h:83
#define FOREACH_MAIN_ID_END
Definition: BKE_main.h:367
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition: BKE_main.h:361
struct bNodeTree * ntreeFromID(struct ID *id)
Definition: node.cc:3231
struct GHash * BKE_scene_undo_depsgraphs_extract(struct Main *bmain)
Definition: scene.cc:3479
void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract)
Definition: scene.cc:3508
const UndoType * BKE_UNDOSYS_TYPE_MEMFILE
Definition: undo_system.c:54
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:364
UndoStep * BKE_undosys_step_same_type_next(UndoStep *us)
Definition: undo_system.c:604
UndoStep * BKE_undosys_step_find_by_type(UndoStack *ustack, const UndoType *ut)
Definition: undo_system.c:649
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define UNUSED(x)
void BLO_memfile_merge(MemFile *first, MemFile *second)
Definition: undofile.c:54
void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag)
ID and Library types, which are fundamental for sdna.
#define ID_IS_LINKED(_id)
Definition: DNA_ID.h:566
@ LIB_TAG_UNDO_OLD_ID_REUSED
Definition: DNA_ID.h:752
@ PRV_USER_EDITED
Definition: DNA_ID.h:518
@ NUM_ICON_SIZES
Definition: DNA_ID_enums.h:18
@ ID_AR
Definition: DNA_ID_enums.h:66
@ ID_SCE
Definition: DNA_ID_enums.h:45
@ ID_OB
Definition: DNA_ID_enums.h:47
@ 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)
void ED_preview_restart_queue_add(struct ID *id, enum eIconSizes size)
void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain)
bool ED_undo_is_memfile_compatible(const struct bContext *C)
struct UndoStack * ED_undo_stack_get(void)
Definition: ed_undo.c:473
void ED_editors_init_for_undo(struct Main *bmain)
Definition: ed_util.c:60
void ED_editors_exit(struct Main *bmain, bool do_undo_system)
Definition: ed_util.c:213
bool ED_editors_flush_edits_ex(struct Main *bmain, bool for_render, bool check_needs_flush)
Definition: ed_util.c:308
#define C
Definition: RandGen.cpp:25
#define NC_SCENE
Definition: WM_types.h:328
#define ND_LAYER_CONTENT
Definition: WM_types.h:402
unsigned int U
Definition: btGjkEpa3.h:78
Scene scene
#define GS(x)
Definition: iris.c:225
struct MemFileUndoStep MemFileUndoStep
void ED_undosys_stack_memfile_id_changed_tag(UndoStack *ustack, ID *id)
Definition: memfile_undo.c:352
static struct MemFile * ed_undosys_step_get_memfile(UndoStep *us_p)
Definition: memfile_undo.c:337
static void memfile_undosys_step_free(UndoStep *us_p)
Definition: memfile_undo.c:298
static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
Definition: memfile_undo.c:100
struct MemFile * ED_undosys_stack_memfile_get_active(UndoStack *ustack)
Definition: memfile_undo.c:343
static bool memfile_undosys_poll(bContext *C)
Definition: memfile_undo.c:57
static bool memfile_undosys_step_encode(struct bContext *UNUSED(C), struct Main *bmain, UndoStep *us_p)
Definition: memfile_undo.c:73
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:159
void ED_memfile_undosys_type(UndoType *ut)
Definition: memfile_undo.c:314
static void memfile_undosys_unfinished_id_previews_restart(ID *id)
Definition: memfile_undo.c:140
static const pxr::TfToken preview("preview", pxr::TfToken::Immortal)
Definition: DNA_ID.h:368
int tag
Definition: DNA_ID.h:387
int recalc_after_undo_push
Definition: DNA_ID.h:401
int recalc
Definition: DNA_ID.h:390
unsigned int session_uuid
Definition: DNA_ID.h:407
char name[66]
Definition: DNA_ID.h:378
Definition: BKE_main.h:121
char is_memfile_undo_flush_needed
Definition: BKE_main.h:145
char use_memfile_full_barrier
Definition: BKE_main.h:150
MemFileUndoData * data
Definition: memfile_undo.c:54
ListBase chunks
Definition: BLO_undofile.h:33
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, 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)