Fix #122623: Partial Resync fails to remap all ID pointers to existing unchanged overrides.

Regression from fb8bf8b0c8: Now that the 'automatic override'
relationships between IDs are much more strict, re-creating
sub-hierarchies of overrides as part of the partial resync may not be
able to 'find' all previously used overrides anymore. An additional
remapping from linked (reference) data to existing, unchanged 'old'
overrides is needed.

In the report case, the root of the partial resync is the Curve obdata
itself, which will imply resync of its material. But the Armature object
used as dependency of the material (through the driver target) has
already been checked as not needing resync, so it is not included as
part of this partial resync sub-hierarchy anymore.

However, it means that the newly resync material override will have a
pointer to the linked armature object, which then needs to be remapped
to the matching existing (unchanged) override.

NOTE: this likely means that too many override IDs are still being
resynced currently. In the report-s case e.g., the Material ID likely
does not need to be resynced either.
This commit is contained in:
Bastien Montagne 2024-07-04 14:56:42 +02:00
parent 265a25ee42
commit 34d157634a

@ -1955,45 +1955,66 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
BKE_main_relations_free(bmain);
}
static void lib_override_library_remap(Main *bmain,
const ID *id_root_reference,
GHash *linkedref_to_old_override)
static void lib_override_library_remap(
Main *bmain,
const ID *id_root_reference,
blender::Vector<std::pair<ID *, ID *>> &references_and_new_overrides,
GHash *linkedref_to_old_override)
{
ID *id;
id::IDRemapper remapper;
id::IDRemapper remapper_overrides_old_to_new;
blender::Vector<ID *> nomain_ids;
blender::Vector<ID *> new_overrides;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
if (id_override_old == nullptr) {
continue;
}
remapper.add(id_override_old, id_override_new);
}
}
FOREACH_MAIN_ID_END;
/* Used to ensure that newly created overrides have all of their 'linked id' pointers remapped to
* the matching override if it exists. Necessary because in partial resync case, some existing
* liboverride may be used by the resynced ones, yet they would not be part of the resynced
* partial hierarchy, so #BKE_lib_override_library_create_from_tag cannot find them and handle
* their remapping. */
id::IDRemapper remapper_overrides_reference_to_old;
/* Remap no-main override IDs we just created too. */
GHashIterator linkedref_to_old_override_iter;
GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
ID *id_override_old_iter = static_cast<ID *>(
BLI_ghashIterator_getValue(&linkedref_to_old_override_iter));
if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) {
/* Add remapping from old to new overrides. */
for (auto [id_reference, id_override_new] : references_and_new_overrides) {
new_overrides.append(id_override_new);
ID *id_override_old = static_cast<ID *>(
BLI_ghash_lookup(linkedref_to_old_override, id_reference));
if (id_override_old == nullptr) {
continue;
}
remapper_overrides_old_to_new.add(id_override_old, id_override_new);
}
nomain_ids.append(id_override_old_iter);
GHashIterator linkedref_to_old_override_iter;
GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
/* Remap no-main override IDs we just created too. */
ID *id_override_old_iter = static_cast<ID *>(
BLI_ghashIterator_getValue(&linkedref_to_old_override_iter));
if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) != 0) {
nomain_ids.append(id_override_old_iter);
}
/* And remap linked data to old (existing, unchanged) overrides, when no new one was created.
*/
ID *id_reference_iter = static_cast<ID *>(
BLI_ghashIterator_getKey(&linkedref_to_old_override_iter));
BLI_assert(id_reference_iter->lib == id_root_reference->lib);
UNUSED_VARS_NDEBUG(id_root_reference);
if (!id_reference_iter->newid) {
remapper_overrides_reference_to_old.add(id_reference_iter, id_override_old_iter);
}
}
/* Remap all IDs to use the new override. */
BKE_libblock_remap_multiple(bmain, remapper, 0);
BKE_libblock_remap_multiple(bmain, remapper_overrides_old_to_new, 0);
BKE_libblock_relink_multiple(bmain,
nomain_ids,
ID_REMAP_TYPE_REMAP,
remapper,
remapper_overrides_old_to_new,
ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
/* In new overrides, remap linked ID to their matching already existing overrides. */
BKE_libblock_relink_multiple(bmain,
new_overrides,
ID_REMAP_TYPE_REMAP,
remapper_overrides_reference_to_old,
ID_REMAP_SKIP_OVERRIDE_LIBRARY);
}
/**
@ -2312,6 +2333,8 @@ static bool lib_override_library_resync(Main *bmain,
* 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);
/* Vector of pairs of reference IDs, and their new override IDs. */
blender::Vector<std::pair<ID *, ID *>> references_and_new_overrides;
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
@ -2323,6 +2346,8 @@ static bool lib_override_library_resync(Main *bmain,
continue;
}
ID *id_override_new = id_reference_iter->newid;
references_and_new_overrides.append(std::make_pair(id_reference_iter, id_override_new));
ID *id_override_old = static_cast<ID *>(
BLI_ghash_lookup(linkedref_to_old_override, id_reference_iter));
@ -2412,9 +2437,16 @@ static bool lib_override_library_resync(Main *bmain,
}
FOREACH_MAIN_LISTBASE_END;
/* We remap old to new override usages in a separate loop, after all new overrides have
* been added to Main. */
lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override);
/* We remap old to new override usages in a separate step, after all new overrides have
* been added to Main.
*
* This function also ensures that newly created overrides get all their linked ID pointers
* remapped to a valid override one, whether new or already existing. In partial resync case,
* #BKE_lib_override_library_create_from_tag cannot reliably discover _all_ valid existing
* overrides used by the newly resynced ones, since the local resynced hierarchy may not contain
* them. */
lib_override_library_remap(
bmain, id_root_reference, references_and_new_overrides, linkedref_to_old_override);
BKE_main_collection_sync(bmain);