diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index dc3335aa231..98a27ec6d56 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -56,28 +56,38 @@ enum { */ ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3, /** Do not remap library override pointers. */ - ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5, - /** Don't touch the special user counts (use when the 'old' remapped ID remains in use): - * - Do not transfer 'fake user' status from old to new ID. - * - Do not clear 'extra user' from old ID. */ - ID_REMAP_SKIP_USER_CLEAR = 1 << 6, + ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 4, /** * Force internal ID runtime pointers (like `ID.newid`, `ID.orig_id` etc.) to also be processed. * This should only be needed in some very specific cases, typically only BKE ID management code * should need it (e.g. required from `id_delete` to ensure no runtime pointer remains using * freed ones). */ - ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7, - /** Force handling user count even for IDs that are outside of Main (used in some cases when - * dealing with IDs temporarily out of Main, but which will be put in it ultimately). - */ - ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 5, + /** Force remapping of 'UI-like' ID usages (ID pointers stored in editors data etc.). */ + ID_REMAP_FORCE_UI_POINTERS = 1 << 6, /** * Force obdata pointers to also be processed, even when object (`id_owner`) is in Edit mode. * This is required by some tools creating/deleting IDs while operating in Edit mode, like e.g. * the 'separate' mesh operator. */ - ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9, + ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 7, + + /** Don't touch the special user counts (use when the 'old' remapped ID remains in use): + * - Do not transfer 'fake user' status from old to new ID. + * - Do not clear 'extra user' from old ID. */ + ID_REMAP_SKIP_USER_CLEAR = 1 << 16, + /** Force handling user count even for IDs that are outside of Main (used in some cases when + * dealing with IDs temporarily out of Main, but which will be put in it ultimately). + */ + ID_REMAP_FORCE_USER_REFCOUNT = 1 << 17, + /** Do NOT handle user count for IDs (used in some cases when dealing with IDs from different + * BMains, if usercount will be recomputed anyway afterwards, like e.g. in memfile reading during + * undo step decoding). */ + ID_REMAP_SKIP_USER_REFCOUNT = 1 << 18, + /** Do NOT tag IDs which had some of their ID pointers updated for update in the depsgraph, or ID + * type specific updates, like e.g. with node trees. */ + ID_REMAP_SKIP_UPDATE_TAGGING = 1 << 19, }; typedef enum eIDRemapType { @@ -95,12 +105,24 @@ typedef enum eIDRemapType { */ void BKE_libblock_remap_multiple_locked(struct Main *bmain, struct IDRemapper *mappings, - short remap_flags); + const int remap_flags); void BKE_libblock_remap_multiple(struct Main *bmain, struct IDRemapper *mappings, - short remap_flags); + const int remap_flags); +/** + * Bare raw remapping of IDs, with no other processing than actually updating the ID pointers. No + * usercount, direct vs indirect linked status update, depsgraph tagging, etc. + * + * This is way more efficient than regular remapping from #BKE_libblock_remap_multiple & co, but it + * implies that calling code handles all the other aspects described above. This is typically the + * case e.g. in readfile process. + * + * WARNING: This call will likely leave the given BMain in invalid state in many aspects. */ +void BKE_libblock_remap_multiple_raw(struct Main *bmain, + struct IDRemapper *mappings, + const int remap_flags); /** * Replace all references in given Main to \a old_id by \a new_id * (if \a new_id is NULL, it unlinks \a old_id). @@ -108,9 +130,9 @@ void BKE_libblock_remap_multiple(struct Main *bmain, * \note Requiring new_id to be non-null, this *may* not be the case ultimately, * but makes things simpler for now. */ -void BKE_libblock_remap_locked(struct Main *bmain, void *old_idv, void *new_idv, short remap_flags) +void BKE_libblock_remap_locked(struct Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1, 2); -void BKE_libblock_remap(struct Main *bmain, void *old_idv, void *new_idv, short remap_flags) +void BKE_libblock_remap(struct Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1, 2); /** @@ -130,12 +152,12 @@ void BKE_libblock_unlink(struct Main *bmain, * * \param old_idv: Unlike BKE_libblock_remap, can be NULL, * in which case all ID usages by given \a idv will be cleared. + * + * \param bmain: May be NULL, in which case there won't be depsgraph updates nor post-processing on + * some ID types (like collections or objects) to ensure their runtime data is valid. */ -void BKE_libblock_relink_ex(struct Main *bmain, - void *idv, - void *old_idv, - void *new_idv, - short remap_flags) ATTR_NONNULL(1, 2); +void BKE_libblock_relink_ex( + struct Main *bmain, void *idv, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(2); /** * Same as #BKE_libblock_relink_ex, but applies all rules defined in \a id_remapper to \a ids (or * does cleanup if `ID_REMAP_TYPE_CLEANUP` is specified as \a remap_type). @@ -144,7 +166,7 @@ void BKE_libblock_relink_multiple(struct Main *bmain, struct LinkNode *ids, eIDRemapType remap_type, struct IDRemapper *id_remapper, - short remap_flags); + int remap_flags); /** * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index b558a2f825d..7fe9f514e2a 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -1426,7 +1426,7 @@ static void blendfile_library_relocate_remap(Main *bmain, ID *new_id, ReportList *reports, const bool do_reload, - const short remap_flags) + const int remap_flags) { BLI_assert(old_id); if (do_reload) { @@ -1594,8 +1594,8 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, BKE_layer_collection_resync_forbid(); /* Note that in reload case, we also want to replace indirect usages. */ - const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | - (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); + const int remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | + (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); for (item_idx = 0, itemlink = lapp_context->items.list; itemlink; item_idx++, itemlink = itemlink->next) { BlendfileLinkAppendContextItem *item = itemlink->link; diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 79500c567f2..857ad6f1e6e 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -60,7 +60,7 @@ typedef struct IDRemap { /** The ID in which we are replacing old_id by new_id usages. */ ID *id_owner; - short flag; + int flag; } IDRemap; /* IDRemap->flag enums defined in BKE_lib.h */ @@ -104,30 +104,46 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, const IDRemapperApplyOptions id_remapper_options, const int cb_flag, const bool is_indirect, - const bool violates_never_null, - const bool force_user_refcount) + const bool violates_never_null) { + const bool skip_update_tagging = (id_remap_data->flag & ID_REMAP_SKIP_UPDATE_TAGGING) != 0; + const bool skip_user_refcount = (id_remap_data->flag & ID_REMAP_SKIP_USER_REFCOUNT) != 0; + const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0; + BLI_assert(!skip_user_refcount || !force_user_refcount); + ID *old_id = *id_ptr; if (!violates_never_null) { BKE_id_remapper_apply_ex(mappings, id_ptr, id_remapper_options, id_self); - DEG_id_tag_update_ex(id_remap_data->bmain, - id_self, - ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - if (id_self != id_owner) { - DEG_id_tag_update_ex(id_remap_data->bmain, - id_owner, - ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - if (GS(id_owner->name) == ID_NT) { - /* Make sure that the node tree is updated after a property in it changed. Ideally, we would - * know which nodes property was changed so that only this node is tagged. */ - BKE_ntree_update_tag_all((bNodeTree *)id_owner); + if (!skip_update_tagging) { + if (id_remap_data->bmain != NULL) { + DEG_id_tag_update_ex(id_remap_data->bmain, + id_self, + ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + if (id_self != id_owner) { + DEG_id_tag_update_ex(id_remap_data->bmain, + id_owner, + ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + } + if (GS(id_owner->name) == ID_NT) { + /* Make sure that the node tree is updated after a property in it changed. Ideally, we + * would know which nodes property was changed so that only this node is tagged. */ + BKE_ntree_update_tag_all((bNodeTree *)id_owner); + } } } /* Get the new_id pointer. When the mapping is violating never null we should use a NULL * pointer otherwise the incorrect users are decreased and increased on the same instance. */ ID *new_id = violates_never_null ? NULL : *id_ptr; + if (!is_indirect && new_id) { + new_id->runtime.remap.status |= ID_REMAP_IS_LINKED_DIRECT; + } + + if (skip_user_refcount) { + return; + } + if (cb_flag & IDWALK_CB_USER) { /* NOTE: by default we don't user-count IDs which are not in the main database. * This is because in certain conditions we can have data-blocks in @@ -148,9 +164,6 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner, /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) * are assumed to be set as needed, that extra user is processed in final handling. */ } - if (!is_indirect && new_id) { - new_id->runtime.remap.status |= ID_REMAP_IS_LINKED_DIRECT; - } } static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) @@ -218,7 +231,6 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; - const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0; #ifdef DEBUG_PRINT printf( @@ -264,8 +276,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) id_remapper_options, cb_flag, is_indirect, - violates_never_null, - force_user_refcount); + violates_never_null); } return IDWALK_RET_NOP; @@ -449,7 +460,8 @@ static void libblock_remap_reset_remapping_status_callback(ID *old_id, * (i.e. \a id does not references any other data-block anymore). * + If \a old_id is non-NULL, behavior is as with a NULL \a id, but only within given \a id. * - * \param bmain: the Main data storage to operate on (must never be NULL). + * \param bmain: the Main data storage to operate on (may be NULL, in which case part of the + * post-process/depsgraph update won't happen). * \param id: the data-block to operate on * (can be NULL, in which case we operate over all IDs from given bmain). * \param old_id: the data-block to dereference (may be NULL if \a id is non-NULL). @@ -457,17 +469,18 @@ static void libblock_remap_reset_remapping_status_callback(ID *old_id, * \param r_id_remap_data: if non-NULL, the IDRemap struct to use * (useful to retrieve info about remapping process). */ -ATTR_NONNULL(1) static void libblock_remap_data(Main *bmain, ID *id, eIDRemapType remap_type, struct IDRemapper *id_remapper, - const short remap_flags) + const int remap_flags) { IDRemap id_remap_data = {0}; - const int foreach_id_flags = ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? - IDWALK_DO_INTERNAL_RUNTIME_POINTERS : - IDWALK_NOP); + const int foreach_id_flags = + (((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? + IDWALK_DO_INTERNAL_RUNTIME_POINTERS : + IDWALK_NOP) | + ((remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0 ? IDWALK_INCLUDE_UI : IDWALK_NOP)); id_remap_data.id_remapper = id_remapper; id_remap_data.type = remap_type; @@ -519,7 +532,7 @@ static void libblock_remap_data(Main *bmain, typedef struct LibblockRemapMultipleUserData { Main *bmain; - short remap_flags; + int remap_flags; } LibBlockRemapMultipleUserData; static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data) @@ -530,7 +543,7 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ LibBlockRemapMultipleUserData *data = user_data; Main *bmain = data->bmain; - const short remap_flags = data->remap_flags; + const int remap_flags = data->remap_flags; BLI_assert(old_id != NULL); BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); @@ -614,7 +627,7 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ void BKE_libblock_remap_multiple_locked(Main *bmain, struct IDRemapper *mappings, - const short remap_flags) + const int remap_flags) { if (BKE_id_remapper_is_empty(mappings)) { /* Early exit nothing to do. */ @@ -640,7 +653,23 @@ void BKE_libblock_remap_multiple_locked(Main *bmain, DEG_relations_tag_update(bmain); } -void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +void BKE_libblock_remap_multiple_raw(Main *bmain, + struct IDRemapper *mappings, + const int remap_flags) +{ + if (BKE_id_remapper_is_empty(mappings)) { + /* Early exit nothing to do. */ + return; + } + + libblock_remap_data(bmain, + NULL, + ID_REMAP_TYPE_REMAP, + mappings, + remap_flags | ID_REMAP_SKIP_USER_REFCOUNT | ID_REMAP_SKIP_UPDATE_TAGGING); +} + +void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const int remap_flags) { struct IDRemapper *remapper = BKE_id_remapper_create(); ID *old_id = old_idv; @@ -650,7 +679,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const BKE_id_remapper_free(remapper); } -void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const int remap_flags) { BKE_main_lock(bmain); @@ -659,7 +688,7 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r BKE_main_unlock(bmain); } -void BKE_libblock_remap_multiple(Main *bmain, struct IDRemapper *mappings, const short remap_flags) +void BKE_libblock_remap_multiple(Main *bmain, struct IDRemapper *mappings, const int remap_flags) { BKE_main_lock(bmain); @@ -673,8 +702,8 @@ void BKE_libblock_unlink(Main *bmain, const bool do_flag_never_null, const bool do_skip_indirect) { - const short remap_flags = (do_skip_indirect ? ID_REMAP_SKIP_INDIRECT_USAGE : 0) | - (do_flag_never_null ? ID_REMAP_FLAG_NEVER_NULL_USAGE : 0); + const int remap_flags = (do_skip_indirect ? ID_REMAP_SKIP_INDIRECT_USAGE : 0) | + (do_flag_never_null ? ID_REMAP_FLAG_NEVER_NULL_USAGE : 0); BKE_main_lock(bmain); @@ -756,7 +785,7 @@ void BKE_libblock_relink_multiple(Main *bmain, LinkNode *ids, const eIDRemapType remap_type, struct IDRemapper *id_remapper, - const short remap_flags) + const int remap_flags) { BLI_assert(remap_type == ID_REMAP_TYPE_REMAP || BKE_id_remapper_is_empty(id_remapper)); @@ -765,6 +794,10 @@ void BKE_libblock_relink_multiple(Main *bmain, libblock_remap_data(bmain, id_iter, remap_type, id_remapper, remap_flags); } + if (bmain == NULL) { + return; + } + switch (remap_type) { case ID_REMAP_TYPE_REMAP: { LibBlockRelinkMultipleUserData user_data = {0}; @@ -815,7 +848,7 @@ void BKE_libblock_relink_multiple(Main *bmain, } void BKE_libblock_relink_ex( - Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags) + Main *bmain, void *idv, void *old_idv, void *new_idv, const int remap_flags) { /* Should be able to replace all _relink() functions (constraints, rigidbody, etc.) ? */ @@ -905,8 +938,8 @@ void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) libblock_relink_to_newid_prepare_data(bmain, id, &relink_data); - const short remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | - ID_REMAP_SKIP_OVERRIDE_LIBRARY; + const int remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | + ID_REMAP_SKIP_OVERRIDE_LIBRARY; BKE_libblock_relink_multiple( bmain, relink_data.ids, ID_REMAP_TYPE_REMAP, relink_data.id_remapper, remap_flag_final);