LibOverride: add recursive resync.

Recursive resync means also resyncing overrides that are linked from
other library files into current working file.

Note that this allows to get 'working' files even when their
dependencies are out of sync. However, since linked data is never
written/saved, this has to be re-done every time the working file is
loaded, until said dependencies are updated properly.

NOTE: This is still missing the 'report' side of things, which is part
of a larger task to enhance reports regarding both linking, and
liboverrides (see T88393).

----------

Technical notes:

Implementing this proved to be slightly more challenging than expected,
mainly because one of the key aspects of the feature was never done in
Blender before: manipulating, re-creating linked data.

This ended up moving the whole resync code to use temp IDs out of bmain,
which is better in the long run anyway (and more aligned with what we
generally want to do when manipulating temp ID data). It should also
give a marginal improvement in performances for regular resync.

This commit also had to carefully 'sort' libraries by level of indirect
usage, as we want to resync first the libraries that are the least directly
used, i.e. libraries that are most used by other libraries.
This commit is contained in:
Bastien Montagne 2021-05-26 16:53:59 +02:00
parent 51ca9833d9
commit 72d660443b
3 changed files with 311 additions and 130 deletions

View File

@ -47,6 +47,7 @@ struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
struct IDOverrideLibraryPropertyOperation;
struct Library;
struct Main;
struct Object;
struct PointerRNA;
@ -68,7 +69,9 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id);
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
bool BKE_lib_override_library_create_from_tag(struct Main *bmain);
bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
const struct Library *reference_library,
const bool do_no_main);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,

View File

