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.
This commit is contained in:
Bastien Montagne
2025-03-22 18:21:36 +01:00
committed by Bastien Montagne
parent 0eba8caaf9
commit 3fa35aa4ee
4 changed files with 92 additions and 33 deletions

View File

@@ -23,6 +23,8 @@
* of IDs in a given Main data-base.
*/
#include "BLI_map.hh"
#include <optional>
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<Library *, Library *> *new_to_old_libraries_map,
Scene *scene,
ViewLayer *view_layer,
BlendFileReadReport *reports);
/**
* Advanced 'smart' function to delete library overrides (including their existing override

View File

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

View File

@@ -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<Library *, Library *> &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<Library *, Library *> 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<ID *>(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<Library *, Library *> 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<ID *>(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);

View File

@@ -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<Library *, Library *> *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<std::pair<ID *, ID *>> 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<Key *>(
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<Library *, Library *> *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<void *>(library),
reinterpret_cast<ID *>(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<Library *, Library *> *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,