Performance: Remap multiple items in UI

During sprite fright loading of complex scenes would spend a long time in remapping ID's
The remapping process is done on a per ID instance that resulted in a very time consuming
process that goes over every possible ID reference to find out if it needs to be updated.

If there are N of references to ID blocks and there are M ID blocks that needed to be remapped
it would take N*M checks. These checks are scattered around the place and memory.
Each reference would only be updated at most once, but most of the time no update is needed at all.

Idea: By grouping the changes together will reduce the number of checks resulting in improved performance.
This would only require N checks. Additional benefits is improved data locality as data is only loaded once
in the L2 cache.

It has be implemented for the resyncing process and UI editors.
On an Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 16Gig the resyncing process went
from 170 seconds to 145 seconds (during hotspot recording).

After this patch has been applied we could add similar approach
to references (references between data blocks) and functionality (tagged deletion).
In my understanding this could reduce the resyncing process to less than a second.
Opening the village production file between 10 and 20 seconds.

Flame graphs showing that UI remapping isn't visible anymore (`WM_main_remap_editor_id_reference`)
* Master {F12769210 size=full}
* This patch {F12769211 size=full}

Reviewed By: mont29

Maniphest Tasks: T94185

Differential Revision: https://developer.blender.org/D13615
This commit is contained in:
Jeroen Bakker 2022-01-25 14:51:35 +01:00 committed by Jeroen Bakker
parent 3f42417cd4
commit 948211679f
Notes: blender-bot 2023-02-14 00:06:52 +01:00
Referenced by commit 460e0a1347, Revert "Performance: Remap multiple items in UI"
Referenced by issue #94185, Outliner ID remapping performance
26 changed files with 718 additions and 309 deletions

View File

@ -38,6 +38,9 @@
extern "C" {
#endif
struct ID;
struct IDRemapper;
/* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */
/* Also IDRemap->flag. */
@ -97,6 +100,19 @@ enum {
ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9,
};
/**
* Replace all references in given Main using the given \a mappings
*
* \note Is preferred over BKE_libblock_remap_locked due to performance.
*/
void BKE_libblock_remap_multiple_locked(struct Main *bmain,
const struct IDRemapper *mappings,
const short remap_flags);
void BKE_libblock_remap_multiple(struct Main *bmain,
const struct IDRemapper *mappings,
const short remap_flags);
/**
* Replace all references in given Main to \a old_id by \a new_id
* (if \a new_id is NULL, it unlinks \a old_id).
@ -146,12 +162,61 @@ void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, int remap_f
ATTR_NONNULL();
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);
typedef void (*BKE_library_remap_editor_id_reference_cb)(const struct IDRemapper *mappings);
void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func);
void BKE_library_callback_remap_editor_id_reference_set(
BKE_library_remap_editor_id_reference_cb func);
/* IDRemapper */
struct IDRemapper;
typedef enum IDRemapperApplyResult {
/** No remapping rules available for the source. */
ID_REMAP_RESULT_SOURCE_UNAVAILABLE,
/** Source isn't mappable (e.g. NULL). */
ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE,
/** Source has been remapped to a new pointer. */
ID_REMAP_RESULT_SOURCE_REMAPPED,
/** Source has been set to NULL. */
ID_REMAP_RESULT_SOURCE_UNASSIGNED,
} IDRemapperApplyResult;
typedef enum IDRemapperApplyOptions {
ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0),
ID_REMAP_APPLY_ENSURE_REAL = (1 << 1),
ID_REMAP_APPLY_DEFAULT = 0,
} IDRemapperApplyOptions;
typedef void (*IDRemapperIterFunction)(struct ID *old_id, struct ID *new_id, void *user_data);
/**
* Create a new ID Remapper.
*
* An ID remapper stores multiple remapping rules.
*/
struct IDRemapper *BKE_id_remapper_create(void);
void BKE_id_remapper_clear(struct IDRemapper *id_remapper);
bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper);
/** Free the given ID Remapper. */
void BKE_id_remapper_free(struct IDRemapper *id_remapper);
/** Add a new remapping. */
void BKE_id_remapper_add(struct IDRemapper *id_remapper, struct ID *old_id, struct ID *new_id);
/**
* Apply a remapping.
*
* Update the id pointer stored in the given r_id_ptr if a remapping rule exists.
*/
IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper,
struct ID **r_id_ptr,
IDRemapperApplyOptions options);
bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter);
void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
IDRemapperIterFunction func,
void *user_data);
#ifdef __cplusplus
}
#endif

View File

