IDManagement: Extend ID remapping code.

This commits adds some new, specific flags to further control ID
remapping process (like and option to skip user refcounting completely).

It also adds a new function to do 'raw' remapping, without any extra
post-processing, depsgraph tagging, etc. This is not used currently, but
will soon be needed by readfile post-processing code changes.

There is also some small cleanups and reorganization in that area of code,
the main noticeable change being the switch from a short to an int for
the flags controlling remapping code (using short here does not give
any benefit, and makes it harder to switch to integers when it becomes
necessary).

No change in behaviors are expected from this commit.
This commit is contained in:
Bastien Montagne
2023-04-14 16:38:38 +02:00
parent 988f23cec3
commit fed463df78
3 changed files with 118 additions and 63 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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);