Blender  V3.3
editmesh_undo.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "MEM_guardedalloc.h"
8 
9 #include "CLG_log.h"
10 
11 #include "DNA_key_types.h"
12 #include "DNA_layer_types.h"
13 #include "DNA_mesh_types.h"
14 #include "DNA_meshdata_types.h"
15 #include "DNA_object_types.h"
16 #include "DNA_scene_types.h"
17 
18 #include "BLI_array_utils.h"
19 #include "BLI_listbase.h"
20 
21 #include "BKE_context.h"
22 #include "BKE_customdata.h"
23 #include "BKE_editmesh.h"
24 #include "BKE_key.h"
25 #include "BKE_layer.h"
26 #include "BKE_lib_id.h"
27 #include "BKE_main.h"
28 #include "BKE_mesh.h"
29 #include "BKE_object.h"
30 #include "BKE_undo_system.h"
31 
32 #include "DEG_depsgraph.h"
33 
34 #include "ED_mesh.h"
35 #include "ED_object.h"
36 #include "ED_undo.h"
37 #include "ED_util.h"
38 
39 #include "WM_api.h"
40 #include "WM_types.h"
41 
42 #define USE_ARRAY_STORE
43 
44 #ifdef USE_ARRAY_STORE
45 // # define DEBUG_PRINT
46 // # define DEBUG_TIME
47 # ifdef DEBUG_TIME
48 # include "PIL_time_utildefines.h"
49 # endif
50 
51 # include "BLI_array_store.h"
52 # include "BLI_array_store_utils.h"
53 /* check on best size later... */
54 # define ARRAY_CHUNK_SIZE 256
55 
56 # define USE_ARRAY_STORE_THREAD
57 #endif
58 
59 #ifdef USE_ARRAY_STORE_THREAD
60 # include "BLI_task.h"
61 #endif
62 
64 static CLG_LogRef LOG = {"ed.undo.mesh"};
65 
66 /* -------------------------------------------------------------------- */
70 #ifdef USE_ARRAY_STORE
71 
72 /* Single linked list of layers stored per type */
73 typedef struct BArrayCustomData {
76  int states_len; /* number of layers for each type */
79 
80 #endif
81 
82 typedef struct UndoMesh {
88 
92 
101  int shapenr;
102 
103 #ifdef USE_ARRAY_STORE
104  /* NULL arrays are considered empty */
105  struct { /* most data is stored as 'custom' data */
109  } store;
110 #endif /* USE_ARRAY_STORE */
111 
112  size_t undo_size;
114 
115 #ifdef USE_ARRAY_STORE
116 
117 /* -------------------------------------------------------------------- */
121 static struct {
123  int users;
124 
130 
131 # ifdef USE_ARRAY_STORE_THREAD
133 # endif
134 
136 
137 static void um_arraystore_cd_compact(struct CustomData *cdata,
138  const size_t data_len,
139  bool create,
140  const BArrayCustomData *bcd_reference,
141  BArrayCustomData **r_bcd_first)
142 {
143  if (data_len == 0) {
144  if (create) {
145  *r_bcd_first = NULL;
146  }
147  }
148 
149  const BArrayCustomData *bcd_reference_current = bcd_reference;
150  BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL;
151  for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) {
152  const eCustomDataType type = cdata->layers[layer_start].type;
153 
154  /* Perform a full copy on dynamic layers.
155  *
156  * Unfortunately we can't compare dynamic layer types as they contain allocated pointers,
157  * which burns CPU cycles looking for duplicate data that doesn't exist.
158  * The array data isn't comparable once copied from the mesh,
159  * this bottlenecks on high poly meshes, see T84114.
160  *
161  * Notes:
162  *
163  * - Ideally the data would be expanded into a format that could be de-duplicated effectively,
164  * this would require a flat representation of each dynamic custom-data layer.
165  *
166  * - The data in the layer could be kept as-is to save on the extra copy,
167  * it would complicate logic in this function.
168  */
169  const bool layer_type_is_dynamic = CustomData_layertype_is_dynamic(type);
170 
171  layer_end = layer_start + 1;
172  while ((layer_end < cdata->totlayer) && (type == cdata->layers[layer_end].type)) {
173  layer_end++;
174  }
175 
176  const int stride = CustomData_sizeof(type);
178  &um_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE) :
179  NULL;
180  const int layer_len = layer_end - layer_start;
181 
182  if (create) {
183  if (bcd_reference_current && (bcd_reference_current->type == type)) {
184  /* common case, the reference is aligned */
185  }
186  else {
187  bcd_reference_current = NULL;
188 
189  /* Do a full lookup when unaligned. */
190  if (bcd_reference) {
191  const BArrayCustomData *bcd_iter = bcd_reference;
192  while (bcd_iter) {
193  if (bcd_iter->type == type) {
194  bcd_reference_current = bcd_iter;
195  break;
196  }
197  bcd_iter = bcd_iter->next;
198  }
199  }
200  }
201  }
202 
203  if (create) {
204  bcd = MEM_callocN(sizeof(BArrayCustomData) + (layer_len * sizeof(BArrayState *)), __func__);
205  bcd->next = NULL;
206  bcd->type = type;
207  bcd->states_len = layer_end - layer_start;
208 
209  if (bcd_prev) {
210  bcd_prev->next = bcd;
211  bcd_prev = bcd;
212  }
213  else {
214  bcd_first = bcd;
215  bcd_prev = bcd;
216  }
217  }
218 
219  CustomDataLayer *layer = &cdata->layers[layer_start];
220  for (int i = 0; i < layer_len; i++, layer++) {
221  if (create) {
222  if (layer->data) {
223  BArrayState *state_reference = (bcd_reference_current &&
224  i < bcd_reference_current->states_len) ?
225  bcd_reference_current->states[i] :
226  NULL;
227  /* See comment on `layer_type_is_dynamic` above. */
228  if (layer_type_is_dynamic) {
229  state_reference = NULL;
230  }
231 
233  bs, layer->data, (size_t)data_len * stride, state_reference);
234  }
235  else {
236  bcd->states[i] = NULL;
237  }
238  }
239 
240  if (layer->data) {
241  MEM_freeN(layer->data);
242  layer->data = NULL;
243  }
244  }
245 
246  if (create) {
247  if (bcd_reference_current) {
248  bcd_reference_current = bcd_reference_current->next;
249  }
250  }
251  }
252 
253  if (create) {
254  *r_bcd_first = bcd_first;
255  }
256 }
257 
263  struct CustomData *cdata,
264  const size_t data_len)
265 {
266  CustomDataLayer *layer = cdata->layers;
267  while (bcd) {
268  const int stride = CustomData_sizeof(bcd->type);
269  for (int i = 0; i < bcd->states_len; i++) {
270  BLI_assert(bcd->type == layer->type);
271  if (bcd->states[i]) {
272  size_t state_len;
273  layer->data = BLI_array_store_state_data_get_alloc(bcd->states[i], &state_len);
274  BLI_assert(stride * data_len == state_len);
275  UNUSED_VARS_NDEBUG(stride, data_len);
276  }
277  else {
278  layer->data = NULL;
279  }
280  layer++;
281  }
282  bcd = bcd->next;
283  }
284 }
285 
287 {
288  while (bcd) {
289  BArrayCustomData *bcd_next = bcd->next;
290  const int stride = CustomData_sizeof(bcd->type);
292  for (int i = 0; i < bcd->states_len; i++) {
293  if (bcd->states[i]) {
295  }
296  }
297  MEM_freeN(bcd);
298  bcd = bcd_next;
299  }
300 }
301 
307 static void um_arraystore_compact_ex(UndoMesh *um, const UndoMesh *um_ref, bool create)
308 {
309  Mesh *me = &um->me;
310 
312  &me->vdata, me->totvert, create, um_ref ? um_ref->store.vdata : NULL, &um->store.vdata);
314  &me->edata, me->totedge, create, um_ref ? um_ref->store.edata : NULL, &um->store.edata);
316  &me->ldata, me->totloop, create, um_ref ? um_ref->store.ldata : NULL, &um->store.ldata);
318  &me->pdata, me->totpoly, create, um_ref ? um_ref->store.pdata : NULL, &um->store.pdata);
319 
320  if (me->key && me->key->totkey) {
321  const size_t stride = me->key->elemsize;
323  &um_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE) :
324  NULL;
325  if (create) {
326  um->store.keyblocks = MEM_mallocN(me->key->totkey * sizeof(*um->store.keyblocks), __func__);
327  }
328  KeyBlock *keyblock = me->key->block.first;
329  for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) {
330  if (create) {
331  BArrayState *state_reference = (um_ref && um_ref->me.key && (i < um_ref->me.key->totkey)) ?
332  um_ref->store.keyblocks[i] :
333  NULL;
335  bs, keyblock->data, (size_t)keyblock->totelem * stride, state_reference);
336  }
337 
338  if (keyblock->data) {
339  MEM_freeN(keyblock->data);
340  keyblock->data = NULL;
341  }
342  }
343  }
344 
345  if (me->mselect && me->totselect) {
346  BLI_assert(create == (um->store.mselect == NULL));
347  if (create) {
348  BArrayState *state_reference = um_ref ? um_ref->store.mselect : NULL;
349  const size_t stride = sizeof(*me->mselect);
351  &um_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE);
353  bs, me->mselect, (size_t)me->totselect * stride, state_reference);
354  }
355 
356  /* keep me->totselect for validation */
357  MEM_freeN(me->mselect);
358  me->mselect = NULL;
359  }
360 
361  if (create) {
362  um_arraystore.users += 1;
363  }
364 
366 }
367 
371 static void um_arraystore_compact(UndoMesh *um, const UndoMesh *um_ref)
372 {
373  um_arraystore_compact_ex(um, um_ref, true);
374 }
375 
376 static void um_arraystore_compact_with_info(UndoMesh *um, const UndoMesh *um_ref)
377 {
378 # ifdef DEBUG_PRINT
379  size_t size_expanded_prev, size_compacted_prev;
381  &um_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
382 # endif
383 
384 # ifdef DEBUG_TIME
385  TIMEIT_START(mesh_undo_compact);
386 # endif
387 
388  um_arraystore_compact(um, um_ref);
389 
390 # ifdef DEBUG_TIME
391  TIMEIT_END(mesh_undo_compact);
392 # endif
393 
394 # ifdef DEBUG_PRINT
395  {
396  size_t size_expanded, size_compacted;
398  &um_arraystore.bs_stride, &size_expanded, &size_compacted);
399 
400  const double percent_total = size_expanded ?
401  (((double)size_compacted / (double)size_expanded) * 100.0) :
402  -1.0;
403 
404  size_t size_expanded_step = size_expanded - size_expanded_prev;
405  size_t size_compacted_step = size_compacted - size_compacted_prev;
406  const double percent_step = size_expanded_step ?
407  (((double)size_compacted_step / (double)size_expanded_step) *
408  100.0) :
409  -1.0;
410 
411  printf("overall memory use: %.8f%% of expanded size\n", percent_total);
412  printf("step memory use: %.8f%% of expanded size\n", percent_step);
413  }
414 # endif
415 }
416 
417 # ifdef USE_ARRAY_STORE_THREAD
418 
419 struct UMArrayData {
421  const UndoMesh *um_ref; /* can be NULL */
422 };
423 static void um_arraystore_compact_cb(TaskPool *__restrict UNUSED(pool), void *taskdata)
424 {
425  struct UMArrayData *um_data = taskdata;
426  um_arraystore_compact_with_info(um_data->um, um_data->um_ref);
427 }
428 
429 # endif /* USE_ARRAY_STORE_THREAD */
430 
435 {
437 }
438 
440 {
441  Mesh *me = &um->me;
442 
447 
448  if (um->store.keyblocks) {
449  const size_t stride = me->key->elemsize;
450  KeyBlock *keyblock = me->key->block.first;
451  for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) {
453  size_t state_len;
454  keyblock->data = BLI_array_store_state_data_get_alloc(state, &state_len);
455  BLI_assert(keyblock->totelem == (state_len / stride));
457  }
458  }
459 
460  if (um->store.mselect) {
461  const size_t stride = sizeof(*me->mselect);
463  size_t state_len;
465  BLI_assert(me->totselect == (state_len / stride));
467  }
468 
469  /* not essential, but prevents accidental dangling pointer access */
471 }
472 
474 {
475  Mesh *me = &um->me;
476 
481 
482  if (um->store.keyblocks) {
483  const size_t stride = me->key->elemsize;
485  for (int i = 0; i < me->key->totkey; i++) {
488  }
490  um->store.keyblocks = NULL;
491  }
492 
493  if (um->store.mselect) {
494  const size_t stride = sizeof(*me->mselect);
498  um->store.mselect = NULL;
499  }
500 
501  um_arraystore.users -= 1;
502 
503  BLI_assert(um_arraystore.users >= 0);
504 
505  if (um_arraystore.users == 0) {
506 # ifdef DEBUG_PRINT
507  printf("mesh undo store: freeing all data!\n");
508 # endif
510 
511 # ifdef USE_ARRAY_STORE_THREAD
513  um_arraystore.task_pool = NULL;
514 # endif
515  }
516 }
517 
520 /* -------------------------------------------------------------------- */
536 {
537  /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */
538  GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len);
539  UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__);
540  for (int i = 0; i < object_len; i++) {
541  const Mesh *me = object[i]->data;
542  BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]);
543  }
544  int uuid_map_len = object_len;
545 
546  /* Loop backwards over all previous mesh undo data until either:
547  * - All elements have been found (where `um_references` we'll have every element set).
548  * - There are no undo steps left to look for. */
549  UndoMesh *um_iter = um_arraystore.local_links.last;
550  while (um_iter && (uuid_map_len != 0)) {
551  UndoMesh **um_p;
552  if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) {
553  *um_p = um_iter;
554  uuid_map_len--;
555  }
556  um_iter = um_iter->local_prev;
557  }
558  BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map));
559  BLI_ghash_free(uuid_map, NULL, NULL);
560  if (uuid_map_len == object_len) {
561  MEM_freeN(um_references);
562  um_references = NULL;
563  }
564  return um_references;
565 }
566 
569 #endif /* USE_ARRAY_STORE */
570 
571 /* for callbacks */
572 /* undo simply makes copies of a bmesh */
577 {
579 #ifdef USE_ARRAY_STORE_THREAD
580  /* changes this waits is low, but must have finished */
581  if (um_arraystore.task_pool) {
583  }
584 #endif
585  /* make sure shape keys work */
586  if (key != NULL) {
587  um->me.key = (Key *)BKE_id_copy_ex(
589  }
590  else {
591  um->me.key = NULL;
592  }
593 
594  /* Uncomment for troubleshooting. */
595  // BM_mesh_validate(em->bm);
596 
598  NULL,
599  em->bm,
600  &um->me,
601  (&(struct BMeshToMeshParams){
602  /* Undo code should not be manipulating 'G_MAIN->object' hooks/vertex-parent. */
603  .calc_object_remap = false,
604  .update_shapekey_indices = false,
605  .cd_mask_extra = {.vmask = CD_MASK_SHAPE_KEYINDEX},
606  .active_shapekey_to_mvert = true,
607  }));
608 
609  um->selectmode = em->selectmode;
610  um->shapenr = em->bm->shapenr;
611 
612 #ifdef USE_ARRAY_STORE
613  {
614  /* Add ourselves. */
615  BLI_addtail(&um_arraystore.local_links, um);
616 
617 # ifdef USE_ARRAY_STORE_THREAD
618  if (um_arraystore.task_pool == NULL) {
620  }
621 
622  struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__);
623  um_data->um = um;
624  um_data->um_ref = um_ref;
625 
627 # else
629 # endif
630  }
631 #else
633 #endif
634 
635  return um;
636 }
637 
639 {
640  BMEditMesh *em_tmp;
641  BMesh *bm;
642 
643 #ifdef USE_ARRAY_STORE
644 # ifdef USE_ARRAY_STORE_THREAD
645  /* changes this waits is low, but must have finished */
647 # endif
648 
649 # ifdef DEBUG_TIME
650  TIMEIT_START(mesh_undo_expand);
651 # endif
652 
654 
655 # ifdef DEBUG_TIME
656  TIMEIT_END(mesh_undo_expand);
657 # endif
658 #endif /* USE_ARRAY_STORE */
659 
660  const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me);
661 
662  em->bm->shapenr = um->shapenr;
663 
665 
666  bm = BM_mesh_create(&allocsize,
667  &((struct BMeshCreateParams){
668  .use_toolflags = true,
669  }));
670 
672  &um->me,
673  (&(struct BMeshFromMeshParams){
674  /* Handled with tessellation. */
675  .calc_face_normal = false,
676  .calc_vert_normal = false,
677  .active_shapekey = um->shapenr,
678  }));
679 
680  em_tmp = BKE_editmesh_create(bm);
681  *em = *em_tmp;
682 
683  /* Normals should not be stored in the undo mesh, so recalculate them. The edit
684  * mesh is expected to have valid normals and there is no tracked dirty state. */
686 
687  /* Calculate face normals and tessellation at once since it's multi-threaded. */
689 
690  em->selectmode = um->selectmode;
692 
694 
695  ob->shapenr = um->shapenr;
696 
697  MEM_freeN(em_tmp);
698 
699 #ifdef USE_ARRAY_STORE
701 #endif
702 }
703 
705 {
706  Mesh *me = &um->me;
707 
708 #ifdef USE_ARRAY_STORE
709 
710 # ifdef USE_ARRAY_STORE_THREAD
711  /* changes this waits is low, but must have finished */
713 # endif
714 
715  /* we need to expand so any allocations in custom-data are freed with the mesh */
717 
718  BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1);
719  BLI_remlink(&um_arraystore.local_links, um);
720 
722 #endif
723 
724  if (me->key) {
725  BKE_key_free_data(me->key);
726  MEM_freeN(me->key);
727  }
728 
730 }
731 
733 {
734  ViewLayer *view_layer = CTX_data_view_layer(C);
735  Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
736  if (obedit && obedit->type == OB_MESH) {
737  Mesh *me = obedit->data;
738  if (me->edit_mesh != NULL) {
739  return obedit;
740  }
741  }
742  return NULL;
743 }
744 
747 /* -------------------------------------------------------------------- */
753 typedef struct MeshUndoStep_Elem {
754  UndoRefID_Object obedit_ref;
757 
758 typedef struct MeshUndoStep {
763 
765 {
767 }
768 
769 static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
770 {
771  MeshUndoStep *us = (MeshUndoStep *)us_p;
772 
773  /* Important not to use the 3D view when getting objects because all objects
774  * outside of this list will be moved out of edit-mode when reading back undo steps. */
775  ViewLayer *view_layer = CTX_data_view_layer(C);
777  uint objects_len = 0;
778  Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
779 
780  us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
781  us->elems_len = objects_len;
782 
783  UndoMesh **um_references = NULL;
784 
785 #ifdef USE_ARRAY_STORE
786  um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len);
787 #endif
788 
789  for (uint i = 0; i < objects_len; i++) {
790  Object *ob = objects[i];
791  MeshUndoStep_Elem *elem = &us->elems[i];
792 
793  elem->obedit_ref.ptr = ob;
794  Mesh *me = elem->obedit_ref.ptr->data;
795  BMEditMesh *em = me->edit_mesh;
797  &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL);
798  em->needs_flush_to_id = 1;
799  us->step.data_size += elem->data.undo_size;
800  elem->data.uv_selectmode = ts->uv_selectmode;
801 
802 #ifdef USE_ARRAY_STORE
804  elem->data.me.id.session_uuid = me->id.session_uuid;
805 #endif
806  }
807  MEM_freeN(objects);
808 
809  if (um_references != NULL) {
810  MEM_freeN(um_references);
811  }
812 
813  bmain->is_memfile_undo_flush_needed = true;
814 
815  return true;
816 }
817 
818 static void mesh_undosys_step_decode(struct bContext *C,
819  struct Main *bmain,
820  UndoStep *us_p,
821  const eUndoStepDir UNUSED(dir),
822  bool UNUSED(is_final))
823 {
824  MeshUndoStep *us = (MeshUndoStep *)us_p;
825 
827  C, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
828 
830 
831  for (uint i = 0; i < us->elems_len; i++) {
832  MeshUndoStep_Elem *elem = &us->elems[i];
833  Object *obedit = elem->obedit_ref.ptr;
834  Mesh *me = obedit->data;
835  if (me->edit_mesh == NULL) {
836  /* Should never fail, may not crash but can give odd behavior. */
837  CLOG_ERROR(&LOG,
838  "name='%s', failed to enter edit-mode for object '%s', undo state invalid",
839  us_p->name,
840  obedit->id.name);
841  continue;
842  }
843  BMEditMesh *em = me->edit_mesh;
844  undomesh_to_editmesh(&elem->data, obedit, em);
845  em->needs_flush_to_id = 1;
847  }
848 
849  /* The first element is always active */
851  CTX_data_scene(C), CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG);
852 
853  /* Check after setting active. */
855 
859 
860  bmain->is_memfile_undo_flush_needed = true;
861 
863 }
864 
866 {
867  MeshUndoStep *us = (MeshUndoStep *)us_p;
868 
869  for (uint i = 0; i < us->elems_len; i++) {
870  MeshUndoStep_Elem *elem = &us->elems[i];
871  undomesh_free_data(&elem->data);
872  }
873  MEM_freeN(us->elems);
874 }
875 
877  UndoTypeForEachIDRefFn foreach_ID_ref_fn,
878  void *user_data)
879 {
880  MeshUndoStep *us = (MeshUndoStep *)us_p;
881 
882  for (uint i = 0; i < us->elems_len; i++) {
883  MeshUndoStep_Elem *elem = &us->elems[i];
884  foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref));
885  }
886 }
887 
889 {
890  ut->name = "Edit Mesh";
891  ut->poll = mesh_undosys_poll;
895 
897 
899 
900  ut->step_size = sizeof(MeshUndoStep);
901 }
902 
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_layertype_is_dynamic(int type)
Definition: customdata.cc:4286
int CustomData_sizeof(int type)
Definition: customdata.cc:4268
void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em)
Definition: editmesh.c:135
BMEditMesh * BKE_editmesh_create(BMesh *bm)
Definition: editmesh.c:29
void BKE_key_free_data(struct Key *key)
Definition: key.c:235
@ LIB_ID_COPY_LOCALIZE
Definition: BKE_lib_id.h:187
@ LIB_ID_COPY_NO_ANIMDATA
Definition: BKE_lib_id.h:154
struct ID * BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, int flag)
void BKE_mesh_update_customdata_pointers(struct Mesh *me, bool do_ensure_tess_cd)
Definition: mesh.cc:874
bool BKE_mesh_vertex_normals_are_dirty(const struct Mesh *mesh)
void BKE_mesh_free_data_for_undo(struct Mesh *me)
Definition: mesh.cc:903
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const struct Object *ob)
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
eUndoStepDir
void(* UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref)
Efficient in-memory storage of multiple similar arrays.
void * BLI_array_store_state_data_get_alloc(BArrayState *state, size_t *r_data_len)
Definition: array_store.c:1580
void BLI_array_store_state_remove(BArrayStore *bs, BArrayState *state)
Definition: array_store.c:1545
BArrayState * BLI_array_store_state_add(BArrayStore *bs, const void *data, size_t data_len, const BArrayState *state_reference)
Definition: array_store.c:1497
void BLI_array_store_at_size_clear(struct BArrayStore_AtSize *bs_stride)
void BLI_array_store_at_size_calc_memory_usage(struct BArrayStore_AtSize *bs_stride, size_t *r_size_expanded, size_t *r_size_compacted)
BArrayStore * BLI_array_store_at_size_ensure(struct BArrayStore_AtSize *bs_stride, int stride, int chunk_size)
BArrayStore * BLI_array_store_at_size_get(struct BArrayStore_AtSize *bs_stride, int stride)
Generic array manipulation API.
#define BLI_array_is_zeroed(arr, arr_len)
#define BLI_assert(a)
Definition: BLI_assert.h:46
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:705
GHash * BLI_ghash_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_popkey(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:805
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned int uint
Definition: BLI_sys_types.h:67
@ TASK_PRIORITY_LOW
Definition: BLI_task.h:56
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition: task_pool.cc:480
TaskPool * BLI_task_pool_create_background(void *userdata, eTaskPriority priority)
Definition: task_pool.cc:407
void BLI_task_pool_free(TaskPool *pool)
Definition: task_pool.cc:440
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition: task_pool.cc:459
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
typedef double(DMatrix)[4][4]
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:190
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
eCustomDataType
Object is a sort of wrapper for general info.
@ OB_MESH
#define OBEDIT_FROM_VIEW_LAYER(view_layer)
void EDBM_mesh_free_data(struct BMEditMesh *em)
void ED_undo_object_editmode_restore_helper(struct bContext *C, struct Object **object_array, uint object_array_len, uint object_array_stride)
Definition: ed_undo.c:817
void ED_undo_object_set_active_or_warn(struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob, const char *info, struct CLG_LogRef *log)
Definition: ed_undo.c:800
struct Object ** ED_undo_editmode_objects_from_view_layer(struct ViewLayer *view_layer, uint *r_len)
Definition: ed_undo.c:888
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei stride
Read Guarded memory(de)allocation.
Utility defines for timing/benchmarks.
#define TIMEIT_START(var)
#define TIMEIT_END(var)
#define C
Definition: RandGen.cpp:25
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
@ BM_SPACEARR_DIRTY_ALL
Definition: bmesh_class.h:416
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params)
Definition: bmesh_mesh.cc:125
#define BMALLOC_TEMPLATE_FROM_ME(...)
Definition: bmesh_mesh.h:197
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params)
Scene scene
void * user_data
static void um_arraystore_expand(UndoMesh *um)
static void um_arraystore_cd_compact(struct CustomData *cdata, const size_t data_len, bool create, const BArrayCustomData *bcd_reference, BArrayCustomData **r_bcd_first)
static UndoMesh ** mesh_undostep_reference_elems_from_objects(Object **object, int object_len)
static void um_arraystore_free(UndoMesh *um)
struct MeshUndoStep MeshUndoStep
struct MeshUndoStep_Elem MeshUndoStep_Elem
static void undomesh_free_data(UndoMesh *um)
static void um_arraystore_compact_cb(TaskPool *__restrict UNUSED(pool), void *taskdata)
struct BArrayCustomData BArrayCustomData
#define ARRAY_CHUNK_SIZE
Definition: editmesh_undo.c:54
void ED_mesh_undosys_type(UndoType *ut)
static void um_arraystore_compact(UndoMesh *um, const UndoMesh *um_ref)
static void um_arraystore_cd_expand(const BArrayCustomData *bcd, struct CustomData *cdata, const size_t data_len)
static void um_arraystore_cd_free(BArrayCustomData *bcd)
static void mesh_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, const eUndoStepDir UNUSED(dir), bool UNUSED(is_final))
TaskPool * task_pool
static void um_arraystore_expand_clear(UndoMesh *um)
static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em)
ListBase local_links
static bool mesh_undosys_poll(bContext *C)
static void mesh_undosys_foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
static struct @482 um_arraystore
static Object * editmesh_object_from_context(bContext *C)
struct UndoMesh UndoMesh
int users
static CLG_LogRef LOG
Definition: editmesh_undo.c:64
static void mesh_undosys_step_free(UndoStep *us_p)
struct BArrayStore_AtSize bs_stride
static void * undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref)
static void um_arraystore_compact_with_info(UndoMesh *um, const UndoMesh *um_ref)
static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
static void um_arraystore_compact_ex(UndoMesh *um, const UndoMesh *um_ref, bool create)
const int state
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
BArrayState * states[0]
Definition: editmesh_undo.c:77
struct BArrayCustomData * next
Definition: editmesh_undo.c:74
eCustomDataType type
Definition: editmesh_undo.c:75
short selectmode
Definition: BKE_editmesh.h:52
struct BMesh * bm
Definition: BKE_editmesh.h:40
char needs_flush_to_id
Definition: BKE_editmesh.h:68
int shapenr
Definition: bmesh_class.h:353
short selectmode
Definition: bmesh_class.h:350
char spacearr_dirty
Definition: bmesh_class.h:344
CustomDataLayer * layers
unsigned int session_uuid
Definition: DNA_ID.h:407
char name[66]
Definition: DNA_ID.h:378
struct KeyBlock * next
Definition: DNA_key_types.h:25
void * data
Definition: DNA_key_types.h:50
int totkey
Definition: DNA_key_types.h:91
ID id
Definition: DNA_key_types.h:63
int elemsize
Definition: DNA_key_types.h:80
ListBase block
Definition: DNA_key_types.h:84
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
char is_memfile_undo_flush_needed
Definition: BKE_main.h:145
UndoRefID_Object obedit_ref
MeshUndoStep_Elem * elems
UndoStep step
struct BMEditMesh * edit_mesh
CustomData vdata
int totedge
int totvert
CustomData pdata
int totpoly
CustomData edata
int totloop
struct Key * key
int totselect
CustomData ldata
struct MSelect * mselect
short shapenr
void * data
struct ToolSettings * toolsettings
const UndoMesh * um_ref
UndoMesh * um
size_t undo_size
BArrayState * mselect
BArrayCustomData * ldata
struct UndoMesh * local_next
Definition: editmesh_undo.c:87
BArrayCustomData * vdata
BArrayState ** keyblocks
int selectmode
Definition: editmesh_undo.c:90
struct UndoMesh * local_prev
Definition: editmesh_undo.c:87
BArrayCustomData * pdata
struct UndoMesh::@483 store
char uv_selectmode
Definition: editmesh_undo.c:91
BArrayCustomData * edata
size_t data_size
char name[64]
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)
void(* step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
const char * name
void(* step_free)(UndoStep *us)
bool(* poll)(struct bContext *C)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)