@ -38,6 +38,7 @@ struct BlendLibReader;
struct BlendWriter;
struct Header;
struct ID;
struct IDRemapper;
struct LibraryForeachIDData;
struct ListBase;
struct Menu;
@ -117,10 +118,7 @@ typedef struct SpaceType {
bContextDataCallback context;
/* Used when we want to replace an ID by another (or NULL). */
void (*id_remap)(struct ScrArea *area,
struct SpaceLink *sl,
struct ID *old_id,
struct ID *new_id);
void (*id_remap)(struct ScrArea *area, struct SpaceLink *sl, const struct IDRemapper *mappings);
int (*space_subtype_get)(struct ScrArea *area);
void (*space_subtype_set)(struct ScrArea *area, int value);

View File

@ -179,6 +179,7 @@ set(SRC
intern/lib_id.c
intern/lib_id_delete.c
intern/lib_id_eval.c
intern/lib_id_remapper.cc
intern/lib_override.c
intern/lib_query.c
intern/lib_remap.c
@ -823,6 +824,7 @@ if(WITH_GTESTS)
intern/idprop_serialize_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
intern/lib_id_remapper_test.cc
intern/lib_id_test.cc
intern/lib_remap_test.cc
intern/tracking_test.cc

View File

@ -154,7 +154,10 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
}
if (remap_editor_id_reference_cb) {
remap_editor_id_reference_cb(id, NULL);
struct IDRemapper *remapper = BKE_id_remapper_create();
BKE_id_remapper_add(remapper, id, NULL);
remap_editor_id_reference_cb(remapper);
BKE_id_remapper_free(remapper);
}
}
@ -292,32 +295,40 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
* Note that we go forward here, since we want to check dependencies before users
* (e.g. meshes before objects).
* Avoids to have to loop twice. */
struct IDRemapper *remapper = BKE_id_remapper_create();
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id, *id_next;
BKE_id_remapper_clear(remapper);
for (id = lb->first; id; id = id_next) {
id_next = id->next;
/* NOTE: in case we delete a library, we also delete all its datablocks! */
if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) {
id->tag |= tag;
/* Will tag 'never NULL' users of this ID too.
* Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
* (and proxy!) links, this can lead to nasty crashing here in second,
* actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
BKE_libblock_remap_locked(bmain,
id,
NULL,
(ID_REMAP_FLAG_NEVER_NULL_USAGE |
ID_REMAP_FORCE_NEVER_NULL_USAGE |
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
BKE_id_remapper_add(remapper, id, NULL);
}
}
if (BKE_id_remapper_is_empty(remapper)) {
continue;
}
/* Will tag 'never NULL' users of this ID too.
* Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
* (and proxy!) links, this can lead to nasty crashing here in second,
* actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
BKE_libblock_remap_multiple_locked(bmain,
remapper,
(ID_REMAP_FLAG_NEVER_NULL_USAGE |
ID_REMAP_FORCE_NEVER_NULL_USAGE |
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS));
}
BKE_id_remapper_free(remapper);
}
BKE_main_unlock(bmain);
/* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones,

View File

@ -0,0 +1,175 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2022 by Blender Foundation.
*/
#include "DNA_ID.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "MEM_guardedalloc.h"
#include "BLI_map.hh"
using IDTypeFilter = uint64_t;
namespace blender::bke::id::remapper {
struct IDRemapper {
private:
Map<ID *, ID *> mappings;
IDTypeFilter source_types = 0;
public:
void clear()
{
mappings.clear();
source_types = 0;
}
bool is_empty() const
{
return mappings.is_empty();
}
void add(ID *old_id, ID *new_id)
{
BLI_assert(old_id != nullptr);
BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name)));
mappings.add(old_id, new_id);
source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name));
}
bool contains_mappings_for_any(IDTypeFilter filter) const
{
return (source_types & filter) != 0;
}
IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options) const
{
BLI_assert(r_id_ptr != nullptr);
if (*r_id_ptr == nullptr) {
return ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE;
}
if (!mappings.contains(*r_id_ptr)) {
return ID_REMAP_RESULT_SOURCE_UNAVAILABLE;
}
if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
id_us_min(*r_id_ptr);
}
*r_id_ptr = mappings.lookup(*r_id_ptr);
if (*r_id_ptr == nullptr) {
return ID_REMAP_RESULT_SOURCE_UNASSIGNED;
}
if (options & ID_REMAP_APPLY_UPDATE_REFCOUNT) {
id_us_plus(*r_id_ptr);
}
if (options & ID_REMAP_APPLY_ENSURE_REAL) {
id_us_ensure_real(*r_id_ptr);
}
return ID_REMAP_RESULT_SOURCE_REMAPPED;
}
void iter(IDRemapperIterFunction func, void *user_data) const
{
for (auto item : mappings.items()) {
func(item.key, item.value, user_data);
}
}
};
} // namespace blender::bke::id::remapper
/** \brief wrap CPP IDRemapper to a C handle. */
static IDRemapper *wrap(blender::bke::id::remapper::IDRemapper *remapper)
{
return static_cast<IDRemapper *>(static_cast<void *>(remapper));
}
/** \brief wrap C handle to a CPP IDRemapper. */
static blender::bke::id::remapper::IDRemapper *unwrap(IDRemapper *remapper)
{
return static_cast<blender::bke::id::remapper::IDRemapper *>(static_cast<void *>(remapper));
}
/** \brief wrap C handle to a CPP IDRemapper. */
static const blender::bke::id::remapper::IDRemapper *unwrap(const IDRemapper *remapper)
{
return static_cast<const blender::bke::id::remapper::IDRemapper *>(
static_cast<const void *>(remapper));
}
extern "C" {
IDRemapper *BKE_id_remapper_create(void)
{
blender::bke::id::remapper::IDRemapper *remapper =
MEM_new<blender::bke::id::remapper::IDRemapper>(__func__);
return wrap(remapper);
}
void BKE_id_remapper_free(IDRemapper *id_remapper)
{
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
MEM_delete<blender::bke::id::remapper::IDRemapper>(remapper);
}
void BKE_id_remapper_clear(struct IDRemapper *id_remapper)
{
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
remapper->clear();
}
bool BKE_id_remapper_is_empty(const struct IDRemapper *id_remapper)
{
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
return remapper->is_empty();
}
void BKE_id_remapper_add(IDRemapper *id_remapper, ID *old_id, ID *new_id)
{
blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
remapper->add(old_id, new_id);
}
bool BKE_id_remapper_has_mapping_for(const struct IDRemapper *id_remapper, uint64_t type_filter)
{
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
return remapper->contains_mappings_for_any(type_filter);
}
IDRemapperApplyResult BKE_id_remapper_apply(const IDRemapper *id_remapper,
ID **r_id_ptr,
const IDRemapperApplyOptions options)
{
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
return remapper->apply(r_id_ptr, options);
}
void BKE_id_remapper_iter(const struct IDRemapper *id_remapper,
IDRemapperIterFunction func,
void *user_data)
{
const blender::bke::id::remapper::IDRemapper *remapper = unwrap(id_remapper);
remapper->iter(func, user_data);
}
}

