readfile: optimization for undo

Was using O(n^2) lookup on ID's with undo.

This caused undo to hang with 1000's of data-blocks
(especially with heavy scenes & outliner-space, which doesn't even need to be visible to cause a slow-down).

Internally this uses a ghash per id-type, which is lazy-initialized.
Each key uses the name and library since there may be name collisions between libraries.

Developer Notes:

- Adds small `BKE_main_idmap_*` API.
- Needed to change linking order for this to build.
This commit is contained in:
Campbell Barton 2016-06-07 16:07:13 +10:00
parent 3054e33d67
commit 441a440cbb
5 changed files with 324 additions and 62 deletions

View File

@ -552,11 +552,11 @@ function(SETUP_BLENDER_SORTED_LIBS)
bf_modifiers
bf_bmesh
bf_gpu
bf_blenloader
bf_blenkernel
bf_physics
bf_nodes
bf_rna
bf_blenloader
bf_imbuf
bf_blenlib
bf_depsgraph

View File

@ -0,0 +1,50 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BKE_LIBRARY_IDMAP_H__
#define __BKE_LIBRARY_IDMAP_H__
/** \file BKE_library_idmap.h
* \ingroup bke
*/
#include "BLI_compiler_attrs.h"
struct ID;
struct Main;
struct IDNameLib_Map;
struct IDNameLib_Map *BKE_main_idmap_create(
struct Main *bmain)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
void BKE_main_idmap_destroy(
struct IDNameLib_Map *id_typemap)
ATTR_NONNULL();
struct Main *BKE_main_idmap_main_get(
struct IDNameLib_Map *id_typemap)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
struct ID *BKE_main_idmap_lookup(
struct IDNameLib_Map *id_typemap,
short id_type, const char *name, const struct Library *lib)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 3);
struct ID *BKE_main_idmap_lookup_id(
struct IDNameLib_Map *id_typemap, const struct ID *id)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2);
#endif /* __BKE_LIBRARY_IDMAP_H__ */

View File

@ -121,6 +121,7 @@ set(SRC
intern/lamp.c
intern/lattice.c
intern/library.c
intern/library_idmap.c
intern/library_query.c
intern/linestyle.c
intern/mask.c
@ -244,6 +245,7 @@ set(SRC
BKE_lamp.h
BKE_lattice.h
BKE_library.h
BKE_library_idmap.h
BKE_library_query.h
BKE_linestyle.h
BKE_main.h

View File

@ -0,0 +1,174 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <string.h>
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "DNA_ID.h"
#include "BKE_idcode.h"
#include "BKE_library.h"
#include "BKE_library_idmap.h" /* own include */
/** \file blender/blenkernel/intern/library_map.c
* \ingroup bke
*
* Utility functions for faster ID lookups.
*/
/** \name BKE_main_idmap API
*
* Cache ID (name, library lookups).
* This doesn't account for adding/removing data-blocks,
* and should only be used when performing many lookups.
*
* \note GHash's are initialized on demand,
* since its likely some types will never have lookups run on them,
* so its a waste to create and never use.
* \{ */
struct IDNameLib_Key {
/** ``ID.name + 2``: without the ID type prefix, since each id type gets it's own 'map' */
const char *name;
/** ``ID.lib``: */
const Library *lib;
};
struct IDNameLib_TypeMap {
GHash *map;
short id_type;
/* only for storage of keys in the ghash, avoid many single allocs */
struct IDNameLib_Key *keys;
};
/**
* Opaque structure, external API users only see this.
*/
struct IDNameLib_Map {
struct IDNameLib_TypeMap type_maps[MAX_LIBARRAY];
struct Main *bmain;
};
static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map, short id_type)
{
for (int i = 0; i < MAX_LIBARRAY; i++) {
if (id_map->type_maps[i].id_type == id_type) {
return &id_map->type_maps[i];
}
}
return NULL;
}
struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain)
{
struct IDNameLib_Map *id_map = MEM_mallocN(sizeof(*id_map), __func__);
int index = 0;
while (index < MAX_LIBARRAY) {
id_map->type_maps[index].map = NULL;
id_map->type_maps[index].id_type = BKE_idcode_iter_step(&index);
}
BLI_assert(index == MAX_LIBARRAY);
id_map->bmain = bmain;
return id_map;
}
struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map)
{
return id_map->bmain;
}
static unsigned int idkey_hash(const void *ptr)
{
const struct IDNameLib_Key *idkey = ptr;
unsigned int key = BLI_ghashutil_strhash(idkey->name);
if (idkey->lib) {
key ^= BLI_ghashutil_ptrhash(idkey->lib);
}
return key;
}
static bool idkey_cmp(const void *a, const void *b)
{
const struct IDNameLib_Key *idkey_a = a;
const struct IDNameLib_Key *idkey_b = b;
return strcmp(idkey_a->name, idkey_b->name) || (idkey_a->lib != idkey_b->lib);
}
ID *BKE_main_idmap_lookup(struct IDNameLib_Map *id_map, short id_type, const char *name, const Library *lib)
{
struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type);
if (UNLIKELY(type_map == NULL)) {
return NULL;
}
/* lazy init */
if (type_map->map == NULL) {
ListBase *lb = which_libbase(id_map->bmain, id_type);
const int lb_len = BLI_listbase_count(lb);
if (lb_len == 0) {
return NULL;
}
type_map->map = BLI_ghash_new_ex(idkey_hash, idkey_cmp, __func__, lb_len);
type_map->keys = MEM_mallocN(sizeof(struct IDNameLib_Key) * lb_len, __func__);
GHash *map = type_map->map;
struct IDNameLib_Key *key = type_map->keys;
for (ID *id = lb->first; id; id = id->next, key++) {
key->name = id->name + 2;
key->lib = id->lib;
BLI_ghash_insert(map, key, id);
}
}
const struct IDNameLib_Key key_lookup = {name, lib};
return BLI_ghash_lookup(type_map->map, &key_lookup);
}
ID *BKE_main_idmap_lookup_id(struct IDNameLib_Map *id_map, const ID *id)
{
return BKE_main_idmap_lookup(id_map, GS(id->name), id->name + 2, id->lib);
}
void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map)
{
struct IDNameLib_TypeMap *type_map = id_map->type_maps;
for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) {
if (type_map->map) {
BLI_ghash_free(type_map->map, NULL, NULL);
type_map->map = NULL;
MEM_freeN(type_map->keys);
}
}
MEM_freeN(id_map);
}
/** \} */

