BKE: Rework ID swap code to properly handle embedded ID pointers. #107044
|
@ -464,18 +464,27 @@ struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id)
|
||||||
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
||||||
* \note Most internal ID data itself is not swapped (only IDProperties are).
|
* \note Most internal ID data itself is not swapped (only IDProperties are).
|
||||||
*
|
*
|
||||||
* \param bmain: May be NULL, in which case there will be no remapping of internal pointers to
|
* \param bmain: May be NULL, in which case there is no guarantee that internal remapping of ID
|
||||||
* itself.
|
* pointers to themselves will be complete (reguarding depsgraph and/or runtime data updates).
|
||||||
|
* \param do_self_remap: Whether to remap internal pointers to itself or not.
|
||||||
|
* \param self_remap_flags: Flags controlling self remapping, see BKE_lib_remap.h.
|
||||||
*/
|
*/
|
||||||
void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b);
|
void BKE_lib_id_swap(struct Main *bmain,
|
||||||
|
struct ID *id_a,
|
||||||
|
struct ID *id_b,
|
||||||
|
const bool do_self_remap,
|
||||||
|
const int self_remap_flags);
|
||||||
/**
|
/**
|
||||||
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
||||||
* \note All internal ID data itself is also swapped.
|
* \note All internal ID data itself is also swapped.
|
||||||
*
|
*
|
||||||
* \param bmain: May be NULL, in which case there will be no remapping of internal pointers to
|
* For parameters description, see #BKE_lib_id_swap above.
|
||||||
* itself.
|
|
||||||
*/
|
*/
|
||||||
void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);
|
void BKE_lib_id_swap_full(struct Main *bmain,
|
||||||
|
struct ID *id_a,
|
||||||
|
struct ID *id_b,
|
||||||
|
const bool do_self_remap,
|
||||||
|
const int self_remap_flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort given \a id into given \a lb list, using case-insensitive comparison of the id names.
|
* Sort given \a id into given \a lb list, using case-insensitive comparison of the id names.
|
||||||
|
|
|
@ -416,7 +416,7 @@ static int brush_undo_preserve_cb(LibraryIDLinkCallbackData *cb_data)
|
||||||
static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old)
|
static void brush_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old)
|
||||||
{
|
{
|
||||||
/* Whole Brush is preserved across undo-steps. */
|
/* Whole Brush is preserved across undo-steps. */
|
||||||
BKE_lib_id_swap(nullptr, id_new, id_old);
|
BKE_lib_id_swap(nullptr, id_new, id_old, false, 0);
|
||||||
|
|
||||||
/* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid.
|
/* `id_new` now has content from `id_old`, we need to ensure those old ID pointers are valid.
|
||||||
* NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */
|
* NOTE: Since we want to re-use all old pointers here, code is much simpler than for Scene. */
|
||||||
|
|
|
@ -765,14 +765,38 @@ ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id)
|
||||||
return newid;
|
return newid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void id_embedded_swap(ID **embedded_id_a,
|
||||||
|
ID **embedded_id_b,
|
||||||
|
const bool do_full_id,
|
||||||
|
struct IDRemapper *remapper_id_a,
|
||||||
|
struct IDRemapper *remapper_id_b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
* Does a mere memory swap over the whole IDs data (including type-specific memory).
|
||||||
* \note Most internal ID data itself is not swapped (only IDProperties are).
|
* \note Most internal ID data itself is not swapped (only IDProperties are).
|
||||||
*/
|
*/
|
||||||
static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id)
|
static void id_swap(Main *bmain,
|
||||||
|
ID *id_a,
|
||||||
|
ID *id_b,
|
||||||
|
const bool do_full_id,
|
||||||
|
const bool do_self_remap,
|
||||||
|
struct IDRemapper *input_remapper_id_a,
|
||||||
|
struct IDRemapper *input_remapper_id_b,
|
||||||
|
const int self_remap_flags)
|
||||||
{
|
{
|
||||||
BLI_assert(GS(id_a->name) == GS(id_b->name));
|
BLI_assert(GS(id_a->name) == GS(id_b->name));
|
||||||
|
|
||||||
|
struct IDRemapper *remapper_id_a = input_remapper_id_a;
|
||||||
|
struct IDRemapper *remapper_id_b = input_remapper_id_b;
|
||||||
|
if (do_self_remap) {
|
||||||
|
if (remapper_id_a == NULL) {
|
||||||
|
remapper_id_a = BKE_id_remapper_create();
|
||||||
|
}
|
||||||
|
if (remapper_id_b == NULL) {
|
||||||
|
remapper_id_b = BKE_id_remapper_create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id_a);
|
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id_a);
|
||||||
BLI_assert(id_type != NULL);
|
BLI_assert(id_type != NULL);
|
||||||
const size_t id_struct_size = id_type->struct_size;
|
const size_t id_struct_size = id_type->struct_size;
|
||||||
|
@ -799,21 +823,87 @@ static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id)
|
||||||
id_b->recalc = id_a_back.recalc;
|
id_b->recalc = id_a_back.recalc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bmain != NULL) {
|
id_embedded_swap((ID **)BKE_ntree_ptr_from_id(id_a),
|
||||||
/* Swap will have broken internal references to itself, restore them. */
|
(ID **)BKE_ntree_ptr_from_id(id_b),
|
||||||
BKE_libblock_relink_ex(bmain, id_a, id_b, id_a, ID_REMAP_SKIP_NEVER_NULL_USAGE);
|
do_full_id,
|
||||||
BKE_libblock_relink_ex(bmain, id_b, id_a, id_b, ID_REMAP_SKIP_NEVER_NULL_USAGE);
|
remapper_id_a,
|
||||||
|
remapper_id_b);
|
||||||
|
if (GS(id_a->name) == ID_SCE) {
|
||||||
|
Scene *scene_a = (Scene *)id_a;
|
||||||
|
Scene *scene_b = (Scene *)id_b;
|
||||||
|
id_embedded_swap((ID **)&scene_a->master_collection,
|
||||||
|
(ID **)&scene_b->master_collection,
|
||||||
|
do_full_id,
|
||||||
|
remapper_id_a,
|
||||||
|
remapper_id_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remapper_id_a != NULL) {
|
||||||
|
BKE_id_remapper_add(remapper_id_a, id_b, id_a);
|
||||||
|
}
|
||||||
|
if (remapper_id_b != NULL) {
|
||||||
|
BKE_id_remapper_add(remapper_id_b, id_a, id_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize remapping of internal referrences to self broken by swapping, if requested. */
|
||||||
|
if (do_self_remap) {
|
||||||
|
LinkNode ids = {.next = NULL, .link = id_a};
|
||||||
|
BKE_libblock_relink_multiple(
|
||||||
|
bmain, &ids, ID_REMAP_TYPE_REMAP, remapper_id_a, self_remap_flags);
|
||||||
|
ids.link = id_b;
|
||||||
|
BKE_libblock_relink_multiple(
|
||||||
|
bmain, &ids, ID_REMAP_TYPE_REMAP, remapper_id_b, self_remap_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_remapper_id_a == NULL && remapper_id_a != NULL) {
|
||||||
|
BKE_id_remapper_free(remapper_id_a);
|
||||||
|
}
|
||||||
|
if (input_remapper_id_b == NULL && remapper_id_b != NULL) {
|
||||||
|
BKE_id_remapper_free(remapper_id_b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_lib_id_swap(Main *bmain, ID *id_a, ID *id_b)
|
/* Conceptually, embedded IDs are part of their owner's data. However, some parts of the code
|
||||||
|
* (like e.g. the depsgraph) may treat them as independant IDs, so swapping them here and
|
||||||
|
* switching their pointers in the owner IDs allows to help not break cached relationships and
|
||||||
|
* such (by preserving the pointer values). */
|
||||||
|
static void id_embedded_swap(ID **embedded_id_a,
|
||||||
|
ID **embedded_id_b,
|
||||||
|
const bool do_full_id,
|
||||||
|
struct IDRemapper *remapper_id_a,
|
||||||
|
struct IDRemapper *remapper_id_b)
|
||||||
{
|
{
|
||||||
id_swap(bmain, id_a, id_b, false);
|
if (embedded_id_a != NULL && *embedded_id_a != NULL) {
|
||||||
|
BLI_assert(embedded_id_b != NULL && *embedded_id_b != NULL);
|
||||||
|
|
||||||
|
/* Do not remap internal references to itself here, since embedded IDs pointers also need to be
|
||||||
|
* potentially remapped in owner ID's data, which will also handle embedded IDs data. */
|
||||||
|
id_swap(
|
||||||
|
NULL, *embedded_id_a, *embedded_id_b, do_full_id, false, remapper_id_a, remapper_id_b, 0);
|
||||||
|
/* Manual 'remap' of owning embedded pointer in owner ID. */
|
||||||
|
SWAP(ID *, *embedded_id_a, *embedded_id_b);
|
||||||
|
|
||||||
|
/* Restore internal pointers to the swapped embedded IDs in their owners' data. This also
|
||||||
|
* includes the potential self-references inside the embedded IDs themselves. */
|
||||||
|
if (remapper_id_a != NULL) {
|
||||||
|
BKE_id_remapper_add(remapper_id_a, *embedded_id_b, *embedded_id_a);
|
||||||
|
}
|
||||||
|
if (remapper_id_b != NULL) {
|
||||||
|
BKE_id_remapper_add(remapper_id_b, *embedded_id_a, *embedded_id_b);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BKE_lib_id_swap_full(Main *bmain, ID *id_a, ID *id_b)
|
void BKE_lib_id_swap(
|
||||||
|
Main *bmain, ID *id_a, ID *id_b, const bool do_self_remap, const int self_remap_flags)
|
||||||
{
|
{
|
||||||
id_swap(bmain, id_a, id_b, true);
|
id_swap(bmain, id_a, id_b, false, do_self_remap, NULL, NULL, self_remap_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_lib_id_swap_full(
|
||||||
|
Main *bmain, ID *id_a, ID *id_b, const bool do_self_remap, const int self_remap_flags)
|
||||||
|
{
|
||||||
|
id_swap(bmain, id_a, id_b, true, do_self_remap, NULL, NULL, self_remap_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop)
|
bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop)
|
||||||
|
|
|
@ -3784,7 +3784,7 @@ void BKE_lib_override_library_main_unused_cleanup(Main *bmain)
|
||||||
|
|
||||||
static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
|
static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
|
||||||
{
|
{
|
||||||
BKE_lib_id_swap(bmain, id_local, id_temp);
|
BKE_lib_id_swap(bmain, id_local, id_temp, true, 0);
|
||||||
/* We need to keep these tags from temp ID into orig one.
|
/* We need to keep these tags from temp ID into orig one.
|
||||||
* ID swap does not swap most of ID data itself. */
|
* ID swap does not swap most of ID data itself. */
|
||||||
id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
|
id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
|
||||||
|
|
|
@ -130,7 +130,7 @@ static void palette_undo_preserve(BlendLibReader * /*reader*/, ID *id_new, ID *i
|
||||||
/* NOTE: We do not care about potential internal references to self here, Palette has none. */
|
/* NOTE: We do not care about potential internal references to self here, Palette has none. */
|
||||||
/* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be
|
/* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be
|
||||||
* fairly delicate. */
|
* fairly delicate. */
|
||||||
BKE_lib_id_swap(nullptr, id_new, id_old);
|
BKE_lib_id_swap(nullptr, id_new, id_old, false, 0);
|
||||||
std::swap(id_new->properties, id_old->properties);
|
std::swap(id_new->properties, id_old->properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
#include "BKE_lib_override.h"
|
#include "BKE_lib_override.h"
|
||||||
#include "BKE_lib_query.h"
|
#include "BKE_lib_query.h"
|
||||||
|
#include "BKE_lib_remap.h"
|
||||||
#include "BKE_main.h" /* for Main */
|
#include "BKE_main.h" /* for Main */
|
||||||
#include "BKE_main_idmap.h"
|
#include "BKE_main_idmap.h"
|
||||||
#include "BKE_material.h"
|
#include "BKE_material.h"
|
||||||
|
@ -3095,10 +3096,17 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
|
||||||
BLI_remlink(old_lb, id_old);
|
BLI_remlink(old_lb, id_old);
|
||||||
BLI_remlink(new_lb, id);
|
BLI_remlink(new_lb, id);
|
||||||
|
|
||||||
/* We do not need any remapping from this call here, since no ID pointer is valid in the data
|
/* We do need remapping of internal pointers to the ID itself here.
|
||||||
* currently (they are all pointing to old addresses, and need to go through `lib_link`
|
*
|
||||||
* process). So we can pass nullptr for the Main pointer parameter. */
|
* Passing a NULL BMain means that not all potential runtime data (like collections' parent
|
||||||
BKE_lib_id_swap_full(nullptr, id, id_old);
|
* pointers etc.) will be up-to-date. However, this should not be a problem here, since these
|
||||||
|
* data are re-generated later in fileread process anyway.. */
|
||||||
|
BKE_lib_id_swap_full(nullptr,
|
||||||
|
id,
|
||||||
|
id_old,
|
||||||
|
true,
|
||||||
|
ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_UPDATE_TAGGING |
|
||||||
|
ID_REMAP_SKIP_USER_REFCOUNT);
|
||||||
|
|
||||||
/* Special temporary usage of this pointer, necessary for the `undo_preserve` call after
|
/* Special temporary usage of this pointer, necessary for the `undo_preserve` call after
|
||||||
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of
|
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of
|
||||||
|
|
Loading…
Reference in New Issue