Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/intern/lib_override.c562
1 files changed, 378 insertions, 184 deletions
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index d65c0bb4e95..e42320fa2ca 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -724,7 +724,23 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
/* For each object tagged for override, ensure we get at least one local or liboverride
* collection to host it. Avoids getting a bunch of random object in the scene's master
* collection when all objects' dependencies are not properly 'packed' into a single root
- * collection. */
+ * collection.
+ *
+ * NOTE: In resync case, we do not handle this at all, since:
+ * - In normal, valid cases nothing would be needed anyway (resync process takes care
+ * of tagging needed 'owner' collection then).
+ * - Partial resync makes it extremely difficult to properly handle such extra
+ * collection 'tagging for override' (since one would need to know if the new object
+ * is actually going to replace an already existing override [most common case], or
+ * if it is actually a real new 'orphan' one).
+ * - While not ideal, having objects dangling around is less critical than both points
+ * above.
+ * So if users add new objects to their library override hierarchy in an invalid way, so
+ * be it. Trying to find a collection to override and host this new object would most
+ * likely make existing override very unclean anyway. */
+ if (is_resync) {
+ return;
+ }
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ID_IS_LINKED(ob) && (ob->id.tag & data->tag) != 0) {
Collection *instantiating_collection = NULL;
@@ -738,17 +754,17 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
instantiating_collection_linknode != NULL;
instantiating_collection_linknode = instantiating_collection_linknode->next) {
instantiating_collection = instantiating_collection_linknode->link;
- /* In (recursive) resync case, if a collection of a 'parent' lib instantiates the
- * linked object, it is also fine. */
- if (!ID_IS_LINKED(instantiating_collection) ||
- (is_resync && ID_IS_LINKED(id_root) &&
- instantiating_collection->id.lib->temp_index < id_root->lib->temp_index)) {
+ if (!ID_IS_LINKED(instantiating_collection)) {
+ /* There is a local collection instantiating the linked object to override, nothing
+ * else to be done here. */
break;
}
- if (ID_IS_LINKED(instantiating_collection) &&
- (!is_resync || instantiating_collection->id.lib == id_root->lib)) {
- instantiating_collection_override_candidate = instantiating_collection;
+ if (instantiating_collection->id.tag & data->tag) {
+ /* There is a linked collection instantiating the linked object to override,
+ * already tagged to be overridden, nothing else to be done here. */
+ break;
}
+ instantiating_collection_override_candidate = instantiating_collection;
instantiating_collection = NULL;
}
}
@@ -1293,6 +1309,8 @@ 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,
@@ -1301,6 +1319,7 @@ static bool lib_override_library_resync(Main *bmain,
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
ID *id_root_reference = id_root->override_library->reference;
+ ID *id;
if (id_root_reference->tag & LIB_TAG_MISSING) {
BKE_reportf(reports != NULL ? reports->reports : NULL,
@@ -1321,94 +1340,145 @@ static bool lib_override_library_resync(Main *bmain,
.is_override = true,
.is_resync = true};
lib_override_group_tag_data_object_to_collection_init(&data);
- lib_override_overrides_group_tag(&data);
-
- BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
- data.id_root = id_root_reference;
- data.is_override = false;
- lib_override_linked_group_tag(&data);
-
- BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
- lib_override_hierarchy_dependencies_recursive_tag(&data);
- /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides. */
+ /* Mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, populated from
+ * each sub-tree that actually needs to be resynced. */
GHash *linkedref_to_old_override = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
- ID *id;
- FOREACH_MAIN_ID_BEGIN (bmain, id) {
- /* IDs that get fully removed from linked data remain as local overrides (using place-holder
- * linked IDs as reference), but they are often not reachable from any current valid local
- * override hierarchy anymore. This will ensure they get properly deleted at the end of this
- * function. */
- if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
- (id->override_library->reference->tag & LIB_TAG_MISSING) != 0 &&
- /* Unfortunately deleting obdata means deleting their objects too. Since there is no
- * guarantee that a valid override object using an obsolete override obdata gets properly
- * updated, we ignore those here for now. In practice this should not be a big issue. */
- !OB_DATA_SUPPORT_ID(GS(id->name))) {
- id->tag |= LIB_TAG_MISSING;
- }
-
- if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
- /* While this should not happen in typical cases (and won't be properly supported here), user
- * is free to do all kind of very bad things, including having different local overrides of a
- * same linked ID in a same hierarchy. */
- IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
- ID *reference_id = id_override_library->reference;
- if (GS(reference_id->name) != GS(id->name)) {
- switch (GS(id->name)) {
- case ID_KE:
- reference_id = (ID *)BKE_key_from_id(reference_id);
- break;
- case ID_GR:
- BLI_assert(GS(reference_id->name) == ID_SCE);
- reference_id = (ID *)((Scene *)reference_id)->master_collection;
- break;
- case ID_NT:
- reference_id = (ID *)ntreeFromID(id);
- break;
- default:
- break;
- }
+
+ /* Only tag linked IDs from related linked reference hierarchy that are actually part of
+ * the sub-trees of each detected sub-roots needing resync. */
+ for (LinkNode *resync_root_link = id_resync_roots; resync_root_link != NULL;
+ resync_root_link = resync_root_link->next) {
+ ID *id_resync_root = resync_root_link->link;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_resync_root));
+
+ if ((id_resync_root->tag & LIB_TAG_NO_MAIN) != 0) {
+ CLOG_ERROR(&LOG,
+ "While dealing with root '%s', resync root ID '%s' (%p) found to be alreaady "
+ "resynced.\n",
+ id_root->name,
+ id_resync_root->name,
+ id_resync_root);
+ }
+ // if (no_main_ids_list && BLI_findindex(no_main_ids_list, id_resync_root) != -1) {
+ // CLOG_ERROR(
+ // &LOG,
+ // "While dealing with root '%s', resync root ID '%s' found to be alreaady
+ // resynced.\n", id_root->name, id_resync_root->name);
+ // }
+
+ ID *id_resync_root_reference = id_resync_root->override_library->reference;
+
+ if (id_resync_root_reference->tag & LIB_TAG_MISSING) {
+ BKE_reportf(
+ reports != NULL ? reports->reports : NULL,
+ RPT_ERROR,
+ "Impossible to resync data-block %s and its dependencies, as its linked reference "
+ "is missing",
+ id_root->name + 2);
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
+ BKE_main_relations_free(bmain);
+ lib_override_group_tag_data_clear(&data);
+ return false;
+ }
+
+ /* Tag local overrides of the current resync sub-hierarchy. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.id_root = id_resync_root;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
+
+ /* Tag reference data matching the current resync sub-hierarchy. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.id_root = id_resync_root->override_library->reference;
+ data.is_override = false;
+ lib_override_linked_group_tag(&data);
+
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* IDs that get fully removed from linked data remain as local overrides (using place-holder
+ * linked IDs as reference), but they are often not reachable from any current valid local
+ * override hierarchy anymore. This will ensure they get properly deleted at the end of this
+ * function. */
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ (id->override_library->reference->tag & LIB_TAG_MISSING) != 0 &&
+ /* Unfortunately deleting obdata means deleting their objects too. Since there is no
+ * guarantee that a valid override object using an obsolete override obdata gets properly
+ * updated, we ignore those here for now. In practice this should not be a big issue. */
+ !OB_DATA_SUPPORT_ID(GS(id->name))) {
+ id->tag |= LIB_TAG_MISSING;
}
- BLI_assert(GS(reference_id->name) == GS(id->name));
- if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
- BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- continue;
- }
- if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
- /* We have an override, but now it does not seem to be necessary to override that ID
- * anymore. Check if there are some actual overrides from the user, otherwise assume
- * that we can get rid of this local override. */
- LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
- if (!ELEM(op->rna_prop_type, PROP_POINTER, PROP_COLLECTION)) {
- id->override_library->reference->tag |= LIB_TAG_DOIT;
+ if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
+ /* While this should not happen in typical cases (and won't be properly supported here),
+ * user is free to do all kind of very bad things, including having different local
+ * overrides of a same linked ID in a same hierarchy. */
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
+ ID *reference_id = id_override_library->reference;
+ if (GS(reference_id->name) != GS(id->name)) {
+ switch (GS(id->name)) {
+ case ID_KE:
+ reference_id = (ID *)BKE_key_from_id(reference_id);
break;
- }
+ case ID_GR:
+ BLI_assert(GS(reference_id->name) == ID_SCE);
+ reference_id = (ID *)((Scene *)reference_id)->master_collection;
+ break;
+ case ID_NT:
+ reference_id = (ID *)ntreeFromID(id);
+ break;
+ default:
+ break;
+ }
+ }
+ BLI_assert(GS(reference_id->name) == GS(id->name));
- bool do_break = false;
- LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
- if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
+ BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+ if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
+ /* We have an override, but now it does not seem to be necessary to override that ID
+ * anymore. Check if there are some actual overrides from the user, otherwise assume
+ * that we can get rid of this local override. */
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ if (!ELEM(op->rna_prop_type, PROP_POINTER, PROP_COLLECTION)) {
id->override_library->reference->tag |= LIB_TAG_DOIT;
- do_break = true;
break;
}
- }
- if (do_break) {
- break;
+
+ bool do_break = false;
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ id->override_library->reference->tag |= LIB_TAG_DOIT;
+ do_break = true;
+ break;
+ }
+ }
+ if (do_break) {
+ break;
+ }
}
}
}
}
}
+ FOREACH_MAIN_ID_END;
+
+ /* Code above may have added some tags, we need to update this too. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
}
- FOREACH_MAIN_ID_END;
- /* Code above may have added some tags, we need to update this too. */
+ /* Tag all local overrides of the current hierarchy. */
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
- lib_override_hierarchy_dependencies_recursive_tag(&data);
+ data.id_root = id_root;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
@@ -1421,6 +1491,7 @@ static bool lib_override_library_resync(Main *bmain,
bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, true);
if (!success) {
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
return success;
}
@@ -1469,6 +1540,8 @@ static bool lib_override_library_resync(Main *bmain,
lib_override_library_property_copy(op_new, op_old);
}
}
+
+ BLI_addtail(no_main_ids_list, id_override_old);
}
else {
/* Add to proper main list, ensure unique name for local ID, sort, and clear relevant
@@ -1565,7 +1638,7 @@ static bool lib_override_library_resync(Main *bmain,
id->newid->tag &= ~LIB_TAG_DOIT;
id_override_old->tag |= LIB_TAG_DOIT;
if (id_override_old->tag & LIB_TAG_NO_MAIN) {
- BKE_id_free(bmain, id_override_old);
+ BLI_assert(BLI_findindex(no_main_ids_list, id_override_old) != -1);
}
}
}
@@ -1654,33 +1727,71 @@ bool BKE_lib_override_library_resync(Main *bmain,
const bool do_hierarchy_enforce,
BlendFileReadReport *reports)
{
+ ListBase no_main_ids_list = {NULL};
+ LinkNode id_resync_roots = {.link = id_root, .next = NULL};
+
const bool success = lib_override_library_resync(bmain,
scene,
view_layer,
id_root,
+ &id_resync_roots,
+ &no_main_ids_list,
override_resync_residual_storage,
do_hierarchy_enforce,
true,
reports);
+ LISTBASE_FOREACH_MUTABLE (ID *, id_iter, &no_main_ids_list) {
+ BKE_id_free(bmain, id_iter);
+ }
+
return success;
}
-/* Also tag ancestors overrides for resync.
+static bool lib_override_resync_id_lib_level_is_valid(ID *id,
+ const int library_indirect_level,
+ const bool do_strict_equal)
+{
+ const int id_lib_level = (ID_IS_LINKED(id) ? id->lib->temp_index : 0);
+ return do_strict_equal ? id_lib_level == library_indirect_level :
+ id_lib_level <= library_indirect_level;
+}
+
+/* Find the root of the override hierarchy the given `id` belongs to. */
+static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != NULL) {
+ id = id_type->owner_get(bmain, id);
+ }
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+ }
+
+ ID *hierarchy_root_id = id->override_library->hierarchy_root;
+ BLI_assert(hierarchy_root_id != NULL);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(hierarchy_root_id));
+ return hierarchy_root_id;
+}
+
+/* Check ancestors overrides for resync, to ensure all IDs in-between two tagged-for-resync ones
+ * are also properly tagged, and to identify the resync root of this subset of the whole hierarchy.
*
* WARNING: Expects `bmain` to have valid relation data.
*
- * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below.
+ * Returns `true` if it finds an ancestor within the current liboverride hierarchy also tagged as
+ * needing resync, `false` otherwise.
*
- * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to
- * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and
- * resync the whole hierarchy.
+ * NOTE: If `check_only` is true, it only does the check and returns, without any modification to
+ * the data. Used for debug/data validation purposes.
*/
-static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
- ID *id,
- const int library_indirect_level)
+static bool lib_override_resync_tagging_finalize_recurse(
+ Main *bmain, ID *id, GHash *id_roots, const int library_indirect_level, const bool check_only)
{
- if (id->lib != NULL && id->lib->temp_index > library_indirect_level) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+
+ if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, false) &&
+ !check_only) {
CLOG_ERROR(
&LOG,
"While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
@@ -1690,7 +1801,7 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
id->lib->filepath,
id->lib->temp_index);
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- return;
+ return false;
}
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
@@ -1698,59 +1809,103 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
/* This ID has already been processed. */
- return;
+ return (ID_IS_OVERRIDE_LIBRARY(id) && (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0);
}
+
/* This way we won't process again that ID, should we encounter it again through another
* relationship hierarchy. */
entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+ /* There can be dependency loops in relationships, e.g.:
+ * - Rig object uses armature ID.
+ * - Armature ID has some drivers using rig object as variable.
+ *
+ * In that case, if this function is initially called on the rig object (therefore tagged as
+ * needing resync), it will:
+ * - Process the rig object
+ * -- Recurse over the armature ID
+ * --- Recurse over the rig object again
+ * --- The rig object is detected as already processed and returns true.
+ * -- The armature has a tagged ancestor (the rig object), so it is not a resync root.
+ * - The rig object has a tagged ancestor (the armature), so it is not a resync root.
+ * ...and this ends up with no resync root.
+ *
+ * Removing the 'need resync' tag before doing the recursive calls allow to break this loop and
+ * results in actually getting a resync root, even though it may not be the 'logical' one.
+ *
+ * In the example above, the armature will become the resync root.
+ */
+ const int id_tag_need_resync = (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+
+ bool is_ancestor_tagged_for_resync = false;
for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL;
entry_item = entry_item->next) {
if (entry_item->usage_flag &
(IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) {
continue;
}
+
ID *id_from = entry_item->id_pointer.from;
- /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
- if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) {
- id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- CLOG_INFO(&LOG,
- 4,
- "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to "
- "be overridden",
- id_from->name,
- id_from->lib,
- id->name,
- id->lib);
- lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level);
+ /* Check if this ID has an override hierarchy ancestor already tagged for resync. */
+ if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib &&
+ id_from->override_library->hierarchy_root == id->override_library->hierarchy_root) {
+ const bool is_ancestor_tagged_for_resync_prev = is_ancestor_tagged_for_resync;
+ is_ancestor_tagged_for_resync |= lib_override_resync_tagging_finalize_recurse(
+ bmain, id_from, id_roots, library_indirect_level, check_only);
+
+ if (!check_only && is_ancestor_tagged_for_resync && !is_ancestor_tagged_for_resync_prev) {
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) now tagged as needing resync because they are used by %s (%p) "
+ "that needs to be resynced",
+ id->name,
+ id->lib,
+ id_from->name,
+ id_from->lib);
+ }
}
}
-}
-/* Ensures parent collection (or objects) in the same override group are also tagged for resync.
- *
- * This is needed since otherwise, some (new) ID added in one sub-collection might be used in
- * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those
- * sub-collections would be unaware that this is the same ID, and would re-generate several
- * overrides for it.
- *
- * NOTE: Related to `lib_override_resync_tagging_finalize` above.
- */
-static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
-{
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (id_type->owner_get != NULL) {
- id = id_type->owner_get(bmain, id);
+ /* Re-enable 'need resync' tag if needed. */
+ id->tag |= id_tag_need_resync;
+
+ if (check_only) {
+ return is_ancestor_tagged_for_resync;
+ }
+
+ if (is_ancestor_tagged_for_resync) {
+ /* If a tagged-for-resync ancestor was found, this id is not a resync sub-tree root, but it is
+ * part of one, and therefore needs to be tagged for resync too. */
+ id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ }
+ else if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) is tagged as needing resync, but none of its override hierarchy "
+ "ancestors are tagged for resync, so it is a partial resync root",
+ id->name,
+ id->lib);
+
+ /* If no tagged-for-resync ancestor was found, but this id is tagged for resync, then it is a
+ * root of a resync sub-tree. Find the root of the whole override hierarchy and add this ID as
+ * one of its resync sub-tree roots. */
+ ID *id_root = lib_override_library_main_resync_root_get(bmain, id);
+ BLI_assert(id_root->lib == id->lib);
+
+ CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name);
+
+ LinkNodePair **id_resync_roots_p;
+ if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) {
+ *id_resync_roots_p = MEM_callocN(sizeof(**id_resync_roots_p), __func__);
}
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+
+ BLI_linklist_append(*id_resync_roots_p, id);
+ is_ancestor_tagged_for_resync = true;
}
- ID *hierarchy_root_id = id->override_library->hierarchy_root;
- BLI_assert(hierarchy_root_id != NULL);
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(hierarchy_root_id));
- return hierarchy_root_id;
+ return is_ancestor_tagged_for_resync;
}
/* Ensure resync of all overrides at one level of indirect usage.
@@ -1810,6 +1965,8 @@ static void lib_override_library_main_resync_on_library_indirect_level(
FOREACH_MAIN_ID_END;
lib_override_group_tag_data_clear(&data);
+ GHash *id_roots = BLI_ghash_ptr_new(__func__);
+
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -1818,6 +1975,10 @@ static void lib_override_library_main_resync_on_library_indirect_level(
continue;
}
+ if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
+ continue;
+ }
+
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
BLI_assert((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
@@ -1826,7 +1987,8 @@ static void lib_override_library_main_resync_on_library_indirect_level(
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib);
- lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
+ lib_override_resync_tagging_finalize_recurse(
+ bmain, id, id_roots, library_indirect_level, false);
continue;
}
@@ -1851,80 +2013,112 @@ static void lib_override_library_main_resync_on_library_indirect_level(
id->lib,
id_to->name,
id_to->lib);
- lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
+ lib_override_resync_tagging_finalize_recurse(
+ bmain, id, id_roots, library_indirect_level, false);
break;
}
}
}
FOREACH_MAIN_ID_END;
+#ifndef NDEBUG
+ /* Check for validity/integrity of the computed set of root IDs, and their sub-branches defined
+ * by their resync root IDs. */
+ {
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots);
+ while (!BLI_ghashIterator_done(id_roots_iter)) {
+ ID *id_root = BLI_ghashIterator_getKey(id_roots_iter);
+ LinkNodePair *id_resync_roots = BLI_ghashIterator_getValue(id_roots_iter);
+ CLOG_INFO(
+ &LOG, 2, "Checking validity of computed TODO data for root '%s'... \n", id_root->name);
+ for (LinkNode *id_resync_root_iter = id_resync_roots->list; id_resync_root_iter != NULL;
+ id_resync_root_iter = id_resync_root_iter->next) {
+ ID *id_resync_root = id_resync_root_iter->link;
+ BLI_assert(id_resync_root == id_root || !BLI_ghash_haskey(id_roots, id_resync_root));
+ if (id_resync_root == id_root) {
+ BLI_assert(id_resync_root_iter == id_resync_roots->list &&
+ id_resync_root_iter == id_resync_roots->last_node);
+ }
+ BLI_assert(!lib_override_resync_tagging_finalize_recurse(
+ bmain, id_resync_root, id_roots, library_indirect_level, true));
+ }
+ BLI_ghashIterator_step(id_roots_iter);
+ }
+ BLI_ghashIterator_free(id_roots_iter);
+ }
+#endif
+
BKE_main_relations_free(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- /* And do the actual resync for all IDs detected as needing it.
- * NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use
- * `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
- bool do_continue = true;
- while (do_continue) {
- do_continue = false;
- ListBase *lb;
- FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
- FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
- (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) ||
- (!ID_IS_LINKED(id) && library_indirect_level != 0)) {
- continue;
- }
+ ListBase no_main_ids_list = {NULL};
- if (ID_IS_LINKED(id)) {
- id->lib->tag |= LIBRARY_TAG_RESYNC_REQUIRED;
- }
+ GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots);
+ while (!BLI_ghashIterator_done(id_roots_iter)) {
+ ID *id_root = BLI_ghashIterator_getKey(id_roots_iter);
+ Library *library = id_root->lib;
+ LinkNodePair *id_resync_roots = BLI_ghashIterator_getValue(id_roots_iter);
- /* We cannot resync a scene that is currently active. */
- if (id == &scene->id) {
- id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- BKE_reportf(reports->reports,
- RPT_WARNING,
- "Scene '%s' was not resynced as it is the currently active one",
- scene->id.name + 2);
- continue;
- }
+ if (ID_IS_LINKED(id_root)) {
+ id_root->lib->tag |= LIBRARY_TAG_RESYNC_REQUIRED;
+ }
- Library *library = id->lib;
-
- /* In complex non-supported cases, with several different override hierarchies sharing
- * relations between each-other, we may end up not actually updating/replacing the given
- * root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
- * project repository, r2687).
- * This can lead to infinite loop here, at least avoid this. */
- id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- id = lib_override_library_main_resync_root_get(bmain, id);
- id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
- BLI_assert(id->lib == library);
- do_continue = true;
-
- CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, library);
- const bool success = lib_override_library_resync(
- bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
- CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
- if (success) {
- reports->count.resynced_lib_overrides++;
- if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list &&
- BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) {
- BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library);
- reports->resynced_lib_overrides_libraries_count++;
- }
- }
- break;
- }
- FOREACH_MAIN_LISTBASE_ID_END;
- if (do_continue) {
- break;
+ CLOG_INFO(&LOG,
+ 2,
+ "Resyncing all dependencies under root %s (%p), first one being '%s'...",
+ id_root->name,
+ library,
+ ((ID *)id_resync_roots->list->link)->name);
+ const bool success = lib_override_library_resync(bmain,
+ scene,
+ view_layer,
+ id_root,
+ id_resync_roots->list,
+ &no_main_ids_list,
+ override_resync_residual_storage,
+ false,
+ false,
+ reports);
+ CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
+ if (success) {
+ reports->count.resynced_lib_overrides++;
+ if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list &&
+ BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) {
+ BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library);
+ reports->resynced_lib_overrides_libraries_count++;
}
}
- FOREACH_MAIN_LISTBASE_END;
+
+ BLI_linklist_free(id_resync_roots->list, NULL);
+ BLI_ghashIterator_step(id_roots_iter);
+ }
+ BLI_ghashIterator_free(id_roots_iter);
+
+ LISTBASE_FOREACH_MUTABLE (ID *, id_iter, &no_main_ids_list) {
+ BKE_id_free(bmain, id_iter);
}
+ BLI_listbase_clear(&no_main_ids_list);
+
+ /* Check there are no left-over IDs needing resync from the current (or higher) level of indirect
+ * library level. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ const bool is_valid_tagged_need_resync = ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
+ lib_override_resync_id_lib_level_is_valid(
+ id, library_indirect_level - 1, false));
+ if (!is_valid_tagged_need_resync) {
+ CLOG_ERROR(&LOG,
+ "ID override %s from library level %d still found as needing resync, when all "
+ "IDs from that level should have been processed after tackling library level %d",
+ id->name,
+ id->lib != NULL ? id->lib->temp_index : 0,
+ library_indirect_level);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BLI_ghash_free(id_roots, NULL, MEM_freeN);
if (do_reports_recursive_resync_timing) {
reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time;