Blender V4.3
lib_id_delete.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13/* all types are needed here, in order to do memory operations */
14#include "DNA_ID.h"
15#include "DNA_key_types.h"
16
17#include "BLI_utildefines.h"
18
19#include "BLI_listbase.h"
20#include "BLI_set.hh"
21#include "BLI_vector.hh"
22
23#include "BKE_anim_data.hh"
24#include "BKE_asset.hh"
25#include "BKE_idprop.hh"
26#include "BKE_idtype.hh"
27#include "BKE_key.hh"
28#include "BKE_layer.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_lib_override.hh"
31#include "BKE_lib_remap.hh"
32#include "BKE_library.hh"
33#include "BKE_main.hh"
34#include "BKE_main_namemap.hh"
35
36#include "lib_intern.hh"
37
38#include "DEG_depsgraph.hh"
39
40#ifdef WITH_PYTHON
41# include "BPY_extern.hh"
42#endif
43
44using namespace blender::bke::id;
45
46void BKE_libblock_free_data(ID *id, const bool do_id_user)
47{
48 if (id->properties) {
49 IDP_FreePropertyContent_ex(id->properties, do_id_user);
50 MEM_freeN(id->properties);
51 id->properties = nullptr;
52 }
53
54 if (id->override_library) {
55 BKE_lib_override_library_free(&id->override_library, do_id_user);
56 id->override_library = nullptr;
57 }
58
59 if (id->asset_data) {
60 BKE_asset_metadata_free(&id->asset_data);
61 }
62
63 if (id->library_weak_reference != nullptr) {
64 MEM_freeN(id->library_weak_reference);
65 }
66
67 BKE_animdata_free(id, do_id_user);
68}
69
70void BKE_libblock_free_datablock(ID *id, const int /*flag*/)
71{
72 const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id);
73
74 if (idtype_info != nullptr) {
75 if (idtype_info->free_data != nullptr) {
76 idtype_info->free_data(id);
77 }
78 return;
79 }
80
81 BLI_assert_msg(0, "IDType Missing IDTypeInfo");
82}
83
84static int id_free(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag)
85{
86 ID *id = static_cast<ID *>(idv);
87
88 if (use_flag_from_idtag) {
89 if ((id->tag & ID_TAG_NO_MAIN) != 0) {
91 }
92 else {
93 flag &= ~LIB_ID_FREE_NO_MAIN;
94 }
95
96 if ((id->tag & ID_TAG_NO_USER_REFCOUNT) != 0) {
98 }
99 else {
100 flag &= ~LIB_ID_FREE_NO_USER_REFCOUNT;
101 }
102
103 if ((id->tag & ID_TAG_NOT_ALLOCATED) != 0) {
105 }
106 else {
107 flag &= ~LIB_ID_FREE_NOT_ALLOCATED;
108 }
109 }
110
111 BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || bmain != nullptr);
114
115 const short type = GS(id->name);
116
117 if (bmain && (flag & LIB_ID_FREE_NO_DEG_TAG) == 0) {
118 BLI_assert(bmain->is_locked_for_linking == false);
119
120 DEG_id_type_tag(bmain, type);
121 }
122
124
125 Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : nullptr;
126
127 if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) {
128 BKE_libblock_relink_ex(bmain, id, nullptr, nullptr, ID_REMAP_SKIP_USER_CLEAR);
129 }
130
131 if ((flag & LIB_ID_FREE_NO_MAIN) == 0 && key != nullptr) {
132 id_free(bmain, &key->id, flag, use_flag_from_idtag);
133 }
134
136
137 /* avoid notifying on removed data */
138 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
139 BKE_main_lock(bmain);
140 }
141
142 if ((flag & LIB_ID_FREE_NO_UI_USER) == 0) {
145 }
146
148 IDRemapper remapper;
149 remapper.add(id, nullptr);
151 }
152 }
153
154 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
155 ListBase *lb = which_libbase(bmain, type);
156 BLI_remlink(lb, id);
157 if ((flag & LIB_ID_FREE_NO_NAMEMAP_REMOVE) == 0) {
158 BKE_main_namemap_remove_name(bmain, id, id->name + 2);
159 }
160 }
161
163
164 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
165 BKE_main_unlock(bmain);
166 }
167
168 if ((flag & LIB_ID_FREE_NOT_ALLOCATED) == 0) {
169 MEM_freeN(id);
170 }
171
172 return flag;
173}
174
175void BKE_id_free_ex(Main *bmain, void *idv, const int flag_orig, const bool use_flag_from_idtag)
176{
177 /* ViewLayer resync needs to be delayed during Scene freeing, since internal relationships
178 * between the Scene's master collection and its view_layers become invalid
179 * (due to remapping). */
180 if (bmain && (flag_orig & LIB_ID_FREE_NO_MAIN) == 0) {
182 }
183
184 const ID_Type id_type = GS(static_cast<ID *>(idv)->name);
185
186 int flag_final = id_free(bmain, idv, flag_orig, use_flag_from_idtag);
187
188 if (bmain) {
189 if ((flag_orig & LIB_ID_FREE_NO_MAIN) == 0) {
191 }
192
193 if ((flag_final & LIB_ID_FREE_NO_MAIN) == 0) {
194 if (ELEM(id_type, ID_SCE, ID_GR, ID_OB)) {
196 }
197 }
198 }
199}
200
201void BKE_id_free(Main *bmain, void *idv)
202{
203 BKE_id_free_ex(bmain, idv, 0, true);
204}
205
206void BKE_id_free_us(Main *bmain, void *idv) /* test users */
207{
208 ID *id = static_cast<ID *>(idv);
209
210 id_us_min(id);
211
212 /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding collections
213 * when deleting an object. Since only 'user_one' usage of objects is collections,
214 * and only 'real user' usage of objects is scenes, removing that 'user_one' tag when there
215 * is no more real (scene) users of an object ensures it gets fully unlinked.
216 * But only for local objects, not linked ones!
217 * Otherwise, there is no real way to get rid of an object anymore -
218 * better handling of this is TODO.
219 */
220 if ((GS(id->name) == ID_OB) && (id->us == 1) && !ID_IS_LINKED(id)) {
222 }
223
224 if (id->us == 0) {
225 const bool is_lib = GS(id->name) == ID_LI;
226
227 BKE_libblock_unlink(bmain, id, false);
228
229 BKE_id_free(bmain, id);
230
231 if (is_lib) {
233 }
234 }
235}
236
237static size_t id_delete(Main *bmain,
238 blender::Set<ID *> &ids_to_delete,
239 const int extra_remapping_flags)
240{
241 bool has_deleted_library = false;
242
243 /* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database,
244 * and has already properly unlinked its other IDs usages.
245 * UI users are always cleared in BKE_libblock_remap_locked() call, so we can always skip it. */
246 const int free_flag = LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_MAIN |
249 ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS | extra_remapping_flags);
250
251 ListBase *lbarray[INDEX_ID_MAX];
252 const int base_count = set_listbasepointers(bmain, lbarray);
253
254 BKE_main_lock(bmain);
256 IDRemapper id_remapper;
257
258 /* Main idea of batch deletion is to remove all IDs to be deleted from Main database.
259 * This means that we won't have to loop over all deleted IDs to remove usages
260 * of other deleted IDs.
261 * This gives tremendous speed-up when deleting a large amount of IDs from a Main
262 * containing thousands of these.
263 * This also means that we have to be very careful here, as we by-pass many 'common'
264 * processing, hence risking to 'corrupt' at least user counts, if not IDs themselves. */
265 bool keep_looping = true;
266 while (keep_looping) {
267 keep_looping = false;
268
269 /* First tag and remove from Main all datablocks directly from target lib.
270 * Note that we go forward here, since we want to check dependencies before users
271 * (e.g. meshes before objects). Reduces the chances to have to loop many times in the
272 * `while (keep_looking)` outer loop. */
273 for (int i = 0; i < base_count; i++) {
274 ListBase *lb = lbarray[i];
275 ID *id_iter;
276
277 FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
278 /* NOTE: in case we delete a library, we also delete all its datablocks! */
279 if (ids_to_delete.contains(id_iter) ||
280 (ID_IS_LINKED(id_iter) && ids_to_delete.contains(&id_iter->lib->id)))
281 {
282 BLI_remlink(lb, id_iter);
283 BKE_main_namemap_remove_name(bmain, id_iter, id_iter->name + 2);
284 ids_to_delete.add(id_iter);
285 id_remapper.add(id_iter, nullptr);
286 /* Do not tag as no_main now, we want to unlink it first (lower-level ID management
287 * code has some specific handling of 'no main' IDs that would be a problem in that
288 * case). */
289
290 /* Forcefully also delete shapekeys of the deleted ID if any, 'orphaned' shapekeys are
291 * not allowed in Blender and will cause lots of problem in modern code (liboverrides,
292 * warning on write & read, etc.). */
293 Key *shape_key = BKE_key_from_id(id_iter);
294 if (shape_key && !ids_to_delete.contains(&shape_key->id)) {
295 BLI_remlink(&bmain->shapekeys, &shape_key->id);
296 BKE_main_namemap_remove_name(bmain, &shape_key->id, shape_key->id.name + 2);
297 ids_to_delete.add(&shape_key->id);
298 id_remapper.add(&shape_key->id, nullptr);
299 }
300
301 keep_looping = true;
302 }
303 }
305 }
306
307 /* Will tag 'never nullptr' users of this ID too.
308 *
309 * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
310 * links, this can lead to nasty crashing here in second, actual deleting loop.
311 * Also, this will also flag users of deleted data that cannot be unlinked
312 * (object using deleted obdata, etc.), so that they also get deleted. */
313 BKE_libblock_remap_multiple_locked(bmain, id_remapper, remapping_flags);
314 for (ID *id_never_null_iter : id_remapper.never_null_users()) {
315 ids_to_delete.add(id_never_null_iter);
316 }
317 id_remapper.clear();
318 }
319
320 /* Since we removed IDs from Main, their own other IDs usages need to be removed 'manually'. */
321 blender::Vector<ID *> cleanup_ids{ids_to_delete.begin(), ids_to_delete.end()};
323 bmain,
324 cleanup_ids,
326 id_remapper,
328 cleanup_ids.clear();
329
330 /* Now we can safely mark that ID as not being in Main database anymore. */
331 /* NOTE: This needs to be done in a separate loop than above, otherwise some user-counts of
332 * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID user-counts
333 * is never affected). */
334 for (ID *id : ids_to_delete) {
335 id->tag |= ID_TAG_NO_MAIN;
336 /* User-count needs to be reset artificially, since some usages may not be cleared in batch
337 * deletion (typically, if one deleted ID uses another deleted ID, this may not be cleared by
338 * remapping code, depending on order in which these are handled). */
339 id->us = ID_FAKE_USERS(id);
340
341 if (!has_deleted_library && GS(id->name) == ID_LI) {
342 has_deleted_library = true;
343 }
344
345 id_free(bmain, id, free_flag, false);
346 }
347
348 BKE_main_unlock(bmain);
351
352 if (has_deleted_library) {
354 }
355
356 bmain->is_memfile_undo_written = false;
357 return size_t(ids_to_delete.size());
358}
359
360void BKE_id_delete_ex(Main *bmain, void *idv, const int extra_remapping_flags)
361{
362 ID *id = static_cast<ID *>(idv);
363 BLI_assert_msg((id->tag & ID_TAG_NO_MAIN) == 0, "Cannot be used with IDs outside of Main");
364
365 blender::Set<ID *> ids_to_delete = {id};
366 id_delete(bmain, ids_to_delete, extra_remapping_flags);
367}
368
369void BKE_id_delete(Main *bmain, void *idv)
370{
371 BKE_id_delete_ex(bmain, idv, 0);
372}
373
375{
376 blender::Set<ID *> ids_to_delete;
377 ID *id_iter;
378 FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
379 if (id_iter->tag & ID_TAG_DOIT) {
380 ids_to_delete.add(id_iter);
381 }
382 }
384 return id_delete(bmain, ids_to_delete, 0);
385}
386
387size_t BKE_id_multi_delete(Main *bmain, blender::Set<ID *> &ids_to_delete)
388{
389 return id_delete(bmain, ids_to_delete, 0);
390}
391
392/* -------------------------------------------------------------------- */
397{
398#ifdef WITH_PYTHON
399# ifdef WITH_PYTHON_SAFETY
400 BPY_id_release(id);
401# endif
402 if (id->py_instance) {
403 BPY_DECREF_RNA_INVALIDATE(id->py_instance);
404 }
405#else
406 UNUSED_VARS(id);
407#endif
408}
409
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:263
void BKE_asset_metadata_free(AssetMetaData **asset_data)
Definition asset.cc:38
void IDP_FreePropertyContent_ex(IDProperty *prop, bool do_id_user)
Definition idprop.cc:1189
const IDTypeInfo * BKE_idtype_get_info_from_id(const ID *id)
Definition idtype.cc:150
Key * BKE_key_from_id(ID *id)
Definition key.cc:1800
void BKE_layer_collection_resync_forbid()
void BKE_main_collection_sync_remap(const Main *bmain)
void BKE_layer_collection_resync_allow()
void id_us_clear_real(ID *id)
Definition lib_id.cc:324
@ LIB_ID_FREE_NO_UI_USER
@ LIB_ID_FREE_NOT_ALLOCATED
@ LIB_ID_FREE_NO_DEG_TAG
@ LIB_ID_FREE_NO_USER_REFCOUNT
@ LIB_ID_FREE_NO_MAIN
@ LIB_ID_FREE_NO_NAMEMAP_REMOVE
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_lib_override_library_free(IDOverrideLibrary **liboverride, bool do_id_user)
void BKE_libblock_remap_multiple_locked(Main *bmain, blender::bke::id::IDRemapper &mappings, const int remap_flags)
Definition lib_remap.cc:645
@ ID_REMAP_SKIP_USER_CLEAR
@ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS
@ ID_REMAP_FORCE_NEVER_NULL_USAGE
@ ID_REMAP_STORE_NEVER_NULL_USAGE
void BKE_libblock_relink_multiple(Main *bmain, const blender::Span< ID * > ids, eIDRemapType remap_type, blender::bke::id::IDRemapper &id_remapper, int remap_flags)
Definition lib_remap.cc:782
@ ID_REMAP_TYPE_CLEANUP
void void void BKE_libblock_unlink(Main *bmain, void *idv, bool do_skip_indirect) ATTR_NONNULL()
Definition lib_remap.cc:710
void BKE_libblock_relink_ex(Main *bmain, void *idv, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(2)
Definition lib_remap.cc:844
void BKE_library_main_rebuild_hierarchy(Main *bmain)
Definition library.cc:234
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:500
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:842
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:469
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:463
void BKE_main_lock(Main *bmain)
Definition main.cc:479
int set_listbasepointers(Main *bmain, ListBase *lb[])
Definition main.cc:929
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:494
void BKE_main_unlock(Main *bmain)
Definition main.cc:484
void BKE_main_namemap_remove_name(Main *bmain, ID *id, const char *name) ATTR_NONNULL()
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
#define UNUSED_VARS(...)
#define ELEM(...)
void BPY_id_release(ID *id)
Definition bpy_rna.cc:312
void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr)
void DEG_id_type_tag(Main *bmain, short id_type)
ID and Library types, which are fundamental for SDNA.
#define ID_FAKE_USERS(id)
Definition DNA_ID.h:616
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
@ ID_TAG_NO_USER_REFCOUNT
Definition DNA_ID.h:985
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
@ ID_TAG_NOT_ALLOCATED
Definition DNA_ID.h:992
@ ID_TAG_NO_MAIN
Definition DNA_ID.h:945
#define INDEX_ID_MAX
Definition DNA_ID.h:1328
ID_Type
@ ID_LI
@ ID_SCE
@ ID_GR
@ ID_OB
Read Guarded memory(de)allocation.
Iterator begin() const
Definition BLI_set.hh:461
int64_t size() const
Definition BLI_set.hh:564
Iterator end() const
Definition BLI_set.hh:471
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
void add(ID *old_id, ID *new_id)
const Set< ID * > & never_null_users() const
#define GS(x)
Definition iris.cc:202
void BKE_id_free_ex(Main *bmain, void *idv, const int flag_orig, const bool use_flag_from_idtag)
size_t BKE_id_multi_tagged_delete(Main *bmain)
void BKE_libblock_free_data_py(ID *id)
size_t BKE_id_multi_delete(Main *bmain, blender::Set< ID * > &ids_to_delete)
void BKE_id_free(Main *bmain, void *idv)
void BKE_libblock_free_data(ID *id, const bool do_id_user)
void BKE_id_delete_ex(Main *bmain, void *idv, const int extra_remapping_flags)
void BKE_id_free_us(Main *bmain, void *idv)
static int id_free(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag)
void BKE_id_delete(Main *bmain, void *idv)
static size_t id_delete(Main *bmain, blender::Set< ID * > &ids_to_delete, const int extra_remapping_flags)
void BKE_libblock_free_datablock(ID *id, const int)
BKE_library_remap_editor_id_reference_cb remap_editor_id_reference_cb
Definition lib_remap.cc:52
BKE_library_free_notifier_reference_cb free_notifier_reference_cb
Definition lib_remap.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
IDTypeFreeDataFunction free_data
Definition DNA_ID.h:413
int tag
Definition DNA_ID.h:434
struct Library * lib
Definition DNA_ID.h:419
char name[66]
Definition DNA_ID.h:425
ID id
Definition DNA_ID.h:529
bool is_locked_for_linking
Definition BKE_main.hh:176
bool is_memfile_undo_written
Definition BKE_main.hh:160
ListBase shapekeys
Definition BKE_main.hh:223
uint8_t flag
Definition wm_window.cc:138