View File

@ -124,6 +124,7 @@
#include "BKE_global.h" // for G
#include "BKE_group.h"
#include "BKE_library.h" // for which_libbase
#include "BKE_library_idmap.h"
#include "BKE_library_query.h"
#include "BKE_idcode.h"
#include "BKE_material.h"
@ -211,6 +212,9 @@
/* use GHash for BHead name-based lookups (speeds up linking) */
#define USE_GHASH_BHEAD
/* Use GHash for restoring pointers by name */
#define USE_GHASH_RESTORE_POINTER
/***/
typedef struct OldNew {
@ -6389,68 +6393,96 @@ typedef enum ePointerUserMode {
USER_REAL = 1, /* ensure at least one real user (fake user ignored) */
} ePointerUserMode;
static bool restore_pointer(ID *id, ID *newid, ePointerUserMode user)
static void restore_pointer_user(ID *id, ID *newid, ePointerUserMode user)
{
if (STREQ(newid->name + 2, id->name + 2)) {
if (newid->lib == id->lib) {
if (user == USER_REAL) {
id_us_ensure_real(newid);
}
return true;
}
BLI_assert(STREQ(newid->name + 2, id->name + 2));
BLI_assert(newid->lib == id->lib);
UNUSED_VARS_NDEBUG(id);
if (user == USER_REAL) {
id_us_ensure_real(newid);
}
return false;
}
#ifndef USE_GHASH_RESTORE_POINTER
/**
* Only for undo files, or to restore a screen after reading without UI...
*
* user
* - USER_IGNORE: no usercount change
* - USER_REAL: ensure a real user (even if a fake one is set)
* A version of #restore_pointer_by_name that performs a full search (slow!).
* Use only for limited lookups, when the overhead of
* creating a #IDNameLib_Map for a single lookup isn't worthwhile.
*/
static void *restore_pointer_by_name(Main *mainp, ID *id, ePointerUserMode user)
static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode user)
{
if (id) {
ListBase *lb = which_libbase(mainp, GS(id->name));
if (lb) { // there's still risk of checking corrupt mem (freed Ids in oops)
if (lb) { /* there's still risk of checking corrupt mem (freed Ids in oops) */
ID *idn = lb->first;
for (; idn; idn = idn->next) {
if (restore_pointer(id, idn, user))
break;
if (STREQ(idn->name + 2, id->name + 2)) {
if (idn->lib == id->lib) {
restore_pointer_user(id, idn, user);
break;
}
}
}
return idn;
}
}
return NULL;
}
#endif
static void lib_link_seq_clipboard_pt_restore(ID *id, Main *newmain)
/**
* Only for undo files, or to restore a screen after reading without UI...
*
* \param user:
* - USER_IGNORE: no usercount change
* - USER_REAL: ensure a real user (even if a fake one is set)
* \param id_map: lookup table, use when performing many lookups.
* this could be made an optional agument (falling back to a full lookup),
* however at the moment it's always available.
*/
static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePointerUserMode user)
{
#ifdef USE_GHASH_RESTORE_POINTER
if (id) {
/* use fast lookup when available */
ID *idn = BKE_main_idmap_lookup_id(id_map, id);
if (idn) {
restore_pointer_user(id, idn, user);
}
return idn;
}
return NULL;
#else
Main *mainp = BKE_main_idmap_main_get(id_map);
return restore_pointer_by_name_main(mainp, id, user);
#endif
}
static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_map)
{
if (id) {
/* clipboard must ensure this */
BLI_assert(id->newid != NULL);
id->newid = restore_pointer_by_name(newmain, (ID *)id->newid, USER_REAL);
id->newid = restore_pointer_by_name(id_map, id->newid, USER_REAL);
}
}
static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
{
Main *newmain = (Main *)arg_pt;
struct IDNameLib_Map *id_map = arg_pt;
lib_link_seq_clipboard_pt_restore((ID *)seq->scene, newmain);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, newmain);
lib_link_seq_clipboard_pt_restore((ID *)seq->clip, newmain);
lib_link_seq_clipboard_pt_restore((ID *)seq->mask, newmain);
lib_link_seq_clipboard_pt_restore((ID *)seq->sound, newmain);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->clip, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->mask, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->sound, id_map);
return 1;
}
static void lib_link_clipboard_restore(Main *newmain)
static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map)
{
/* update IDs stored in sequencer clipboard */
BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, newmain);
BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
}
/* called from kernel/blender.c */
@ -6462,11 +6494,13 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
wmWindowManager *wm;
bScreen *sc;
ScrArea *sa;
struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain);
/* first windowmanager */
for (wm = newmain->wm.first; wm; wm = wm->id.next) {
for (win= wm->windows.first; win; win= win->next) {
win->screen = restore_pointer_by_name(newmain, (ID *)win->screen, USER_REAL);
win->screen = restore_pointer_by_name(id_map, (ID *)win->screen, USER_REAL);
if (win->screen == NULL)
win->screen = curscreen;
@ -6479,7 +6513,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
for (sc = newmain->screen.first; sc; sc = sc->id.next) {
Scene *oldscene = sc->scene;
sc->scene= restore_pointer_by_name(newmain, (ID *)sc->scene, USER_REAL);
sc->scene= restore_pointer_by_name(id_map, (ID *)sc->scene, USER_REAL);
if (sc->scene == NULL)
sc->scene = curscene;
@ -6498,16 +6532,16 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
if (v3d->scenelock)
v3d->camera = NULL; /* always get from scene */
else
v3d->camera = restore_pointer_by_name(newmain, (ID *)v3d->camera, USER_REAL);
v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL);
if (v3d->camera == NULL)
v3d->camera = sc->scene->camera;
v3d->ob_centre = restore_pointer_by_name(newmain, (ID *)v3d->ob_centre, USER_REAL);
v3d->ob_centre = restore_pointer_by_name(id_map, (ID *)v3d->ob_centre, USER_REAL);
for (bgpic= v3d->bgpicbase.first; bgpic; bgpic= bgpic->next) {
if ((bgpic->ima = restore_pointer_by_name(newmain, (ID *)bgpic->ima, USER_IGNORE))) {
if ((bgpic->ima = restore_pointer_by_name(id_map, (ID *)bgpic->ima, USER_IGNORE))) {
id_us_plus((ID *)bgpic->ima);
}
if ((bgpic->clip = restore_pointer_by_name(newmain, (ID *)bgpic->clip, USER_IGNORE))) {
if ((bgpic->clip = restore_pointer_by_name(id_map, (ID *)bgpic->clip, USER_IGNORE))) {
id_us_plus((ID *)bgpic->clip);
}
}
@ -6551,10 +6585,10 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
bDopeSheet *ads = sipo->ads;
if (ads) {
ads->source = restore_pointer_by_name(newmain, (ID *)ads->source, USER_REAL);
ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
if (ads->filter_grp)
ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE);
ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE);
}
/* force recalc of list of channels (i.e. includes calculating F-Curve colors)
@ -6564,7 +6598,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
}
else if (sl->spacetype == SPACE_BUTS) {
SpaceButs *sbuts = (SpaceButs *)sl;
sbuts->pinid = restore_pointer_by_name(newmain, sbuts->pinid, USER_IGNORE);
sbuts->pinid = restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE);
if (sbuts->pinid == NULL) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
@ -6581,11 +6615,11 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
else if (sl->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)sl;
saction->action = restore_pointer_by_name(newmain, (ID *)saction->action, USER_REAL);
saction->ads.source = restore_pointer_by_name(newmain, (ID *)saction->ads.source, USER_REAL);
saction->action = restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL);
saction->ads.source = restore_pointer_by_name(id_map, (ID *)saction->ads.source, USER_REAL);
if (saction->ads.filter_grp)
saction->ads.filter_grp = restore_pointer_by_name(newmain, (ID *)saction->ads.filter_grp, USER_IGNORE);
saction->ads.filter_grp = restore_pointer_by_name(id_map, (ID *)saction->ads.filter_grp, USER_IGNORE);
/* force recalc of list of channels, potentially updating the active action
@ -6596,7 +6630,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
else if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
sima->image = restore_pointer_by_name(newmain, (ID *)sima->image, USER_REAL);
sima->image = restore_pointer_by_name(id_map, (ID *)sima->image, USER_REAL);
/* this will be freed, not worth attempting to find same scene,
* since it gets initialized later */
@ -6611,8 +6645,8 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
sima->gpd = restore_pointer_by_name(newmain, (ID *)sima->gpd, USER_REAL);
sima->mask_info.mask = restore_pointer_by_name(newmain, (ID *)sima->mask_info.mask, USER_REAL);
sima->gpd = restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL);
sima->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sima->mask_info.mask, USER_REAL);
}
else if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
@ -6620,29 +6654,29 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
sseq->gpd = restore_pointer_by_name(newmain, (ID *)sseq->gpd, USER_REAL);
sseq->gpd = restore_pointer_by_name(id_map, (ID *)sseq->gpd, USER_REAL);
}
else if (sl->spacetype == SPACE_NLA) {
SpaceNla *snla = (SpaceNla *)sl;
bDopeSheet *ads = snla->ads;
if (ads) {
ads->source = restore_pointer_by_name(newmain, (ID *)ads->source, USER_REAL);
ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
if (ads->filter_grp)
ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE);
ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE);
}
}
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
st->text = restore_pointer_by_name(newmain, (ID *)st->text, USER_REAL);
st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_REAL);
if (st->text == NULL) st->text = newmain->text.first;
}
else if (sl->spacetype == SPACE_SCRIPT) {
SpaceScript *scpt = (SpaceScript *)sl;
scpt->script = restore_pointer_by_name(newmain, (ID *)scpt->script, USER_REAL);
scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL);
/*sc->script = NULL; - 2.45 set to null, better re-run the script */
if (scpt->script) {
@ -6652,7 +6686,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
else if (sl->spacetype == SPACE_OUTLINER) {
SpaceOops *so= (SpaceOops *)sl;
so->search_tse.id = restore_pointer_by_name(newmain, so->search_tse.id, USER_IGNORE);
so->search_tse.id = restore_pointer_by_name(id_map, so->search_tse.id, USER_IGNORE);
if (so->treestore) {
TreeStoreElem *tselem;
@ -6662,7 +6696,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
while ((tselem = BLI_mempool_iterstep(&iter))) {
/* Do not try to restore pointers to drivers/sequence/etc., can crash in undo case! */
if (TSE_IS_REAL_ID(tselem)) {
tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE);
tselem->id = restore_pointer_by_name(id_map, tselem->id, USER_IGNORE);
}
else {
tselem->id = NULL;
@ -6680,14 +6714,14 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
bNodeTree *ntree;
/* node tree can be stored locally in id too, link this first */
snode->id = restore_pointer_by_name(newmain, snode->id, USER_REAL);
snode->from = restore_pointer_by_name(newmain, snode->from, USER_IGNORE);
snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL);
snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE);
ntree = nodetree_from_id(snode->id);
if (ntree)
snode->nodetree = ntree;
else
snode->nodetree = restore_pointer_by_name(newmain, (ID*)snode->nodetree, USER_REAL);
snode->nodetree = restore_pointer_by_name(id_map, (ID*)snode->nodetree, USER_REAL);
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
@ -6695,7 +6729,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
path->nodetree = snode->nodetree;
}
else
path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, USER_REAL);
path->nodetree= restore_pointer_by_name(id_map, (ID*)path->nodetree, USER_REAL);
if (!path->nodetree)
break;
@ -6721,22 +6755,24 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
else if (sl->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)sl;
sclip->clip = restore_pointer_by_name(newmain, (ID *)sclip->clip, USER_REAL);
sclip->mask_info.mask = restore_pointer_by_name(newmain, (ID *)sclip->mask_info.mask, USER_REAL);
sclip->clip = restore_pointer_by_name(id_map, (ID *)sclip->clip, USER_REAL);
sclip->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sclip->mask_info.mask, USER_REAL);
sclip->scopes.ok = 0;
}
else if (sl->spacetype == SPACE_LOGIC) {
SpaceLogic *slogic = (SpaceLogic *)sl;
slogic->gpd = restore_pointer_by_name(newmain, (ID *)slogic->gpd, USER_REAL);
slogic->gpd = restore_pointer_by_name(id_map, (ID *)slogic->gpd, USER_REAL);
}
}
}
}
/* update IDs stored in all possible clipboards */
lib_link_clipboard_restore(newmain);
lib_link_clipboard_restore(id_map);
BKE_main_idmap_destroy(id_map);
}
static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)