From 794c2828af60af02a38381c2a9a81f9046381074 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 17 Sep 2021 16:22:29 +0200 Subject: Initial implementation of local ID re-use when appending. This commit adds to ID struct a new optional 'weak reference' to a linked ID (in the form of a blend file library path and full ID name). This can then be used on next append to try to find a matching local ID instead of re-making the linked data local again. Ref. T90545 NOTE: ID re-use will be disabled for regular append for the time being (3.0 release), and only used for assets. Therefore, this commit should not change anything user-wise. Differential Revision: https://developer.blender.org/D12545 --- source/blender/blenkernel/intern/main.c | 202 ++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) (limited to 'source/blender/blenkernel/intern/main.c') diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 981f1d4a623..26dcadcc77b 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -35,6 +35,7 @@ #include "DNA_ID.h" #include "BKE_global.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -358,6 +359,207 @@ GSet *BKE_main_gset_create(Main *bmain, GSet *gset) return gset; } +/* Utils for ID's library weak reference API. */ +typedef struct LibWeakRefKey { + char filepath[FILE_MAX]; + char id_name[MAX_ID_NAME]; +} LibWeakRefKey; + +static LibWeakRefKey *lib_weak_key_create(LibWeakRefKey *key, + const char *lib_path, + const char *id_name) +{ + if (key == NULL) { + key = MEM_mallocN(sizeof(*key), __func__); + } + BLI_strncpy(key->filepath, lib_path, sizeof(key->filepath)); + BLI_strncpy(key->id_name, id_name, sizeof(key->id_name)); + return key; +} + +static uint lib_weak_key_hash(const void *ptr) +{ + const LibWeakRefKey *string_pair = ptr; + uint hash = BLI_ghashutil_strhash_p_murmur(string_pair->filepath); + return hash ^ BLI_ghashutil_strhash_p_murmur(string_pair->id_name); +} + +static bool lib_weak_key_cmp(const void *a, const void *b) +{ + const LibWeakRefKey *string_pair_a = a; + const LibWeakRefKey *string_pair_b = b; + + return !(STREQ(string_pair_a->filepath, string_pair_b->filepath) && + STREQ(string_pair_a->id_name, string_pair_b->id_name)); +} + +/** + * Generate a mapping between 'library path' of an ID (as a pair (relative blend file path, id + * name)), and a current local ID, if any. + * + * This uses the information stored in `ID.library_weak_reference`. + */ +GHash *BKE_main_library_weak_reference_create(Main *bmain) +{ + GHash *library_weak_reference_mapping = BLI_ghash_new( + lib_weak_key_hash, lib_weak_key_cmp, __func__); + + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ID *id_iter = lb->first; + if (id_iter == NULL) { + continue; + } + if (!BKE_idtype_idcode_append_is_reusable(GS(id_iter->name))) { + continue; + } + BLI_assert(BKE_idtype_idcode_is_linkable(GS(id_iter->name))); + + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) { + if (id_iter->library_weak_reference == NULL) { + continue; + } + LibWeakRefKey *key = lib_weak_key_create(NULL, + id_iter->library_weak_reference->library_filepath, + id_iter->library_weak_reference->library_id_name); + BLI_ghash_insert(library_weak_reference_mapping, key, id_iter); + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + + return library_weak_reference_mapping; +} + +/** + * Destroy the data generated by #BKE_main_library_weak_reference_create. + */ +void BKE_main_library_weak_reference_destroy(GHash *library_weak_reference_mapping) +{ + BLI_ghash_free(library_weak_reference_mapping, MEM_freeN, NULL); +} + +/** + * Search for a local ID matching the given linked ID reference. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID + * type. + */ +ID *BKE_main_library_weak_reference_search_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name) +{ + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + return (ID *)BLI_ghash_lookup(library_weak_reference_mapping, &key); +} + +/** + * Add the given ID weak library reference to given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name))); + + new_id->library_weak_reference = MEM_mallocN(sizeof(*(new_id->library_weak_reference)), + __func__); + + LibWeakRefKey *key = lib_weak_key_create(NULL, library_filepath, library_id_name); + void **id_p; + const bool already_exist_in_mapping = BLI_ghash_ensure_p( + library_weak_reference_mapping, key, &id_p); + BLI_assert(!already_exist_in_mapping); + + BLI_strncpy(new_id->library_weak_reference->library_filepath, + library_filepath, + sizeof(new_id->library_weak_reference->library_filepath)); + BLI_strncpy(new_id->library_weak_reference->library_id_name, + library_id_name, + sizeof(new_id->library_weak_reference->library_id_name)); + *id_p = new_id; +} + +/** + * Update the status of the given ID weak library reference in current local IDs and the runtime + * mapping. + * + * This effectively transfers the 'ownership' of the given weak reference from `old_id` to + * `new_id`. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_update_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(STREQ(old_id->library_weak_reference->library_filepath, library_filepath)); + BLI_assert(STREQ(old_id->library_weak_reference->library_id_name, library_id_name)); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + void **id_p = BLI_ghash_lookup_p(library_weak_reference_mapping, &key); + BLI_assert(id_p != NULL && *id_p == old_id); + + new_id->library_weak_reference = old_id->library_weak_reference; + old_id->library_weak_reference = NULL; + *id_p = new_id; +} + +/** + * Remove the given ID weak library reference from the given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_remove_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + + BLI_assert(BLI_ghash_lookup(library_weak_reference_mapping, &key) == old_id); + BLI_ghash_remove(library_weak_reference_mapping, &key, MEM_freeN, NULL); + + MEM_SAFE_FREE(old_id->library_weak_reference); +} + /** * Generates a raw .blend file thumbnail data from given image. * -- cgit v1.2.3