View File

@ -0,0 +1,83 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2022 by Blender Foundation.
*/
#include "testing/testing.h"
#include "BKE_lib_remap.h"
#include "BLI_string.h"
#include "DNA_ID.h"
namespace blender::bke::id::remapper::tests {
TEST(lib_id_remapper, unavailable)
{
ID id1;
ID *idp = &id1;
IDRemapper *remapper = BKE_id_remapper_create();
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNAVAILABLE);
BKE_id_remapper_free(remapper);
}
TEST(lib_id_remapper, not_mappable)
{
ID *idp = nullptr;
IDRemapper *remapper = BKE_id_remapper_create();
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE);
BKE_id_remapper_free(remapper);
}
TEST(lib_id_remapper, mapped)
{
ID id1;
ID id2;
ID *idp = &id1;
BLI_strncpy(id1.name, "OB1", sizeof(id1.name));
BLI_strncpy(id2.name, "OB2", sizeof(id2.name));
IDRemapper *remapper = BKE_id_remapper_create();
BKE_id_remapper_add(remapper, &id1, &id2);
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_REMAPPED);
EXPECT_EQ(idp, &id2);
BKE_id_remapper_free(remapper);
}
TEST(lib_id_remapper, unassigned)
{
ID id1;
ID *idp = &id1;
IDRemapper *remapper = BKE_id_remapper_create();
BKE_id_remapper_add(remapper, &id1, nullptr);
IDRemapperApplyResult result = BKE_id_remapper_apply(remapper, &idp, ID_REMAP_APPLY_DEFAULT);
EXPECT_EQ(result, ID_REMAP_RESULT_SOURCE_UNASSIGNED);
EXPECT_EQ(idp, nullptr);
BKE_id_remapper_free(remapper);
}
} // namespace blender::bke::id::remapper::tests

View File

