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
This commit is contained in:
@@ -49,8 +49,11 @@ enum {
|
||||
* appended.
|
||||
* NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */
|
||||
IDTYPE_FLAGS_ONLY_APPEND = 1 << 2,
|
||||
/** Allow to re-use an existing local ID with matching weak library reference instead of creating
|
||||
* a new copy of it, when appending. See also #LibraryWeakReference in `DNA_ID.h`. */
|
||||
IDTYPE_FLAGS_APPEND_IS_REUSABLE = 1 << 3,
|
||||
/** Indicates that the given IDType does not have animation data. */
|
||||
IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3,
|
||||
IDTYPE_FLAGS_NO_ANIMDATA = 1 << 4,
|
||||
};
|
||||
|
||||
typedef struct IDCacheKey {
|
||||
@@ -290,6 +293,7 @@ bool BKE_idtype_idcode_is_valid(const short idcode);
|
||||
|
||||
bool BKE_idtype_idcode_is_linkable(const short idcode);
|
||||
bool BKE_idtype_idcode_is_only_appendable(const short idcode);
|
||||
bool BKE_idtype_idcode_append_is_reusable(const short idcode);
|
||||
/* Macro currently, since any linkable IDtype should be localizable. */
|
||||
#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable
|
||||
|
||||
|
||||
@@ -212,6 +212,32 @@ void BKE_main_relations_tag_set(struct Main *bmain,
|
||||
|
||||
struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
|
||||
|
||||
/*
|
||||
* Temporary runtime API to allow re-using local (already appended) IDs instead of appending a new
|
||||
* copy again.
|
||||
*/
|
||||
|
||||
struct GHash *BKE_main_library_weak_reference_create(struct Main *bmain) ATTR_NONNULL();
|
||||
void BKE_main_library_weak_reference_destroy(struct GHash *library_weak_reference_mapping)
|
||||
ATTR_NONNULL();
|
||||
struct ID *BKE_main_library_weak_reference_search_item(
|
||||
struct GHash *library_weak_reference_mapping,
|
||||
const char *library_filepath,
|
||||
const char *library_id_name) ATTR_NONNULL();
|
||||
void BKE_main_library_weak_reference_add_item(struct GHash *library_weak_reference_mapping,
|
||||
const char *library_filepath,
|
||||
const char *library_id_name,
|
||||
struct ID *new_id) ATTR_NONNULL();
|
||||
void BKE_main_library_weak_reference_update_item(struct GHash *library_weak_reference_mapping,
|
||||
const char *library_filepath,
|
||||
const char *library_id_name,
|
||||
struct ID *old_id,
|
||||
struct ID *new_id) ATTR_NONNULL();
|
||||
void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_reference_mapping,
|
||||
const char *library_filepath,
|
||||
const char *library_id_name,
|
||||
struct ID *old_id) ATTR_NONNULL();
|
||||
|
||||
/* *** Generic utils to loop over whole Main database. *** */
|
||||
|
||||
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \
|
||||
|
||||
@@ -315,7 +315,7 @@ IDTypeInfo IDType_ID_AR = {
|
||||
.name = "Armature",
|
||||
.name_plural = "armatures",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_ARMATURE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = armature_init_data,
|
||||
.copy_data = armature_copy_data,
|
||||
|
||||
@@ -586,6 +586,11 @@ void BKE_bpath_traverse_id(
|
||||
return;
|
||||
}
|
||||
|
||||
if (id->library_weak_reference != NULL) {
|
||||
rewrite_path_fixed(
|
||||
id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data);
|
||||
}
|
||||
|
||||
switch (GS(id->name)) {
|
||||
case ID_IM: {
|
||||
Image *ima;
|
||||
|
||||
@@ -133,7 +133,7 @@ IDTypeInfo IDType_ID_CF = {
|
||||
.name = "CacheFile",
|
||||
.name_plural = "cache_files",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_CACHEFILE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = cache_file_init_data,
|
||||
.copy_data = cache_file_copy_data,
|
||||
|
||||
@@ -182,7 +182,7 @@ IDTypeInfo IDType_ID_CA = {
|
||||
.name = "Camera",
|
||||
.name_plural = "cameras",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_CAMERA,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = camera_init_data,
|
||||
.copy_data = camera_copy_data,
|
||||
|
||||
@@ -311,7 +311,7 @@ IDTypeInfo IDType_ID_CU = {
|
||||
.name = "Curve",
|
||||
.name_plural = "curves",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_CURVE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = curve_init_data,
|
||||
.copy_data = curve_copy_data,
|
||||
|
||||
@@ -160,7 +160,7 @@ IDTypeInfo IDType_ID_VF = {
|
||||
.name = "Font",
|
||||
.name_plural = "fonts",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_VFONT,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = vfont_init_data,
|
||||
.copy_data = vfont_copy_data,
|
||||
|
||||
@@ -181,7 +181,7 @@ IDTypeInfo IDType_ID_HA = {
|
||||
.name = "Hair",
|
||||
.name_plural = "hairs",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_HAIR,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = hair_init_data,
|
||||
.copy_data = hair_copy_data,
|
||||
|
||||
@@ -254,6 +254,24 @@ bool BKE_idtype_idcode_is_only_appendable(const short idcode)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an ID type can try to reuse and existing matching local one when being appended again.
|
||||
*
|
||||
* \param idcode: The IDType code to check.
|
||||
* \return Boolean, false when it cannot be re-used, true otherwise.
|
||||
*/
|
||||
bool BKE_idtype_idcode_append_is_reusable(const short idcode)
|
||||
{
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode);
|
||||
BLI_assert(id_type != NULL);
|
||||
if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_APPEND_IS_REUSABLE) != 0) {
|
||||
/* All appendable ID types should also always be linkable. */
|
||||
BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB).
|
||||
*/
|
||||
|
||||
@@ -345,7 +345,7 @@ IDTypeInfo IDType_ID_IM = {
|
||||
.name = "Image",
|
||||
.name_plural = "images",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_IMAGE,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = image_init_data,
|
||||
.copy_data = image_copy_data,
|
||||
|
||||
@@ -196,7 +196,7 @@ IDTypeInfo IDType_ID_LT = {
|
||||
.name = "Lattice",
|
||||
.name_plural = "lattices",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_LATTICE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = lattice_init_data,
|
||||
.copy_data = lattice_copy_data,
|
||||
|
||||
@@ -1301,6 +1301,9 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
|
||||
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
|
||||
}
|
||||
|
||||
/* This is never duplicated, only one existing ID should have a given weak ref to library/ID. */
|
||||
new_id->library_weak_reference = NULL;
|
||||
|
||||
if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) {
|
||||
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
||||
/* We do not want to copy existing override rules here, as they would break the proper
|
||||
@@ -2440,6 +2443,10 @@ void BKE_id_blend_write(BlendWriter *writer, ID *id)
|
||||
BKE_asset_metadata_write(writer, id->asset_data);
|
||||
}
|
||||
|
||||
if (id->library_weak_reference != NULL) {
|
||||
BLO_write_struct(writer, LibraryWeakReference, id->library_weak_reference);
|
||||
}
|
||||
|
||||
/* ID_WM's id->properties are considered runtime only, and never written in .blend file. */
|
||||
if (id->properties && !ELEM(GS(id->name), ID_WM)) {
|
||||
IDP_BlendWrite(writer, id->properties);
|
||||
|
||||
@@ -69,6 +69,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user)
|
||||
BKE_asset_metadata_free(&id->asset_data);
|
||||
}
|
||||
|
||||
if (id->library_weak_reference != NULL) {
|
||||
MEM_freeN(id->library_weak_reference);
|
||||
}
|
||||
|
||||
BKE_animdata_free(id, do_id_user);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ IDTypeInfo IDType_ID_LA = {
|
||||
.name = "Light",
|
||||
.name_plural = "lights",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_LIGHT,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = light_init_data,
|
||||
.copy_data = light_copy_data,
|
||||
|
||||
@@ -91,7 +91,7 @@ IDTypeInfo IDType_ID_LP = {
|
||||
.name = "LightProbe",
|
||||
.name_plural = "lightprobes",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_LIGHTPROBE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = lightprobe_init_data,
|
||||
.copy_data = NULL,
|
||||
|
||||
@@ -751,7 +751,7 @@ IDTypeInfo IDType_ID_LS = {
|
||||
.name = "FreestyleLineStyle",
|
||||
.name_plural = "linestyles",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = linestyle_init_data,
|
||||
.copy_data = linestyle_copy_data,
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -254,7 +254,7 @@ IDTypeInfo IDType_ID_MSK = {
|
||||
.name = "Mask",
|
||||
.name_plural = "masks",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_MASK,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = mask_copy_data,
|
||||
|
||||
@@ -259,7 +259,7 @@ IDTypeInfo IDType_ID_MA = {
|
||||
.name = "Material",
|
||||
.name_plural = "materials",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_MATERIAL,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = material_init_data,
|
||||
.copy_data = material_copy_data,
|
||||
|
||||
@@ -188,7 +188,7 @@ IDTypeInfo IDType_ID_MB = {
|
||||
.name = "Metaball",
|
||||
.name_plural = "metaballs",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_METABALL,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = metaball_init_data,
|
||||
.copy_data = metaball_copy_data,
|
||||
|
||||
@@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_ME = {
|
||||
.name = "Mesh",
|
||||
.name_plural = "meshes",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_MESH,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = mesh_init_data,
|
||||
.copy_data = mesh_copy_data,
|
||||
|
||||
@@ -346,7 +346,7 @@ IDTypeInfo IDType_ID_MC = {
|
||||
.name = "MovieClip",
|
||||
.name_plural = "movieclips",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_MOVIECLIP,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = movie_clip_init_data,
|
||||
.copy_data = movie_clip_copy_data,
|
||||
|
||||
@@ -992,7 +992,7 @@ IDTypeInfo IDType_ID_NT = {
|
||||
/* name */ "NodeTree",
|
||||
/* name_plural */ "node_groups",
|
||||
/* translation_context */ BLT_I18NCONTEXT_ID_NODETREE,
|
||||
/* flags */ 0,
|
||||
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
/* init_data */ ntree_init_data,
|
||||
/* copy_data */ ntree_copy_data,
|
||||
|
||||
@@ -174,7 +174,7 @@ IDTypeInfo IDType_ID_PT = {
|
||||
/* name */ "PointCloud",
|
||||
/* name_plural */ "pointclouds",
|
||||
/* translation_context */ BLT_I18NCONTEXT_ID_POINTCLOUD,
|
||||
/* flags */ 0,
|
||||
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
/* init_data */ pointcloud_init_data,
|
||||
/* copy_data */ pointcloud_copy_data,
|
||||
|
||||
@@ -152,7 +152,7 @@ IDTypeInfo IDType_ID_SIM = {
|
||||
/* name */ "Simulation",
|
||||
/* name_plural */ "simulations",
|
||||
/* translation_context */ BLT_I18NCONTEXT_ID_SIMULATION,
|
||||
/* flags */ 0,
|
||||
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
/* init_data */ simulation_init_data,
|
||||
/* copy_data */ simulation_copy_data,
|
||||
|
||||
@@ -204,7 +204,7 @@ IDTypeInfo IDType_ID_SO = {
|
||||
.name = "Sound",
|
||||
.name_plural = "sounds",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_SOUND,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
/* A fuzzy case, think NULLified content is OK here... */
|
||||
.init_data = NULL,
|
||||
|
||||
@@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_SPK = {
|
||||
.name = "Speaker",
|
||||
.name_plural = "speakers",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_SPEAKER,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = speaker_init_data,
|
||||
.copy_data = NULL,
|
||||
|
||||
@@ -241,7 +241,7 @@ IDTypeInfo IDType_ID_TXT = {
|
||||
.name = "Text",
|
||||
.name_plural = "texts",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_TEXT,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = text_init_data,
|
||||
.copy_data = text_copy_data,
|
||||
|
||||
@@ -210,7 +210,7 @@ IDTypeInfo IDType_ID_TE = {
|
||||
.name = "Texture",
|
||||
.name_plural = "textures",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_TEXTURE,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = texture_init_data,
|
||||
.copy_data = texture_copy_data,
|
||||
|
||||
@@ -642,7 +642,7 @@ IDTypeInfo IDType_ID_VO = {
|
||||
/* name */ "Volume",
|
||||
/* name_plural */ "volumes",
|
||||
/* translation_context */ BLT_I18NCONTEXT_ID_VOLUME,
|
||||
/* flags */ 0,
|
||||
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
/* init_data */ volume_init_data,
|
||||
/* copy_data */ volume_copy_data,
|
||||
|
||||
@@ -190,7 +190,7 @@ IDTypeInfo IDType_ID_WO = {
|
||||
.name = "World",
|
||||
.name_plural = "worlds",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_WORLD,
|
||||
.flags = 0,
|
||||
.flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
||||
|
||||
.init_data = world_init_data,
|
||||
.copy_data = world_copy_data,
|
||||
|
||||
@@ -2195,6 +2195,13 @@ static void direct_link_id_common(
|
||||
/* Initialize with provided tag. */
|
||||
id->tag = tag;
|
||||
|
||||
if (ID_IS_LINKED(id)) {
|
||||
id->library_weak_reference = NULL;
|
||||
}
|
||||
else {
|
||||
BLO_read_data_address(reader, &id->library_weak_reference);
|
||||
}
|
||||
|
||||
if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
/* For placeholder we only need to set the tag and properly initialize generic ID fields above,
|
||||
* no further data to read. */
|
||||
|
||||
@@ -392,7 +392,14 @@ typedef struct ID {
|
||||
* that references this ID (the bones of an armature or the modifiers of an object for e.g.).
|
||||
*/
|
||||
void *py_instance;
|
||||
void *_pad1;
|
||||
|
||||
/**
|
||||
* Weak reference to an ID in a given library file, used to allow re-using already appended data
|
||||
* in some cases, instead of appending it again.
|
||||
*
|
||||
* May be NULL.
|
||||
*/
|
||||
struct LibraryWeakReference *library_weak_reference;
|
||||
} ID;
|
||||
|
||||
/**
|
||||
@@ -426,6 +433,26 @@ typedef struct Library {
|
||||
short versionfile, subversionfile;
|
||||
} Library;
|
||||
|
||||
/**
|
||||
* A weak library/ID reference for local data that has been appended, to allow re-using that local
|
||||
* data instead of creating a new copy of it in future appends.
|
||||
*
|
||||
* NOTE: This is by design a week reference, in other words code should be totally fine and perform
|
||||
* a regular append if it cannot find a valid matching local ID.
|
||||
*
|
||||
* NOTE: There should always be only one single ID in current Main matching a given linked
|
||||
* reference.
|
||||
*/
|
||||
typedef struct LibraryWeakReference {
|
||||
/** Expected to match a `Library.filepath`. */
|
||||
char library_filepath[1024];
|
||||
|
||||
/** MAX_ID_NAME. May be different from the current local ID name. */
|
||||
char library_id_name[66];
|
||||
|
||||
char _pad[2];
|
||||
} LibraryWeakReference;
|
||||
|
||||
/* for PreviewImage->flag */
|
||||
enum ePreviewImage_Flag {
|
||||
PRV_CHANGED = (1 << 0),
|
||||
|
||||
@@ -1936,6 +1936,15 @@ static void rna_def_ID(BlenderRNA *brna)
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
|
||||
RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from");
|
||||
|
||||
prop = RNA_def_pointer(srna,
|
||||
"library_weak_reference",
|
||||
"LibraryWeakReference",
|
||||
"Library Weak Reference",
|
||||
"Weak reference to a data-block in another library .blend file (used to "
|
||||
"re-use already appended data instead of appending new copies)");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
|
||||
|
||||
prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
|
||||
@@ -2143,6 +2152,31 @@ static void rna_def_library(BlenderRNA *brna)
|
||||
RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks");
|
||||
}
|
||||
|
||||
static void rna_def_library_weak_reference(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "LibraryWeakReference", NULL);
|
||||
RNA_def_struct_ui_text(
|
||||
srna,
|
||||
"LibraryWeakReference",
|
||||
"Read-only external reference to a linked data-block and its library file");
|
||||
|
||||
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
|
||||
RNA_def_property_string_sdna(prop, NULL, "library_filepath");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "File Path", "Path to the library .blend file");
|
||||
|
||||
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_FILEPATH);
|
||||
RNA_def_property_string_sdna(prop, NULL, "library_id_name");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"ID name",
|
||||
"Full ID name in the library .blend file (including the two leading 'id type' chars)");
|
||||
}
|
||||
|
||||
/**
|
||||
* \attention This is separate from the above. It allows for RNA functions to
|
||||
* return an IDProperty *. See MovieClip.metadata for a usage example.
|
||||
@@ -2175,6 +2209,7 @@ void RNA_def_ID(BlenderRNA *brna)
|
||||
rna_def_ID_properties(brna);
|
||||
rna_def_ID_materials(brna);
|
||||
rna_def_library(brna);
|
||||
rna_def_library_weak_reference(brna);
|
||||
rna_def_idproperty_wrap_ptr(brna);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,9 @@ typedef struct WMLinkAppendData {
|
||||
/** Allows to easily find an existing items from an ID pointer. Used by append code. */
|
||||
GHash *new_id_to_item;
|
||||
|
||||
/** Runtime info used by append code to manage re-use of already appended matching IDs. */
|
||||
GHash *library_weak_reference_mapping;
|
||||
|
||||
/* Internal 'private' data */
|
||||
MemArena *memarena;
|
||||
} WMLinkAppendData;
|
||||
@@ -230,6 +233,8 @@ static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
|
||||
BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL);
|
||||
}
|
||||
|
||||
BLI_assert(lapp_data->library_weak_reference_mapping == NULL);
|
||||
|
||||
BLI_memarena_free(lapp_data->memarena);
|
||||
}
|
||||
|
||||
@@ -637,6 +642,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
BLI_ghash_insert(lapp_data->new_id_to_item, id, item);
|
||||
}
|
||||
|
||||
const bool do_reuse_existing_id = false;
|
||||
lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain);
|
||||
|
||||
/* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect
|
||||
* dependencies), this list will grow and we will process those IDs later, leading to a flatten
|
||||
* recursive processing of all the linked dependencies. */
|
||||
@@ -650,7 +658,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
|
||||
/* Clear tag previously used to mark IDs needing post-processing (instantiation of loose
|
||||
* objects etc.). */
|
||||
id->tag &= ~LIB_TAG_DOIT;
|
||||
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
|
||||
|
||||
ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ?
|
||||
BKE_main_library_weak_reference_search_item(
|
||||
lapp_data->library_weak_reference_mapping,
|
||||
id->lib->filepath,
|
||||
id->name) :
|
||||
NULL;
|
||||
|
||||
if (item->append_action != WM_APPEND_ACT_UNSET) {
|
||||
/* Already set, pass. */
|
||||
@@ -659,6 +674,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
|
||||
item->append_action = WM_APPEND_ACT_KEEP_LINKED;
|
||||
}
|
||||
/* Only re-use existing local ID for indirectly linked data, the ID explicitely selected by the
|
||||
* user we always fully append. */
|
||||
else if (do_reuse_existing_id && existing_local_id != NULL &&
|
||||
(item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) {
|
||||
CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name);
|
||||
item->append_action = WM_APPEND_ACT_REUSE_LOCAL;
|
||||
item->customdata = existing_local_id;
|
||||
}
|
||||
else if (id->tag & LIB_TAG_PRE_EXISTING) {
|
||||
CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name);
|
||||
item->append_action = WM_APPEND_ACT_COPY_LOCAL;
|
||||
@@ -678,6 +701,16 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
BKE_library_foreach_ID_link(
|
||||
bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP);
|
||||
}
|
||||
|
||||
/* If we found a matching existing local id but are not re-using it, we need to properly clear
|
||||
* its weak reference to linked data. */
|
||||
if (existing_local_id != NULL &&
|
||||
!ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) {
|
||||
BKE_main_library_weak_reference_remove_item(lapp_data->library_weak_reference_mapping,
|
||||
id->lib->filepath,
|
||||
id->name,
|
||||
existing_local_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Effectively perform required operation on every linked ID. */
|
||||
@@ -689,6 +722,11 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
}
|
||||
|
||||
ID *local_appended_new_id = NULL;
|
||||
char lib_filepath[FILE_MAX];
|
||||
BLI_strncpy(lib_filepath, id->lib->filepath, sizeof(lib_filepath));
|
||||
char lib_id_name[MAX_ID_NAME];
|
||||
BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name));
|
||||
|
||||
switch (item->append_action) {
|
||||
case WM_APPEND_ACT_COPY_LOCAL: {
|
||||
BKE_lib_id_make_local(
|
||||
@@ -721,6 +759,13 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
}
|
||||
|
||||
if (local_appended_new_id != NULL) {
|
||||
if (BKE_idtype_idcode_append_is_reusable(GS(local_appended_new_id->name))) {
|
||||
BKE_main_library_weak_reference_add_item(lapp_data->library_weak_reference_mapping,
|
||||
lib_filepath,
|
||||
lib_id_name,
|
||||
local_appended_new_id);
|
||||
}
|
||||
|
||||
if (GS(local_appended_new_id->name) == ID_OB) {
|
||||
BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id);
|
||||
}
|
||||
@@ -733,6 +778,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
}
|
||||
}
|
||||
|
||||
BKE_main_library_weak_reference_destroy(lapp_data->library_weak_reference_mapping);
|
||||
lapp_data->library_weak_reference_mapping = NULL;
|
||||
|
||||
/* Remap IDs as needed. */
|
||||
for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
|
||||
WMLinkAppendDataItem *item = itemlink->link;
|
||||
@@ -745,7 +793,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
|
||||
if (id == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) {
|
||||
if (ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_REUSE_LOCAL)) {
|
||||
BLI_assert(ID_IS_LINKED(id));
|
||||
id = id->newid;
|
||||
if (id == NULL) {
|
||||
|
||||
Reference in New Issue
Block a user