Blender V5.0
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
10
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 "BLO_readfile.hh"
37
38#include "lib_intern.hh"
39
40#include "DEG_depsgraph.hh"
41
42#ifdef WITH_PYTHON
43# include "BPY_extern.hh"
44#endif
45
46using namespace blender::bke::id;
47
48void BKE_libblock_free_data(ID *id, const bool do_id_user)
49{
50 if (id->properties) {
51 IDP_FreePropertyContent_ex(id->properties, do_id_user);
52 MEM_freeN(id->properties);
53 id->properties = nullptr;
54 }
55 if (id->system_properties) {
56 IDP_FreePropertyContent_ex(id->system_properties, do_id_user);
57 MEM_freeN(id->system_properties);
58 id->system_properties = nullptr;
59 }
60
61 if (id->override_library) {
62 BKE_lib_override_library_free(&id->override_library, do_id_user);
63 id->override_library = nullptr;
64 }
65
66 if (id->asset_data) {
67 BKE_asset_metadata_free(&id->asset_data);
68 }
69
70 if (id->library_weak_reference != nullptr) {
71 MEM_freeN(id->library_weak_reference);
72 }
73
74 BKE_animdata_free(id, do_id_user);
75
77}
78
79void BKE_libblock_free_datablock(ID *id, const int /*flag*/)
80{
81 const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id);
82
83 if (idtype_info != nullptr) {
84 if (idtype_info->free_data != nullptr) {
85 idtype_info->free_data(id);
86 }
87 return;
88 }
89
90 BLI_assert_msg(0, "IDType Missing IDTypeInfo");
91}
92
94{
95 if (id->runtime) {
96 /* During "normal" file loading this data is released when versioning ends. Some versioning
97 * code also deletes IDs, though. For example, in the startup blend file, brushes that were
98 * replaced by assets are deleted. This means that the regular "delete this ID" flow (aka this
99 * code here) also needs to free this data. */
101
102 MEM_SAFE_DELETE(id->runtime);
103 }
104}
105
106static int id_free(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag)
107{
108 ID *id = static_cast<ID *>(idv);
109
110 if (use_flag_from_idtag) {
111 if ((id->tag & ID_TAG_NO_MAIN) != 0) {
113 }
114 else {
116 }
117
118 if ((id->tag & ID_TAG_NO_USER_REFCOUNT) != 0) {
120 }
121 else {
123 }
124
125 if ((id->tag & ID_TAG_NOT_ALLOCATED) != 0) {
127 }
128 else {
130 }
131 }
132
133 BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || bmain != nullptr);
136
137 const short type = GS(id->name);
138
139 if (bmain && (flag & LIB_ID_FREE_NO_DEG_TAG) == 0) {
140 BLI_assert(bmain->is_locked_for_linking == false);
141
142 DEG_id_type_tag(bmain, type);
143 }
144
146
147 Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : nullptr;
148
149 if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) {
150 BKE_libblock_relink_ex(bmain, id, nullptr, nullptr, ID_REMAP_SKIP_USER_CLEAR);
151 }
152
153 if ((flag & LIB_ID_FREE_NO_MAIN) == 0 && key != nullptr) {
154 id_free(bmain, &key->id, flag, use_flag_from_idtag);
155 }
156
158
159 /* avoid notifying on removed data */
160 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
161 BKE_main_lock(bmain);
162 }
163
164 if ((flag & LIB_ID_FREE_NO_UI_USER) == 0) {
167 }
168
170 IDRemapper remapper;
171 remapper.add(id, nullptr);
173 }
174 }
175
176 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
177 ListBase *lb = which_libbase(bmain, type);
178 BLI_remlink(lb, id);
179 if ((flag & LIB_ID_FREE_NO_NAMEMAP_REMOVE) == 0) {
180 BKE_main_namemap_remove_id(*bmain, *id);
181 }
182 }
183
185
186 if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
187 BKE_main_unlock(bmain);
188 }
189
190 if ((flag & LIB_ID_FREE_NOT_ALLOCATED) == 0) {
191 MEM_freeN(id);
192 }
193
194 return flag;
195}
196
197void BKE_id_free_ex(Main *bmain, void *idv, const int flag_orig, const bool use_flag_from_idtag)
198{
199 /* ViewLayer resync needs to be delayed during Scene freeing, since internal relationships
200 * between the Scene's master collection and its view_layers become invalid
201 * (due to remapping). */
202 if (bmain && (flag_orig & LIB_ID_FREE_NO_MAIN) == 0) {
204 }
205
206 const ID_Type id_type = GS(static_cast<ID *>(idv)->name);
207
208 int flag_final = id_free(bmain, idv, flag_orig, use_flag_from_idtag);
209
210 if (bmain) {
211 if ((flag_orig & LIB_ID_FREE_NO_MAIN) == 0) {
213 }
214
215 if ((flag_final & LIB_ID_FREE_NO_MAIN) == 0) {
216 if (ELEM(id_type, ID_SCE, ID_GR, ID_OB)) {
218 }
219 }
220 }
221}
222
223void BKE_id_free(Main *bmain, void *idv)
224{
225 BKE_id_free_ex(bmain, idv, 0, true);
226}
227
228void BKE_id_free_us(Main *bmain, void *idv) /* test users */
229{
230 ID *id = static_cast<ID *>(idv);
231
232 id_us_min(id);
233
234 /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding collections
235 * when deleting an object. Since only 'user_one' usage of objects is collections,
236 * and only 'real user' usage of objects is scenes, removing that 'user_one' tag when there
237 * is no more real (scene) users of an object ensures it gets fully unlinked.
238 * But only for local objects, not linked ones!
239 * Otherwise, there is no real way to get rid of an object anymore -
240 * better handling of this is TODO.
241 */
242 if ((GS(id->name) == ID_OB) && (id->us == 1) && !ID_IS_LINKED(id)) {
244 }
245
246 if (id->us == 0) {
247 const bool is_lib = GS(id->name) == ID_LI;
248
249 BKE_libblock_unlink(bmain, id, false);
250
251 BKE_id_free(bmain, id);
252
253 if (is_lib) {
255 }
256 }
257}
258
259static size_t id_delete(Main *bmain,
260 blender::Set<ID *> &ids_to_delete,
261 const int extra_remapping_flags)
262{
263 bool has_deleted_library = false;
264
265 /* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database,
266 * and has already properly unlinked its other IDs usages.
267 * UI users are always cleared in BKE_libblock_remap_locked() call, so we can always skip it. */
268 const int free_flag = LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_MAIN |
271 ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS | extra_remapping_flags);
272
273 MainListsArray lbarray = BKE_main_lists_get(*bmain);
274 const int base_count = lbarray.size();
275
276 BKE_main_lock(bmain);
278 IDRemapper id_remapper;
279
280 /* Main idea of batch deletion is to remove all IDs to be deleted from Main database.
281 * This means that we won't have to loop over all deleted IDs to remove usages
282 * of other deleted IDs.
283 * This gives tremendous speed-up when deleting a large amount of IDs from a Main
284 * containing thousands of these.
285 * This also means that we have to be very careful here, as we bypass many 'common'
286 * processing, hence risking to 'corrupt' at least user counts, if not IDs themselves. */
287 bool keep_looping = true;
288 while (keep_looping) {
289 keep_looping = false;
290
291 /* First tag and remove from Main all datablocks directly from target lib.
292 * Note that we go forward here, since we want to check dependencies before users
293 * (e.g. meshes before objects). Reduces the chances to have to loop many times in the
294 * `while (keep_looking)` outer loop. */
295 for (int i = 0; i < base_count; i++) {
296 ListBase *lb = lbarray[i];
297 ID *id_iter;
298
299 FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
300 /* NOTE: in case we delete a library, we also delete all its datablocks! */
301 if (ids_to_delete.contains(id_iter) ||
302 (ID_IS_LINKED(id_iter) && ids_to_delete.contains(&id_iter->lib->id)))
303 {
304 BLI_remlink(lb, id_iter);
305 BKE_main_namemap_remove_id(*bmain, *id_iter);
306 ids_to_delete.add(id_iter);
307 id_remapper.add(id_iter, nullptr);
308 /* Do not tag as no_main now, we want to unlink it first (lower-level ID management
309 * code has some specific handling of 'no main' IDs that would be a problem in that
310 * case). */
311
312 /* Forcefully also delete shapekeys of the deleted ID if any, 'orphaned' shapekeys are
313 * not allowed in Blender and will cause lots of problem in modern code (liboverrides,
314 * warning on write & read, etc.). */
315 Key *shape_key = BKE_key_from_id(id_iter);
316 if (shape_key && !ids_to_delete.contains(&shape_key->id)) {
317 BLI_remlink(&bmain->shapekeys, &shape_key->id);
318 BKE_main_namemap_remove_id(*bmain, shape_key->id);
319 ids_to_delete.add(&shape_key->id);
320 id_remapper.add(&shape_key->id, nullptr);
321 }
322
323 keep_looping = true;
324 }
325 }
327 }
328
329 /* Will tag 'never nullptr' users of this ID too.
330 *
331 * NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
332 * links, this can lead to nasty crashing here in second, actual deleting loop.
333 * Also, this will also flag users of deleted data that cannot be unlinked
334 * (object using deleted obdata, etc.), so that they also get deleted. */
335 BKE_libblock_remap_multiple_locked(bmain, id_remapper, remapping_flags);
336 for (ID *id_never_null_iter : id_remapper.never_null_users()) {
337 ids_to_delete.add(id_never_null_iter);
338 }
339 id_remapper.clear();
340 }
341
342 /* Remapping above may have left some Library::runtime::archived_libraries items to nullptr,
343 * clean this up and shrink the vector accordingly. */
345
346 /* Since we removed IDs from Main, their own other IDs usages need to be removed 'manually'. */
347 blender::Vector<ID *> cleanup_ids{ids_to_delete.begin(), ids_to_delete.end()};
349 bmain,
350 cleanup_ids,
352 id_remapper,
354 cleanup_ids.clear();
355
356 /* Now we can safely mark that ID as not being in Main database anymore. */
357 /* NOTE: This needs to be done in a separate loop than above, otherwise some user-counts of
358 * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID user-counts
359 * is never affected). */
360 for (ID *id : ids_to_delete) {
361 id->tag |= ID_TAG_NO_MAIN;
362 /* User-count needs to be reset artificially, since some usages may not be cleared in batch
363 * deletion (typically, if one deleted ID uses another deleted ID, this may not be cleared by
364 * remapping code, depending on order in which these are handled). */
365 id->us = ID_FAKE_USERS(id);
366
367 if (!has_deleted_library && GS(id->name) == ID_LI) {
368 has_deleted_library = true;
369 }
370
371 id_free(bmain, id, free_flag, false);
372 }
373
374 BKE_main_unlock(bmain);
377
378 if (has_deleted_library) {
380 }
381
382 bmain->is_memfile_undo_written = false;
383 return size_t(ids_to_delete.size());
384}
385
386void BKE_id_delete_ex(Main *bmain, void *idv, const int extra_remapping_flags)
387{
388 ID *id = static_cast<ID *>(idv);
389 BLI_assert_msg((id->tag & ID_TAG_NO_MAIN) == 0, "Cannot be used with IDs outside of Main");
390
391 blender::Set<ID *> ids_to_delete = {id};
392 id_delete(bmain, ids_to_delete, extra_remapping_flags);
393}
394
395void BKE_id_delete(Main *bmain, void *idv)
396{
397 BKE_id_delete_ex(bmain, idv, 0);
398}
399
401{
402 blender::Set<ID *> ids_to_delete;
403 ID *id_iter;
404 FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
405 if (id_iter->tag & ID_TAG_DOIT) {
406 ids_to_delete.add(id_iter);
407 }
408 }
410 return id_delete(bmain, ids_to_delete, 0);
411}
412
413size_t BKE_id_multi_delete(Main *bmain, blender::Set<ID *> &ids_to_delete)
414{
415 return id_delete(bmain, ids_to_delete, 0);
416}
417
418/* -------------------------------------------------------------------- */
421
423{
424#ifdef WITH_PYTHON
425# ifdef WITH_PYTHON_SAFETY
426 BPY_id_release(id);
427# endif
428 if (id->py_instance) {
429 BPY_DECREF_RNA_INVALIDATE(id->py_instance);
430 }
431#else
432 UNUSED_VARS(id);
433#endif
434}
435
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:188
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:1213
const IDTypeInfo * BKE_idtype_get_info_from_id(const ID *id)
Definition idtype.cc:146
Key * BKE_key_from_id(ID *id)
Definition key.cc:1771
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:331
void id_us_min(ID *id)
Definition lib_id.cc:366
@ 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 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:655
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:792
@ ID_REMAP_TYPE_CLEANUP
void void void BKE_libblock_unlink(Main *bmain, void *idv, bool do_skip_indirect) ATTR_NONNULL()
Definition lib_remap.cc:720
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:854
@ 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_library_main_rebuild_hierarchy(Main *bmain)
Definition library.cc:328
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
MainListsArray BKE_main_lists_get(Main &bmain)
Definition main.cc:987
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:902
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:552
std::array< ListBase *, INDEX_ID_MAX - 1 > MainListsArray
Definition BKE_main.hh:645
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:546
void BKE_main_lock(Main *bmain)
Definition main.cc:486
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
void BKE_main_unlock(Main *bmain)
Definition main.cc:491
void BKE_main_namemap_remove_id(Main &bmain, ID &id)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define UNUSED_VARS(...)
#define ELEM(...)
external readfile function prototypes.
void BLO_readfile_id_runtime_data_free(ID &id)
Definition readfile.cc:2220
void BPY_id_release(ID *id)
Definition bpy_rna.cc:397
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.
@ ID_TAG_NO_USER_REFCOUNT
Definition DNA_ID.h:1018
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
@ ID_TAG_NOT_ALLOCATED
Definition DNA_ID.h:1025
@ ID_TAG_NO_MAIN
Definition DNA_ID.h:978
#define ID_FAKE_USERS(id)
Definition DNA_ID.h:655
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
ID_Type
@ ID_LI
@ ID_SCE
@ ID_GR
@ ID_OB
Read Guarded memory(de)allocation.
Iterator begin() const
Definition BLI_set.hh:480
int64_t size() const
Definition BLI_set.hh:587
Iterator end() const
Definition BLI_set.hh:490
bool contains(const Key &key) const
Definition BLI_set.hh:310
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)
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)
void BKE_libblock_free_runtime_data(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:51
BKE_library_free_notifier_reference_cb free_notifier_reference_cb
Definition lib_remap.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void main_cleanup_parent_archives(Main &bmain)
Definition library.cc:714
const char * name
IDTypeFreeDataFunction free_data
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
struct Library * lib
Definition DNA_ID.h:420
ID id
Definition DNA_ID.h:550
bool is_locked_for_linking
Definition BKE_main.hh:224
bool is_memfile_undo_written
Definition BKE_main.hh:208
ListBase shapekeys
Definition BKE_main.hh:290
i
Definition text_draw.cc:230
uint8_t flag
Definition wm_window.cc:145