@ -1121,6 +1121,45 @@ void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadRepor
}
}
static void lib_override_library_remap(Main *bmain,
const ID *id_root_reference,
GHash *linkedref_to_old_override)
{
ID *id;
struct IDRemapper *remapper = BKE_id_remapper_create();
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;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old == NULL) {
continue;
}
BKE_id_remapper_add(remapper, id_override_old, id_override_new);
/* 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) == 0) {
continue;
}
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;
/* Remap all IDs to use the new override. */
BKE_libblock_remap_multiple(bmain, remapper, 0);
BKE_id_remapper_free(remapper);
}
static bool lib_override_library_resync(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@ -1312,32 +1351,9 @@ static bool lib_override_library_resync(Main *bmain,
}
FOREACH_MAIN_LISTBASE_END;
/* We need to remap old to new override usages in a separate loop, after all new overrides have
/* We 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->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;
lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override);
BKE_main_collection_sync(bmain);

View File

@ -510,11 +510,18 @@ static void libblock_remap_data(
#endif
}
void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
typedef struct LibblockRemapMultipleUserData {
Main *bmain;
short remap_flags;
} LibBlockRemapMultipleUserData;
static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_data)
{
LibBlockRemapMultipleUserData *data = user_data;
Main *bmain = data->bmain;
const short remap_flags = data->remap_flags;
IDRemap id_remap_data;
ID *old_id = old_idv;
ID *new_id = new_idv;
int skipped_direct, skipped_refcounted;
BLI_assert(old_id != NULL);
@ -527,13 +534,6 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
free_notifier_reference_cb(old_id);
}
/* We assume editors do not hold references to their IDs... This is false in some cases
* (Image is especially tricky here),
* editors' code is to handle refcount (id->us) itself then. */
if (remap_editor_id_reference_cb) {
remap_editor_id_reference_cb(old_id, new_id);
}
skipped_direct = id_remap_data.skipped_direct;
skipped_refcounted = id_remap_data.skipped_refcounted;
@ -606,6 +606,41 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
DEG_relations_tag_update(bmain);
}
void BKE_libblock_remap_multiple_locked(Main *bmain,
const struct IDRemapper *mappings,
const short remap_flags)
{
if (BKE_id_remapper_is_empty(mappings)) {
/* Early exit nothing to do. */
return;
}
LibBlockRemapMultipleUserData user_data;
user_data.bmain = bmain;
user_data.remap_flags = remap_flags;
BKE_id_remapper_iter(mappings, libblock_remap_foreach_idpair_cb, &user_data);
/* We assume editors do not hold references to their IDs... This is false in some cases
* (Image is especially tricky here),
* editors' code is to handle refcount (id->us) itself then. */
if (remap_editor_id_reference_cb) {
remap_editor_id_reference_cb(mappings);
}
/* Full rebuild of DEG! */
DEG_relations_tag_update(bmain);
}
void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
{
struct IDRemapper *remapper = BKE_id_remapper_create();
ID *old_id = old_idv;
ID *new_id = new_idv;
BKE_id_remapper_add(remapper, old_id, new_id);
BKE_libblock_remap_multiple_locked(bmain, remapper, remap_flags);
BKE_id_remapper_free(remapper);
}
void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags)
{
BKE_main_lock(bmain);
@ -615,6 +650,17 @@ void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short r
BKE_main_unlock(bmain);
}
void BKE_libblock_remap_multiple(Main *bmain,
const struct IDRemapper *mappings,
const short remap_flags)
{
BKE_main_lock(bmain);
BKE_libblock_remap_multiple_locked(bmain, mappings, remap_flags);
BKE_main_unlock(bmain);
}
void BKE_libblock_unlink(Main *bmain,
void *idv,
const bool do_flag_never_null,

View File

@ -33,6 +33,7 @@ extern "C" {
struct GPUBatch;
struct Main;
struct bContext;
struct IDRemapper;
/* ed_util.c */
@ -60,10 +61,13 @@ bool ED_editors_flush_edits(struct Main *bmain);
*
* \param new_id: may be NULL to unlink \a old_id.
*/
void ED_spacedata_id_remap_single(struct ScrArea *area,
struct SpaceLink *sl,
struct ID *old_id,
struct ID *new_id);
void ED_spacedata_id_remap(struct ScrArea *area,
struct SpaceLink *sl,
struct ID *old_id,
struct ID *new_id);
const struct IDRemapper *mappings);
void ED_operatortypes_edutils(void);

View File

@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_lib_remap.h"
#include "BKE_nla.h"
#include "BKE_screen.h"
@ -814,20 +815,15 @@ static void action_refresh(const bContext *C, ScrArea *area)
/* XXX re-sizing y-extents of tot should go here? */
}
static void action_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void action_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceAction *sact = (SpaceAction *)slink;
if ((ID *)sact->action == old_id) {
sact->action = (bAction *)new_id;
}
if ((ID *)sact->ads.filter_grp == old_id) {
sact->ads.filter_grp = (Collection *)new_id;
}
if ((ID *)sact->ads.source == old_id) {
sact->ads.source = new_id;
}
BKE_id_remapper_apply(mappings, (ID **)&sact->action, ID_REMAP_APPLY_DEFAULT);
BKE_id_remapper_apply(mappings, (ID **)&sact->ads.filter_grp, ID_REMAP_APPLY_DEFAULT);
BKE_id_remapper_apply(mappings, &sact->ads.source, ID_REMAP_APPLY_DEFAULT);
}
/**

View File

@ -32,6 +32,7 @@
#include "BKE_context.h"
#include "BKE_gpencil_modifier.h" /* Types for registering panels. */
#include "BKE_lib_remap.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_shader_fx.h"
@ -860,54 +861,53 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
}
}
static void buttons_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void buttons_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceProperties *sbuts = (SpaceProperties *)slink;
if (sbuts->pinid == old_id) {
sbuts->pinid = new_id;
if (new_id == NULL) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
if (BKE_id_remapper_apply(mappings, &sbuts->pinid, ID_REMAP_APPLY_DEFAULT) ==
ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
if (sbuts->path) {
ButsContextPath *path = sbuts->path;
for (int i = 0; i < path->len; i++) {
switch (BKE_id_remapper_apply(mappings, &path->ptr[i].owner_id, ID_REMAP_APPLY_DEFAULT)) {
case ID_REMAP_RESULT_SOURCE_UNASSIGNED: {
if (i == 0) {
MEM_SAFE_FREE(sbuts->path);
}
else {
memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
path->len = i;
}
break;
}
case ID_REMAP_RESULT_SOURCE_REMAPPED: {
RNA_id_pointer_create(path->ptr[i].owner_id, &path->ptr[i]);
/* There is no easy way to check/make path downwards valid, just nullify it.
* Next redraw will rebuild this anyway. */
i++;
memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
path->len = i;
break;
}
int i;
for (i = 0; i < path->len; i++) {
if (path->ptr[i].owner_id == old_id) {
break;
case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE:
case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: {
/* Nothing to do. */
break;
}
}
}
if (i == path->len) {
/* pass */
}
else if (new_id == NULL) {
if (i == 0) {
MEM_SAFE_FREE(sbuts->path);
}
else {
memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
path->len = i;
}
}
else {
RNA_id_pointer_create(new_id, &path->ptr[i]);
/* There is no easy way to check/make path downwards valid, just nullify it.
* Next redraw will rebuild this anyway. */
i++;
memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
path->len = i;
}
}
if (sbuts->texuser) {
ButsContextTexture *ct = sbuts->texuser;
if ((ID *)ct->texture == old_id) {
ct->texture = (Tex *)new_id;
}
BKE_id_remapper_apply(mappings, (ID **)&ct->texture, ID_REMAP_APPLY_DEFAULT);
BLI_freelistN(&ct->users);
ct->user = NULL;
}

View File

@ -39,6 +39,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_movieclip.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
@ -1317,23 +1318,18 @@ static void clip_properties_region_listener(const wmRegionListenerParams *params
/********************* registration ********************/
static void clip_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void clip_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceClip *sclip = (SpaceClip *)slink;
if (!ELEM(GS(old_id->name), ID_MC, ID_MSK)) {
if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_MC | FILTER_ID_MSK)) {
return;
}
if ((ID *)sclip->clip == old_id) {
sclip->clip = (MovieClip *)new_id;
id_us_ensure_real(new_id);
}
if ((ID *)sclip->mask_info.mask == old_id) {
sclip->mask_info.mask = (Mask *)new_id;
id_us_ensure_real(new_id);
}
BKE_id_remapper_apply(mappings, (ID **)&sclip->clip, ID_REMAP_APPLY_ENSURE_REAL);
BKE_id_remapper_apply(mappings, (ID **)&sclip->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL);
}
void ED_spacetype_clip(void)

View File

@ -33,6 +33,7 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_screen.h"
@ -989,7 +990,7 @@ static int /*eContextResult*/ file_context(const bContext *C,
return CTX_RESULT_MEMBER_NOT_FOUND;
}
static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id))
static void file_id_remap(ScrArea *area, SpaceLink *sl, const struct IDRemapper *UNUSED(mappings))
{
SpaceFile *sfile = (SpaceFile *)sl;

View File

@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_anim_api.h"
@ -796,18 +797,17 @@ static void graph_refresh(const bContext *C, ScrArea *area)
graph_refresh_fcurve_colors(C);
}
static void graph_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void graph_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceGraph *sgraph = (SpaceGraph *)slink;
if (sgraph->ads) {
if ((ID *)sgraph->ads->filter_grp == old_id) {
sgraph->ads->filter_grp = (Collection *)new_id;
}
if ((ID *)sgraph->ads->source == old_id) {
sgraph->ads->source = new_id;
}
if (!sgraph->ads) {
return;
}
BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->filter_grp, ID_REMAP_APPLY_DEFAULT);
BKE_id_remapper_apply(mappings, (ID **)&sgraph->ads->source, ID_REMAP_APPLY_DEFAULT);
}
static int graph_space_subtype_get(ScrArea *area)