@ -48,6 +48,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@ -207,12 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
*override = NULL;
}
static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
static ID *lib_override_library_create_from(Main *bmain,
ID *reference_id,
const int lib_id_copy_flags)
{
/* Note: We do not want to copy possible override data from reference here (whether it is an
* override template, or already an override of some other ref data). */
ID *local_id = BKE_id_copy_ex(
bmain, reference_id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE);
ID *local_id = BKE_id_copy_ex(bmain,
reference_id,
NULL,
LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE |
lib_id_copy_flags);
if (local_id == NULL) {
return NULL;
@ -270,7 +276,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(reference_id->lib != NULL);
ID *local_id = lib_override_library_create_from(bmain, reference_id);
ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
if (do_tagged_remap) {
Key *reference_key, *local_key = NULL;
@ -315,9 +321,17 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
* \param reference_library the library from which the linked data being overridden come from
* (i.e. the library of the linked reference ID).
*
* \param do_no_main Create the new override data outside of Main database. Used for resyncing of
* linked overrides.
*
* \return \a true on success, \a false otherwise.
*/
bool BKE_lib_override_library_create_from_tag(Main *bmain)
bool BKE_lib_override_library_create_from_tag(Main *bmain,
const Library *reference_library,
const bool do_no_main)
{
ID *reference_id;
bool success = true;
@ -327,7 +341,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Get all IDs we want to override. */
FOREACH_MAIN_ID_BEGIN (bmain, reference_id) {
if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL &&
if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library &&
BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__);
todo_id_iter->data = reference_id;
@ -339,10 +353,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Override the IDs. */
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
/* If `newid` is already set, assume it has been handled by calling code.
* Only current use case: re-using proxy ID when converting to liboverride. */
if (reference_id->newid == NULL) {
/* If `newid` is already set, assume it has been handled by calling code.
* Only current use case: re-using proxy ID when converting to liboverride. */
if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) {
/* Note: `no main` case is used during resync procedure, to support recursive resync. This
* requires extra care further odwn the resync process, see
* `BKE_lib_override_library_resync`. */
reference_id->newid = lib_override_library_create_from(
bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
if (reference_id->newid == NULL) {
success = false;
break;
}
@ -382,23 +402,42 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Still checking the whole Main, that way we can tag other local IDs as needing to be
* remapped to use newly created overriding IDs, if needed. */
FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
/* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
* local IDs usages anyway. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
/* In case we created new overrides as 'no main', they are not accessible directly in this
* loop, but we can get to them through their reference's `newid` pointer. */
if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) {
other_id = id->newid;
/* Otherwise we cannot properly dinstinguish between IDs that are actually from the
* linked library (and should not be remapped), and IDs that are overrides re-generated
* from the reference from the linked library, and must therefore be remapped.
*
* This is reset afterwards at the end of this loop. */
other_id->lib = NULL;
}
else {
other_id = id;
}
/* If other ID is a linked one, but not from the same library as our reference, then we
* consider we should also remap it, as part of recursive resync. */
if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) {
BKE_libblock_relink_ex(bmain,
other_id,
reference_id,
local_id,
ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
if (reference_key != NULL) {
BKE_libblock_relink_ex(bmain,
other_id,
&reference_key->id,
&local_key->id,
ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
}
}
if (other_id != id) {
other_id->lib = reference_id->lib;
}
}
FOREACH_MAIN_ID_END;
}
@ -598,26 +637,22 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
if (ELEM(to_id, NULL, id_owner)) {
continue;
}
if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) {
if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
continue;
}
/* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case
* above). */
if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) {
Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
if (to_id_reference->lib != reference_lib) {
/* We do not override data-blocks from other libraries, nor do we process them. */
continue;
}
Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
if (to_id_reference->lib != reference_lib) {
/* We do not override data-blocks from other libraries, nor do we process them. */
continue;
}
if (to_id_reference->tag & LIB_TAG_MISSING) {
to_id->tag |= missing_tag;
}
else {
to_id->tag |= tag;
}
if (to_id_reference->tag & LIB_TAG_MISSING) {
to_id->tag |= missing_tag;
}
else {
to_id->tag |= tag;
}
/* Recursively process the dependencies. */
@ -631,7 +666,7 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
{
ID *id_root = data->id_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root));
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@ -656,7 +691,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
BKE_main_relations_free(bmain);
return BKE_lib_override_library_create_from_tag(bmain);
return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
@ -667,6 +702,9 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *residual_storage,
const bool is_resync)
{
/* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
* do not do anything about it. */
BKE_main_collection_sync(bmain);
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
@ -676,7 +714,7 @@ static void lib_override_library_create_post_process(Main *bmain,
/* Instantiating the root collection or object should never be needed in resync case, since the
* old override would be remapped to the new one. */
if (!is_resync && id_root != NULL && id_root->newid != NULL) {
if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
@ -721,56 +759,58 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
if (ob_new != NULL) {
BLI_assert(ob_new->id.override_library != NULL &&
ob_new->id.override_library->reference == &ob->id);
if (ob_new == NULL || ob_new->id.lib != NULL) {
continue;
}
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
if (id_root != NULL && default_instantiating_collection == NULL) {
ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
switch (GS(id_ref->name)) {
case ID_GR: {
/* Adding the object to a specific collection outside of the root overridden one is a
* fairly bad idea (it breaks the override hierarchy concept). But there is no other
* way to do this currently (we cannot add new collections to overridden root one,
* this is not currently supported).
* Since that will be fairly annoying and noisy, only do that in case the override
* object is not part of any existing collection (i.e. its user count is 0). In
* practice this should never happen I think. */
if (ID_REAL_USERS(ob_new) != 0) {
continue;
}
default_instantiating_collection = BKE_collection_add(
bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
/* Hide the collection from viewport and render. */
default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
COLLECTION_RESTRICT_RENDER;
break;
BLI_assert(ob_new->id.override_library != NULL &&
ob_new->id.override_library->reference == &ob->id);
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
if (id_root != NULL && default_instantiating_collection == NULL) {
ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
switch (GS(id_ref->name)) {
case ID_GR: {
/* Adding the object to a specific collection outside of the root overridden one is a
* fairly bad idea (it breaks the override hierarchy concept). But there is no other
* way to do this currently (we cannot add new collections to overridden root one,
* this is not currently supported).
* Since that will be fairly annoying and noisy, only do that in case the override
* object is not part of any existing collection (i.e. its user count is 0). In
* practice this should never happen I think. */
if (ID_REAL_USERS(ob_new) != 0) {
continue;
}
case ID_OB: {
/* Add the other objects to one of the collections instantiating the
* root object, or scene's master collection if none found. */
Object *ob_ref = (Object *)id_ref;
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (BKE_collection_has_object(collection, ob_ref) &&
BKE_view_layer_has_collection(view_layer, collection) &&
!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
default_instantiating_collection = collection;
}
}
break;
}
default:
BLI_assert(0);
default_instantiating_collection = BKE_collection_add(
bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
/* Hide the collection from viewport and render. */
default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
COLLECTION_RESTRICT_RENDER;
break;
}
case ID_OB: {
/* Add the other objects to one of the collections instantiating the
* root object, or scene's master collection if none found. */
Object *ob_ref = (Object *)id_ref;
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (BKE_collection_has_object(collection, ob_ref) &&
BKE_view_layer_has_collection(view_layer, collection) &&
!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
default_instantiating_collection = collection;
}
}
break;
}
default:
BLI_assert(0);
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
@ -890,7 +930,6 @@ bool BKE_lib_override_library_resync(Main *bmain,
ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
ID *id_root_reference = id_root->override_library->reference;
@ -924,12 +963,35 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag |= LIB_TAG_MISSING;
}
if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
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. */
if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) {
BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id);
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));
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
@ -968,7 +1030,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Note that this call also remaps all pointers of tagged IDs from old override IDs to new
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
const bool success = BKE_lib_override_library_create_from_tag(bmain);
const bool success = BKE_lib_override_library_create_from_tag(
bmain, id_root_reference->lib, true);
if (!success) {
return success;
@ -977,55 +1040,100 @@ bool BKE_lib_override_library_resync(Main *bmain,
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
/* We need to 'move back' newly created override into its proper library (since it was
* duplicated from the reference ID with 'no main' option, it should currently be the same
* as the reference ID one). */
BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib);
BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib);
id_override_new->lib = id_root->lib;
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf));
memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name));
memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name));
/* Note that this is a very efficient way to keep BMain IDs ordered as expected after
* swapping their names.
* However, one has to be very careful with this when iterating over the listbase at the
* same time. Here it works because we only execute this code when we are in the linked
* IDs, which are always *after* all local ones, and we only affect local IDs. */
BLI_listbase_swaplinks(lb, id_override_old, id_override_new);
/* Remap the whole local IDs to use the new override. */
BKE_libblock_remap(
bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE);
BLI_insertlinkreplace(lb, id_override_old, id_override_new);
id_override_old->tag |= LIB_TAG_NO_MAIN;
id_override_new->tag &= ~LIB_TAG_NO_MAIN;
/* Copy over overrides rules from old override ID to new one. */
BLI_duplicatelist(&id_override_new->override_library->properties,
&id_override_old->override_library->properties);
for (IDOverrideLibraryProperty *
op_new = id_override_new->override_library->properties.first,
*op_old = id_override_old->override_library->properties.first;
op_new;
op_new = op_new->next, op_old = op_old->next) {
lib_override_library_property_copy(op_new, op_old);
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
/* Copy over overrides rules from old override ID to new one. */
BLI_duplicatelist(&id_override_new->override_library->properties,
&id_override_old->override_library->properties);
IDOverrideLibraryProperty *op_new =
id_override_new->override_library->properties.first;
IDOverrideLibraryProperty *op_old =
id_override_old->override_library->properties.first;
for (; op_new; op_new = op_new->next, op_old = op_old->next) {
lib_override_library_property_copy(op_new, op_old);
}
}
}
else {
/* Add to proper main list, ensure unique name for local ID, sort, and clear relevant
* tags. */
BKE_libblock_management_main_add(bmain, id_override_new);
}
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
/* We need to apply override rules in a separate loop, after all ID pointers have been properly
* remapped, and all new local override IDs have gotten their proper original names, otherwise
* override operations based on those ID names would fail. */
/* We need to remap old to new override usages in a separate loop, after all new overrides have
* been added to Main. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old != NULL) {
/* Remap all IDs to use the new override. */
BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
/* 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 = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
BKE_libblock_relink_ex(bmain,
id_override_old_iter,
id_override_old,
id_override_new,
ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
}
}
}
}
}
FOREACH_MAIN_ID_END;
BKE_main_collection_sync(bmain);
/* We need to apply override rules in a separate loop, after all ID pointers have been properly
* remapped, and all new local override IDs have gotten their proper original names, otherwise
* override operations based on those ID names would fail. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
continue;
}
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old == NULL) {
continue;
}
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) {
/* Apply rules on new override ID using old one as 'source' data. */
/* Note that since we already remapped ID pointers in old override IDs to new ones, we
* can also apply ID pointer override rules safely here. */
@ -1059,30 +1167,44 @@ bool BKE_lib_override_library_resync(Main *bmain,
RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
RNA_OVERRIDE_APPLY_FLAG_NOP);
}
/* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages
* of the old one.
* This is necessary in case said old ID is not in Main anymore. */
BKE_libblock_relink_ex(bmain,
id_override_old,
NULL,
NULL,
ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT;
}
}
FOREACH_MAIN_ID_END;
/* Delete old override IDs.
* Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
* This improves performances anyway, so everything is fine. */
* Note that we have to use tagged group deletion here, since ID deletion also uses
* LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */
int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
/* Note that this works because linked IDs are always after local ones (including overrides),
* so we will only ever tag an old override ID after we have already checked it in this loop,
* hence we cannot untag it later. */
if (id->newid != NULL && ID_IS_LINKED(id)) {
/* Note that this works because linked IDs are always after local ones (including
* overrides), so we will only ever tag an old override ID after we have already checked it
* in this loop, hence we cannot untag it later. */
if (id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old != NULL) {
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);
}
}
}
id->tag &= ~LIB_TAG_DOIT;
}
/* Also deal with old overrides that went missing in new linked data. */
/* Also deal with old overrides that went missing in new linked data - only for real local
* overrides for now, not those who are linked. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
if (!BKE_lib_override_library_is_user_edited(id)) {
@ -1113,6 +1235,10 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
}
FOREACH_MAIN_ID_END;
/* Cleanup, many pointers in this GHash are already invalid now. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
BKE_id_multi_tagged_delete(bmain);
/* At this point, `id_root` has very likely been deleted, we need to update it to its new
@ -1145,14 +1271,56 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
/* Cleanup. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
BKE_main_id_clear_newpoins(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */
return success;
}
static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data)
{
ID *id_owner = cb_data->id_owner;
ID *id = *cb_data->id_pointer;
if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) {
const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0;
if (owner_library_indirect_level >= id->lib->temp_index) {
id->lib->temp_index = owner_library_indirect_level + 1;
*(bool *)cb_data->user_data = true;
}
}
return IDWALK_RET_NOP;
}
/** Define the `temp_index` of libraries from their highest level of indirect usage.
*
* E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of
* 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */
static int lib_override_libraries_index_define(Main *bmain)
{
LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
/* index 0 is reserved for local data. */
library->temp_index = 1;
}
bool do_continue = true;
while (do_continue) {
do_continue = false;
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
BKE_library_foreach_ID_link(
bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY);
}
FOREACH_MAIN_ID_END;
}
int library_indirect_level_max = 0;
LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
if (library->temp_index > library_indirect_level_max) {
library_indirect_level_max = library->temp_index;
}
}
return library_indirect_level_max;
}
/**
* Detect and handle required resync of overrides data, when relations between reference linked IDs
* have changed.
@ -1200,7 +1368,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
@ -1222,12 +1390,12 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* 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. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib);
continue;
}
@ -1242,14 +1410,16 @@ void BKE_lib_override_library_main_resync(Main *bmain,
ID *id_to = *entry_item->id_pointer.to;
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) {
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG,
3,
"ID %s now tagged as needing resync because they use linked %s that now needs "
"to be overridden",
"ID %s (%p) now tagged as needing resync because they use linked %s (%p) that "
"now needs to be overridden",
id->name,
id_to->name);
id->lib,
id_to->name,
id_to->lib);
break;
}
}
@ -1259,6 +1429,8 @@ void BKE_lib_override_library_main_resync(Main *bmain,
BKE_main_relations_free(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
int library_indirect_level = lib_override_libraries_index_define(bmain);
/* 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. */
@ -1268,13 +1440,12 @@ void BKE_lib_override_library_main_resync(Main *bmain,
do_continue = false;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
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;
}
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
if (ID_IS_LINKED(id)) {
continue;
}
do_continue = true;
/* In complex non-supported cases, with several different override hierarchies sharing
@ -1284,7 +1455,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* This can lead to infinite loop here, at least avoid this. */
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib);
const bool success = BKE_lib_override_library_resync(
bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
@ -1296,6 +1467,13 @@ void BKE_lib_override_library_main_resync(Main *bmain,
}
}
FOREACH_MAIN_LISTBASE_END;
if (!do_continue && library_indirect_level != 0) {
/* We are done with overrides from that level of indirect linking, we can keep going with
* those 'less' indirectly linked now. */
library_indirect_level--;
do_continue = true;
}
}
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */

View File

@ -366,7 +366,7 @@ typedef struct Library {
struct PackedFile *packedfile;
/* Temp data needed by read/write code. */
/* Temp data needed by read/write code, and liboverride recursive resync. */
int temp_index;
/** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */
short versionfile, subversionfile;