From 3fa35aa4ee8ffb44caac23cbdae1d1301592c409 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Sat, 22 Mar 2025 18:21:36 +0100 Subject: [PATCH] LibOverride: Improve resync when relocating linked data. Relocation is the only case where old and new linked data may have a different library. This makes remapping dependencies IDs for the liboverrides that use these linked data somewhat more challenging and complicated. From basic tests with both simple data, and full production-level Mikassa char, it seems to behave fairly well now. --- source/blender/blenkernel/BKE_lib_override.hh | 17 ++-- source/blender/blenkernel/intern/blendfile.cc | 1 + .../intern/blendfile_link_append.cc | 23 ++++- .../blender/blenkernel/intern/lib_override.cc | 84 +++++++++++++------ 4 files changed, 92 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.hh b/source/blender/blenkernel/BKE_lib_override.hh index dbd516359be..968520c86ec 100644 --- a/source/blender/blenkernel/BKE_lib_override.hh +++ b/source/blender/blenkernel/BKE_lib_override.hh @@ -23,6 +23,8 @@ * of IDs in a given Main data-base. */ +#include "BLI_map.hh" + #include struct BlendFileReadReport; @@ -274,13 +276,18 @@ bool BKE_lib_override_library_resync(Main *bmain, * Then it will handle the resync of necessary IDs (through calls to * #BKE_lib_override_library_resync). * + * \param new_to_old_libraries_map: If not null, a mapping between new and old libraries. Only + * useful when they are not the same, e.g. when relocating a library or ID. + * * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in - * which case \a scene's master collection children hierarchy is used instead). + * which case \a scene's master collection children hierarchy is used instead). */ -void BKE_lib_override_library_main_resync(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - BlendFileReadReport *reports); +void BKE_lib_override_library_main_resync( + Main *bmain, + const blender::Map *new_to_old_libraries_map, + Scene *scene, + ViewLayer *view_layer, + BlendFileReadReport *reports); /** * Advanced 'smart' function to delete library overrides (including their existing override diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 9df81b6d9f4..fd9544a2505 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -1243,6 +1243,7 @@ static void setup_app_data(bContext *C, BKE_lib_override_library_main_resync( bmain, + nullptr, curscene, bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene), reports); diff --git a/source/blender/blenkernel/intern/blendfile_link_append.cc b/source/blender/blenkernel/intern/blendfile_link_append.cc index 2ce22019e6b..d20b014378b 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.cc +++ b/source/blender/blenkernel/intern/blendfile_link_append.cc @@ -1894,8 +1894,10 @@ static void blendfile_relocate_postprocess_cleanup(BlendfileLinkAppendContext &l } /** Update and resync as needed liboverrides. */ -static void blendfile_relocate_postprocess_liboverrides(BlendfileLinkAppendContext &lapp_context, - ReportList *reports) +static void blendfile_relocate_postprocess_liboverrides( + BlendfileLinkAppendContext &lapp_context, + const blender::Map &new_to_old_libraries_map, + ReportList *reports) { Main &bmain = *lapp_context.params->bmain; @@ -1922,6 +1924,7 @@ static void blendfile_relocate_postprocess_liboverrides(BlendfileLinkAppendConte BlendFileReadReport report{}; report.reports = reports; BKE_lib_override_library_main_resync(&bmain, + &new_to_old_libraries_map, lapp_context.params->context.scene, lapp_context.params->context.view_layer, &report); @@ -2011,6 +2014,10 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, * code is wrong, we need to redo it here after adding them back to main. */ BKE_main_id_refcount_recompute(bmain, false); + /* Mapping from old to new libraries, needed to allow liboverride resync to map properly old and + * new data. */ + blender::Map new_to_old_libraries_map; + BKE_layer_collection_resync_forbid(); /* Note that in reload case, we also want to replace indirect usages. */ const int remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | @@ -2018,6 +2025,9 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, for (BlendfileLinkAppendContextItem &item : lapp_context->items) { ID *old_id = static_cast(item.userdata); ID *new_id = item.new_id; + if (new_id) { + new_to_old_libraries_map.add(new_id->lib, old_id->lib); + } blendfile_library_relocate_id_remap(bmain, old_id, new_id, reports, do_reload, remap_flags); } BKE_layer_collection_resync_allow(); @@ -2029,7 +2039,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, blendfile_relocate_postprocess_cleanup(*lapp_context); /* Update and resync liboverrides of reloaded linked data-blocks. */ - blendfile_relocate_postprocess_liboverrides(*lapp_context, reports); + blendfile_relocate_postprocess_liboverrides(*lapp_context, new_to_old_libraries_map, reports); BKE_main_collection_sync(bmain); } @@ -2062,12 +2072,17 @@ void BKE_blendfile_id_relocate(BlendfileLinkAppendContext &lapp_context, ReportL /* Finalize relocation (remap ID usages, rebuild LibOverrides if needed, etc.). */ + /* Mapping from old to new libraries, needed to allow liboverride resync to map properly old and + * new data. */ + blender::Map new_to_old_libraries_map{}; + /* The first item should be the root of the relocation, and the only one containing a non-null * `userdata`. */ BlendfileLinkAppendContextItem &root_item = lapp_context.items.front(); BLI_assert(root_item.userdata); ID *old_id = static_cast(root_item.userdata); ID *new_id = root_item.new_id; + new_to_old_libraries_map.add(new_id->lib, old_id->lib); BLI_assert(GS(old_id->name) == GS(new_id->name)); #ifndef NDEBUG for (BlendfileLinkAppendContextItem &item : lapp_context.items) { @@ -2090,7 +2105,7 @@ void BKE_blendfile_id_relocate(BlendfileLinkAppendContext &lapp_context, ReportL blendfile_relocate_postprocess_cleanup(lapp_context); /* Update and resync liboverrides of reloaded linked data-blocks. */ - blendfile_relocate_postprocess_liboverrides(lapp_context, reports); + blendfile_relocate_postprocess_liboverrides(lapp_context, new_to_old_libraries_map, reports); BKE_main_collection_sync(bmain); diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 754e46bdd15..cd94780e253 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -2090,17 +2090,29 @@ static LibOverrideMissingIDsData_Key lib_override_library_resync_missing_id_key( return LibOverrideMissingIDsData_Key(id_name_key, id->lib); } -static LibOverrideMissingIDsData lib_override_library_resync_build_missing_ids_data(Main *bmain) +static LibOverrideMissingIDsData lib_override_library_resync_build_missing_ids_data( + Main *bmain, const bool is_relocate) { LibOverrideMissingIDsData missing_ids; ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (!ID_IS_LINKED(id_iter)) { - continue; + if (is_relocate) { + if (!ID_IS_OVERRIDE_LIBRARY(id_iter)) { + continue; + } + const int required_tags = ID_TAG_LIBOVERRIDE_NEED_RESYNC; + if ((id_iter->tag & required_tags) != required_tags) { + continue; + } } - const int required_tags = (ID_TAG_MISSING | ID_TAG_LIBOVERRIDE_NEED_RESYNC); - if ((id_iter->tag & required_tags) != required_tags) { - continue; + else { /* Handling of missing linked liboverrides. */ + if (!ID_IS_LINKED(id_iter)) { + continue; + } + const int required_tags = (ID_TAG_MISSING | ID_TAG_LIBOVERRIDE_NEED_RESYNC); + if ((id_iter->tag & required_tags) != required_tags) { + continue; + } } LibOverrideMissingIDsData_Key key = lib_override_library_resync_missing_id_key(id_iter); @@ -2129,16 +2141,18 @@ static ID *lib_override_library_resync_search_missing_ids_data( return match_id; } -static bool lib_override_library_resync(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - ID *id_root, - LinkNode *id_resync_roots, - ListBase *no_main_ids_list, - Collection *override_resync_residual_storage, - const bool do_hierarchy_enforce, - const bool do_post_process, - BlendFileReadReport *reports) +static bool lib_override_library_resync( + Main *bmain, + const blender::Map *new_to_old_libraries_map, + Scene *scene, + ViewLayer *view_layer, + ID *id_root, + LinkNode *id_resync_roots, + ListBase *no_main_ids_list, + Collection *override_resync_residual_storage, + const bool do_hierarchy_enforce, + const bool do_post_process, + BlendFileReadReport *reports) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -2341,10 +2355,19 @@ static bool lib_override_library_resync(Main *bmain, return success; } + /* This used to be the library of the root reference. Should always be the same as the current + * library on readfile case, but may differ when relocating linked data from a library to + * another (See #BKE_blendfile_id_relocate and #BKE_blendfile_library_relocate). */ + Library *id_root_reference_lib_old = (new_to_old_libraries_map ? + new_to_old_libraries_map->lookup_default( + id_root_reference->lib, id_root_reference->lib) : + id_root_reference->lib); + const bool is_relocate = id_root_reference_lib_old != id_root_reference->lib; + /* Get a mapping of all missing linked IDs that were liboverrides, to search for 'old * liboverrides' for newly created ones that do not already have one, in next step. */ LibOverrideMissingIDsData missing_ids_data = lib_override_library_resync_build_missing_ids_data( - bmain); + bmain, is_relocate); /* Vector of pairs of reference IDs, and their new override IDs. */ blender::Vector> references_and_new_overrides; @@ -2353,7 +2376,7 @@ static bool lib_override_library_resync(Main *bmain, ID *id_reference_iter; FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_reference_iter) { if ((id_reference_iter->tag & ID_TAG_DOIT) == 0 || id_reference_iter->newid == nullptr || - id_reference_iter->lib != id_root_reference->lib) + !ELEM(id_reference_iter->lib, id_root_reference->lib, id_root_reference_lib_old)) { continue; } @@ -2378,12 +2401,20 @@ static bool lib_override_library_resync(Main *bmain, * to it was stored in the local .blend file. however, since that linked liboverride ID does * not actually exist in the original library file, on next file read it is lost and marked * as missing ID. */ - if (id_override_old == nullptr && ID_IS_LINKED(id_override_new)) { + if (id_override_old == nullptr && (ID_IS_LINKED(id_override_new) || is_relocate)) { id_override_old = lib_override_library_resync_search_missing_ids_data(missing_ids_data, id_override_new); BLI_assert(id_override_old == nullptr || id_override_old->lib == id_override_new->lib); if (id_override_old != nullptr) { BLI_ghash_insert(linkedref_to_old_override, id_reference_iter, id_override_old); + + Key *key_override_old = BKE_key_from_id(id_override_old); + Key *key_reference_iter = BKE_key_from_id(id_reference_iter); + if (key_reference_iter && key_override_old) { + BLI_ghash_insert( + linkedref_to_old_override, &key_reference_iter->id, &key_override_old->id); + } + CLOG_INFO(&LOG_RESYNC, 2, "Found missing linked old override best-match %s for new linked override %s", @@ -2496,7 +2527,6 @@ static bool lib_override_library_resync(Main *bmain, Key *key_linked_reference = BKE_key_from_id(id_override_new->override_library->reference); BLI_assert(key_linked_reference != nullptr); BLI_assert(key_linked_reference->id.newid == &(*key_override_old_p)->id); - Key *key_override_old = static_cast( BLI_ghash_lookup(linkedref_to_old_override, &key_linked_reference->id)); BLI_assert(key_override_old != nullptr); @@ -2802,6 +2832,7 @@ bool BKE_lib_override_library_resync(Main *bmain, id_resync_roots.next = nullptr; const bool success = lib_override_library_resync(bmain, + nullptr, scene, view_layer, id_root, @@ -3295,6 +3326,7 @@ static void lib_override_resync_tagging_finalize(Main *bmain, */ static bool lib_override_library_main_resync_on_library_indirect_level( Main *bmain, + const blender::Map *new_to_old_libraries_map, Scene *scene, ViewLayer *view_layer, Collection *override_resync_residual_storage, @@ -3475,6 +3507,7 @@ static bool lib_override_library_main_resync_on_library_indirect_level( reinterpret_cast(library), reinterpret_cast(id_resync_roots->list->link)->name); const bool success = lib_override_library_resync(bmain, + new_to_old_libraries_map, scene, view_layer, id_root, @@ -3689,10 +3722,12 @@ static int lib_override_libraries_index_define(Main *bmain) return library_indirect_level_max; } -void BKE_lib_override_library_main_resync(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - BlendFileReadReport *reports) +void BKE_lib_override_library_main_resync( + Main *bmain, + const blender::Map *new_to_old_libraries_map, + Scene *scene, + ViewLayer *view_layer, + BlendFileReadReport *reports) { /* We use a specific collection to gather/store all 'orphaned' override collections and objects * generated by re-sync-process. This avoids putting them in scene's master collection. */ @@ -3735,6 +3770,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, * is needed at all). */ while (lib_override_library_main_resync_on_library_indirect_level( bmain, + new_to_old_libraries_map, scene, view_layer, override_resync_residual_storage,