View File

@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "RNA_access.h"
@ -983,29 +984,19 @@ static void image_header_region_listener(const wmRegionListenerParams *params)
}
}
static void image_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void image_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceImage *simg = (SpaceImage *)slink;
if (!ELEM(GS(old_id->name), ID_IM, ID_GD, ID_MSK)) {
if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_IM | FILTER_ID_GD | FILTER_ID_MSK)) {
return;
}
if ((ID *)simg->image == old_id) {
simg->image = (Image *)new_id;
id_us_ensure_real(new_id);
}
if ((ID *)simg->gpd == old_id) {
simg->gpd = (bGPdata *)new_id;
id_us_min(old_id);
id_us_plus(new_id);
}
if ((ID *)simg->mask_info.mask == old_id) {
simg->mask_info.mask = (Mask *)new_id;
id_us_ensure_real(new_id);
}
BKE_id_remapper_apply(mappings, (ID **)&simg->image, ID_REMAP_APPLY_ENSURE_REAL);
BKE_id_remapper_apply(mappings, (ID **)&simg->gpd, ID_REMAP_APPLY_UPDATE_REFCOUNT);
BKE_id_remapper_apply(mappings, (ID **)&simg->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL);
}
/**

View File

@ -33,6 +33,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_anim_api.h"
@ -577,18 +578,17 @@ static void nla_listener(const wmSpaceTypeListenerParams *params)
}
}
static void nla_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void nla_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceNla *snla = (SpaceNla *)slink;
if (snla->ads) {
if ((ID *)snla->ads->filter_grp == old_id) {
snla->ads->filter_grp = (Collection *)new_id;
}
if ((ID *)snla->ads->source == old_id) {
snla->ads->source = new_id;
}
if (snla->ads == NULL) {
return;
}
BKE_id_remapper_apply(mappings, (ID **)&snla->ads->filter_grp, ID_REMAP_APPLY_DEFAULT);
BKE_id_remapper_apply(mappings, (ID **)&snla->ads->source, ID_REMAP_APPLY_DEFAULT);
}
void ED_spacetype_nla(void)

View File

@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_node.h"
#include "BKE_screen.h"
@ -896,71 +897,63 @@ static void node_widgets()
WM_gizmogrouptype_append_and_link(gzmap_type, NODE_GGT_backdrop_corner_pin);
}
static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void node_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceNode *snode = (SpaceNode *)slink;
if (snode->id == old_id) {
if (ELEM(BKE_id_remapper_apply(mappings, &snode->id, ID_REMAP_APPLY_DEFAULT),
ID_REMAP_RESULT_SOURCE_REMAPPED,
ID_REMAP_RESULT_SOURCE_UNASSIGNED)) {
/* nasty DNA logic for SpaceNode:
* ideally should be handled by editor code, but would be bad level call
*/
BLI_freelistN(&snode->treepath);
/* XXX Untested in case new_id != nullptr... */
snode->id = new_id;
snode->from = nullptr;
snode->nodetree = nullptr;
snode->edittree = nullptr;
}
else if (GS(old_id->name) == ID_OB) {
if (snode->from == old_id) {
if (new_id == nullptr) {
snode->flag &= ~SNODE_PIN;
}
snode->from = new_id;
if (BKE_id_remapper_apply(mappings, &snode->from, ID_REMAP_APPLY_DEFAULT) ==
ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
snode->flag &= ~SNODE_PIN;
}
BKE_id_remapper_apply(mappings, (ID **)&snode->gpd, ID_REMAP_APPLY_UPDATE_REFCOUNT);
if (!BKE_id_remapper_has_mapping_for(mappings, FILTER_ID_NT)) {
return;
}
bNodeTreePath *path, *path_next;
for (path = (bNodeTreePath *)snode->treepath.first; path; path = path->next) {
BKE_id_remapper_apply(mappings, (ID **)&path->nodetree, ID_REMAP_APPLY_ENSURE_REAL);
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
snode->nodetree = path->nodetree;
}
if (path->nodetree == nullptr) {
break;
}
}
else if (GS(old_id->name) == ID_GD) {
if ((ID *)snode->gpd == old_id) {
snode->gpd = (bGPdata *)new_id;
id_us_min(old_id);
id_us_plus(new_id);
}
/* remaining path entries are invalid, remove */
for (; path; path = path_next) {
path_next = path->next;
BLI_remlink(&snode->treepath, path);
MEM_freeN(path);
}
else if (GS(old_id->name) == ID_NT) {
bNodeTreePath *path, *path_next;
for (path = (bNodeTreePath *)snode->treepath.first; path; path = path->next) {
if ((ID *)path->nodetree == old_id) {
path->nodetree = (bNodeTree *)new_id;
id_us_ensure_real(new_id);
}
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
snode->nodetree = path->nodetree;
}
if (path->nodetree == nullptr) {
break;
}
}
/* remaining path entries are invalid, remove */
for (; path; path = path_next) {
path_next = path->next;
BLI_remlink(&snode->treepath, path);
MEM_freeN(path);
}
/* edittree is just the last in the path,
* set this directly since the path may have been shortened above */
if (snode->treepath.last) {
path = (bNodeTreePath *)snode->treepath.last;
snode->edittree = path->nodetree;
}
else {
snode->edittree = nullptr;
}
/* edittree is just the last in the path,
* set this directly since the path may have been shortened above */
if (snode->treepath.last) {
path = (bNodeTreePath *)snode->treepath.last;
snode->edittree = path->nodetree;
}
else {
snode->edittree = nullptr;
}
}

View File

@ -31,6 +31,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_lib_remap.h"
#include "BKE_outliner_treehash.h"
#include "BKE_screen.h"
@ -405,45 +406,49 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl)
return (SpaceLink *)space_outliner_new;
}
static void outliner_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)slink;
/* Some early out checks. */
if (!TREESTORE_ID_TYPE(old_id)) {
return; /* ID type is not used by outliner. */
BKE_id_remapper_apply(mappings, (ID **)&space_outliner->search_tse.id, ID_REMAP_APPLY_DEFAULT);
if (!space_outliner->treestore) {
return;
}
if (space_outliner->search_tse.id == old_id) {
space_outliner->search_tse.id = new_id;
}
TreeStoreElem *tselem;
BLI_mempool_iter iter;
bool changed = false;
bool unassigned = false;
if (space_outliner->treestore) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
bool changed = false;
BLI_mempool_iternew(space_outliner->treestore, &iter);
while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
if (tselem->id == old_id) {
tselem->id = new_id;
BLI_mempool_iternew(space_outliner->treestore, &iter);
while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
switch (BKE_id_remapper_apply(mappings, &tselem->id, ID_REMAP_APPLY_DEFAULT)) {
case ID_REMAP_RESULT_SOURCE_REMAPPED:
changed = true;
}
break;
case ID_REMAP_RESULT_SOURCE_UNASSIGNED:
changed = true;
unassigned = true;
break;
case ID_REMAP_RESULT_SOURCE_UNAVAILABLE:
case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE:
break;
}
}
/* Note that the Outliner may not be the active editor of the area, and hence not initialized.
* So runtime data might not have been created yet. */
if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
/* rebuild hash table, because it depends on ids too */
/* postpone a full rebuild because this can be called many times on-free */
space_outliner->storeflag |= SO_TREESTORE_REBUILD;
/* Note that the Outliner may not be the active editor of the area, and hence not initialized.
* So runtime data might not have been created yet. */
if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
/* rebuild hash table, because it depends on ids too */
/* postpone a full rebuild because this can be called many times on-free */
space_outliner->storeflag |= SO_TREESTORE_REBUILD;
if (new_id == nullptr) {
/* Redraw is needed when removing data for multiple outlines show the same data.
* without this, the stale data won't get fully flushed when this outliner
* is not the active outliner the user is interacting with. See T85976. */
ED_area_tag_redraw(area);
}
if (unassigned) {
/* Redraw is needed when removing data for multiple outlines show the same data.
* without this, the stale data won't get fully flushed when this outliner
* is not the active outliner the user is interacting with. See T85976. */
ED_area_tag_redraw(area);
}
}
}

View File

@ -39,6 +39,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "BKE_sequencer_offscreen.h"
@ -988,19 +989,12 @@ static void sequencer_buttons_region_listener(const wmRegionListenerParams *para
}
}
static void sequencer_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void sequencer_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceSeq *sseq = (SpaceSeq *)slink;
if (!ELEM(GS(old_id->name), ID_GD)) {
return;
}
if ((ID *)sseq->gpd == old_id) {
sseq->gpd = (bGPdata *)new_id;
id_us_min(old_id);
id_us_plus(new_id);
}
BKE_id_remapper_apply(mappings, (ID **)&sseq->gpd, ID_REMAP_APPLY_DEFAULT);
}
/* ************************************* */

View File

@ -18,6 +18,7 @@
#include "BLI_listbase.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_screen.h"
@ -171,21 +172,23 @@ static void spreadsheet_keymap(wmKeyConfig *keyconf)
WM_keymap_ensure(keyconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
}
static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void spreadsheet_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const IDRemapper *mappings)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
if ((ID *)object_context->object == old_id) {
if (new_id && GS(new_id->name) == ID_OB) {
object_context->object = (Object *)new_id;
}
else {
object_context->object = nullptr;
}
}
if (context->type != SPREADSHEET_CONTEXT_OBJECT) {
continue;
}
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
if (object_context->object != nullptr && GS(object_context->object->id.name) != ID_OB) {
object_context->object = nullptr;
continue;
}
BKE_id_remapper_apply(mappings, ((ID **)&object_context->object), ID_REMAP_APPLY_DEFAULT);
}
}

View File

@ -32,6 +32,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
#include "ED_screen.h"
@ -401,18 +402,12 @@ static void text_properties_region_draw(const bContext *C, ARegion *region)
}
}
static void text_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
static void text_id_remap(ScrArea *UNUSED(area),
SpaceLink *slink,
const struct IDRemapper *mappings)
{
SpaceText *stext = (SpaceText *)slink;
if (!ELEM(GS(old_id->name), ID_TXT)) {
return;
}
if ((ID *)stext->text == old_id) {
stext->text = (Text *)new_id;
id_us_ensure_real(new_id);
}
BKE_id_remapper_apply(mappings, (ID **)&stext->text, ID_REMAP_APPLY_ENSURE_REAL);
}
/********************* registration ********************/

View File

@ -48,6 +48,7 @@
#include "BKE_idprop.h"
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_mesh.h"
@ -1813,50 +1814,54 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
return CTX_RESULT_MEMBER_NOT_FOUND;
}
static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings)
{
View3D *v3d;
ARegion *region;
bool is_local = false;
if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) ==
ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
/* Otherwise, bonename may remain valid...
* We could be smart and check this, too? */
v3d->ob_center_bone[0] = '\0';
}
}
if (!ELEM(GS(old_id->name), ID_OB, ID_MA, ID_IM, ID_MC)) {
static void view3d_id_remap_v3d(ScrArea *area,
SpaceLink *slink,
View3D *v3d,
const struct IDRemapper *mappings,
const bool is_local)
{
ARegion *region;
if (BKE_id_remapper_apply(mappings, (ID **)&v3d->camera, ID_REMAP_APPLY_DEFAULT) ==
ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
/* 3D view might be inactive, in that case needs to use slink->regionbase */
ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase :
&slink->regionbase;
for (region = regionbase->first; region; region = region->next) {
if (region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd :
region->regiondata;
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
rv3d->persp = RV3D_PERSP;
}
}
}
}
}
static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings)
{
if (!BKE_id_remapper_has_mapping_for(
mappings, FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_MC)) {
return;
}
for (v3d = (View3D *)slink; v3d; v3d = v3d->localvd, is_local = true) {
if ((ID *)v3d->camera == old_id) {
v3d->camera = (Object *)new_id;
if (!new_id) {
/* 3D view might be inactive, in that case needs to use slink->regionbase */
ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase :
&slink->regionbase;
for (region = regionbase->first; region; region = region->next) {
if (region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd :
region->regiondata;
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
rv3d->persp = RV3D_PERSP;
}
}
}
}
}
/* Values in local-view aren't used, see: T52663 */
if (is_local == false) {
if ((ID *)v3d->ob_center == old_id) {
v3d->ob_center = (Object *)new_id;
/* Otherwise, bonename may remain valid...
* We could be smart and check this, too? */
if (new_id == NULL) {
v3d->ob_center_bone[0] = '\0';
}
}
}
if (is_local) {
break;
}
View3D *view3d = (View3D *)slink;
view3d_id_remap_v3d(area, slink, view3d, mappings, false);
view3d_id_remap_v3d_ob_centers(view3d, mappings);
if (view3d->localvd != NULL) {
/* Object centers in local-view aren't used, see: T52663 */
view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true);
}
}

View File

@ -35,6 +35,7 @@
#include "BKE_collection.h"
#include "BKE_global.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_multires.h"
@ -434,11 +435,27 @@ void unpack_menu(bContext *C,
UI_popup_menu_end(C, pup);
}
void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, ID *old_id, ID *new_id)
void ED_spacedata_id_remap(struct ScrArea *area,
struct SpaceLink *sl,
const struct IDRemapper *mappings)
{
SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
if (st && st->id_remap) {
st->id_remap(area, sl, mappings);
}
}
void ED_spacedata_id_remap_single(struct ScrArea *area,
struct SpaceLink *sl,
ID *old_id,
ID *new_id)
{
SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
if (st && st->id_remap) {
st->id_remap(area, sl, old_id, new_id);
struct IDRemapper *mappings = BKE_id_remapper_create();
BKE_id_remapper_add(mappings, old_id, new_id);
st->id_remap(area, sl, mappings);
BKE_id_remapper_free(mappings);
}
}

View File

@ -47,6 +47,7 @@ struct GHashIterator;
struct GPUViewport;
struct ID;
struct IDProperty;
struct IDRemapper;
struct ImBuf;
struct ImageFormatData;
struct Main;
@ -471,7 +472,7 @@ void WM_main_add_notifier(unsigned int type, void *reference);
* Clear notifiers by reference, Used so listeners don't act on freed data.
*/
void WM_main_remove_notifier_reference(const void *reference);
void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id);
void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings);
/* reports */
/**

View File

@ -51,6 +51,7 @@
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@ -312,28 +313,39 @@ void WM_main_remove_notifier_reference(const void *reference)
}
}
void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
static void wm_main_remap_assetlist(ID *old_id, ID *new_id, void *UNUSED(user_data))
{
ED_assetlist_storage_id_remap(old_id, new_id);
}
static void wm_main_remap_msgbus_notify(ID *old_id, ID *new_id, void *user_data)
{
struct wmMsgBus *mbus = user_data;
if (new_id != NULL) {
WM_msg_id_update(mbus, old_id, new_id);
}
else {
WM_msg_id_remove(mbus, old_id);
}
}
void WM_main_remap_editor_id_reference(const struct IDRemapper *mappings)
{
Main *bmain = G_MAIN;
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
ED_spacedata_id_remap(area, sl, old_id, new_id);
ED_spacedata_id_remap(area, sl, mappings);
}
}
}
ED_assetlist_storage_id_remap(old_id, new_id);
BKE_id_remapper_iter(mappings, wm_main_remap_assetlist, NULL);
wmWindowManager *wm = bmain->wm.first;
if (wm && wm->message_bus) {
struct wmMsgBus *mbus = wm->message_bus;
if (new_id != NULL) {
WM_msg_id_update(mbus, old_id, new_id);
}
else {
WM_msg_id_remove(mbus, old_id);
}
BKE_id_remapper_iter(mappings, wm_main_remap_msgbus_notify, wm->message_bus);
}
}

View File

@ -252,7 +252,7 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove);
BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh);
BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference);
BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap);
BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap_single);
DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update);
ED_spacetypes_init();