Cleanup: split off direct_link_id() function
Differential Revision: https://developer.blender.org/D7324
This commit is contained in:
parent
c4def7992b
commit
0aac74f18f
Notes:
blender-bot
2023-02-14 06:05:22 +01:00
Referenced by commit 9632a06201
, Fix embedded IDs not being enough/properly initialized.
|
@ -470,8 +470,9 @@ static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *l
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void oldnewmap_free_unused(OldNewMap *onm)
|
||||
static void oldnewmap_clear(OldNewMap *onm)
|
||||
{
|
||||
/* Free unused data. */
|
||||
for (int i = 0; i < onm->nentries; i++) {
|
||||
OldNew *entry = &onm->entries[i];
|
||||
if (entry->nr == 0) {
|
||||
|
@ -479,10 +480,7 @@ static void oldnewmap_free_unused(OldNewMap *onm)
|
|||
entry->newp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void oldnewmap_clear(OldNewMap *onm)
|
||||
{
|
||||
onm->capacity_exp = DEFAULT_SIZE_EXP;
|
||||
oldnewmap_clear_map(onm);
|
||||
onm->nentries = 0;
|
||||
|
@ -2768,7 +2766,7 @@ static void direct_link_id_override_property_cb(FileData *fd, void *data)
|
|||
link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb);
|
||||
}
|
||||
|
||||
static void direct_link_id(FileData *fd, ID *id, ID *id_old);
|
||||
static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int tag);
|
||||
static void direct_link_nodetree(FileData *fd, bNodeTree *ntree);
|
||||
static void direct_link_collection(FileData *fd, Collection *collection);
|
||||
|
||||
|
@ -2778,7 +2776,8 @@ static void direct_link_id_private_id(FileData *fd, ID *id, ID *id_old)
|
|||
bNodeTree **nodetree = BKE_ntree_ptr_from_id(id);
|
||||
if (nodetree != NULL && *nodetree != NULL) {
|
||||
*nodetree = newdataadr(fd, *nodetree);
|
||||
direct_link_id(fd, (ID *)*nodetree, id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL);
|
||||
direct_link_id_common(
|
||||
fd, (ID *)*nodetree, id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL, 0);
|
||||
direct_link_nodetree(fd, *nodetree);
|
||||
}
|
||||
|
||||
|
@ -2786,15 +2785,16 @@ static void direct_link_id_private_id(FileData *fd, ID *id, ID *id_old)
|
|||
Scene *scene = (Scene *)id;
|
||||
if (scene->master_collection != NULL) {
|
||||
scene->master_collection = newdataadr(fd, scene->master_collection);
|
||||
direct_link_id(fd,
|
||||
&scene->master_collection->id,
|
||||
id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL);
|
||||
direct_link_id_common(fd,
|
||||
&scene->master_collection->id,
|
||||
id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL,
|
||||
0);
|
||||
direct_link_collection(fd, scene->master_collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_id(FileData *fd, ID *id, ID *id_old)
|
||||
static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int tag)
|
||||
{
|
||||
/*link direct data of ID properties*/
|
||||
if (id->properties) {
|
||||
|
@ -2804,10 +2804,8 @@ static void direct_link_id(FileData *fd, ID *id, ID *id_old)
|
|||
}
|
||||
id->py_instance = NULL;
|
||||
|
||||
/* That way data-lock reading not going through main read_libblock()
|
||||
* function are still in a clear tag state.
|
||||
* (glowering at certain nodetree fake data-lock here...). */
|
||||
id->tag = 0;
|
||||
/* Initialize with provided tag. */
|
||||
id->tag = tag;
|
||||
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
|
||||
|
||||
/* NOTE: It is important to not clear the recalc flags for undo/redo.
|
||||
|
@ -8378,7 +8376,7 @@ void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions)
|
|||
|
||||
static bool direct_link_screen(FileData *fd, bScreen *screen)
|
||||
{
|
||||
bool wrong_id = false;
|
||||
bool success = true;
|
||||
|
||||
screen->regionbase.first = screen->regionbase.last = NULL;
|
||||
screen->context = NULL;
|
||||
|
@ -8388,10 +8386,10 @@ static bool direct_link_screen(FileData *fd, bScreen *screen)
|
|||
|
||||
if (!direct_link_area_map(fd, AREAMAP_FROM_SCREEN(screen))) {
|
||||
printf("Error reading Screen %s... removing it.\n", screen->id.name + 2);
|
||||
wrong_id = true;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return wrong_id;
|
||||
return success;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -9227,289 +9225,15 @@ static const char *dataname(short id_code)
|
|||
return "Data from Lib Block";
|
||||
}
|
||||
|
||||
static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *allocname)
|
||||
static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *id_old)
|
||||
{
|
||||
bhead = blo_bhead_next(fd, bhead);
|
||||
|
||||
while (bhead && bhead->code == DATA) {
|
||||
void *data;
|
||||
#if 0
|
||||
/* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
|
||||
short* sp = fd->filesdna->structs[bhead->SDNAnr];
|
||||
char* tmp = malloc(100);
|
||||
allocname = fd->filesdna->types[sp[0]];
|
||||
strcpy(tmp, allocname);
|
||||
data = read_struct(fd, bhead, tmp);
|
||||
#else
|
||||
data = read_struct(fd, bhead, allocname);
|
||||
#endif
|
||||
|
||||
if (data) {
|
||||
oldnewmap_insert(fd->datamap, bhead->old, data, 0);
|
||||
}
|
||||
|
||||
bhead = blo_bhead_next(fd, bhead);
|
||||
if (fd->memfile == NULL) {
|
||||
/* When actually reading a file , we do want to reset/re-generate session uuids.
|
||||
* In undo case, we want to re-use existing ones. */
|
||||
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
|
||||
}
|
||||
|
||||
return bhead;
|
||||
}
|
||||
|
||||
static BHead *read_libblock(FileData *fd,
|
||||
Main *main,
|
||||
BHead *bhead,
|
||||
const int tag,
|
||||
const bool placeholder_set_indirect_extern,
|
||||
ID **r_id)
|
||||
{
|
||||
/* this routine reads a libblock and its direct data. Use link functions to connect it all
|
||||
*/
|
||||
ID *id;
|
||||
ListBase *lb;
|
||||
const char *allocname;
|
||||
|
||||
/* XXX Very weakly handled currently, see comment at the end of this function before trying to
|
||||
* use it for anything new. */
|
||||
bool wrong_id = false;
|
||||
|
||||
/* In undo case, most libs and linked data should be kept as is from previous state
|
||||
* (see BLO_read_from_memfile).
|
||||
* However, some needed by the snapshot being read may have been removed in previous one,
|
||||
* and would go missing.
|
||||
* This leads e.g. to disappearing objects in some undo/redo case, see T34446.
|
||||
* That means we have to carefully check whether current lib or
|
||||
* libdata already exits in old main, if it does we merely copy it over into new main area,
|
||||
* otherwise we have to do a full read of that bhead... */
|
||||
if (fd->memfile && ELEM(bhead->code, ID_LI, ID_LINK_PLACEHOLDER)) {
|
||||
const char *idname = blo_bhead_id_name(fd, bhead);
|
||||
|
||||
DEBUG_PRINTF("Checking %s...\n", idname);
|
||||
|
||||
if (bhead->code == ID_LI) {
|
||||
Main *libmain = fd->old_mainlist->first;
|
||||
/* Skip oldmain itself... */
|
||||
for (libmain = libmain->next; libmain; libmain = libmain->next) {
|
||||
DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>");
|
||||
if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) {
|
||||
Main *oldmain = fd->old_mainlist->first;
|
||||
DEBUG_PRINTF("FOUND!\n");
|
||||
/* In case of a library, we need to re-add its main to fd->mainlist,
|
||||
* because if we have later a missing ID_LINK_PLACEHOLDER,
|
||||
* we need to get the correct lib it is linked to!
|
||||
* Order is crucial, we cannot bulk-add it in BLO_read_from_memfile()
|
||||
* like it used to be. */
|
||||
BLI_remlink(fd->old_mainlist, libmain);
|
||||
BLI_remlink_safe(&oldmain->libraries, libmain->curlib);
|
||||
BLI_addtail(fd->mainlist, libmain);
|
||||
BLI_addtail(&main->libraries, libmain->curlib);
|
||||
|
||||
if (r_id) {
|
||||
*r_id = NULL; /* Just in case... */
|
||||
}
|
||||
return blo_bhead_next(fd, bhead);
|
||||
}
|
||||
DEBUG_PRINTF("nothing...\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("... in %s (%s): ",
|
||||
main->curlib ? main->curlib->id.name : "<NULL>",
|
||||
main->curlib ? main->curlib->name : "<NULL>");
|
||||
if ((id = BKE_libblock_find_name(main, GS(idname), idname + 2))) {
|
||||
DEBUG_PRINTF("FOUND!\n");
|
||||
/* Even though we found our linked ID,
|
||||
* there is no guarantee its address is still the same. */
|
||||
if (id != bhead->old) {
|
||||
oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
|
||||
}
|
||||
|
||||
/* No need to do anything else for ID_LINK_PLACEHOLDER,
|
||||
* it's assumed already present in its lib's main. */
|
||||
if (r_id) {
|
||||
*r_id = NULL; /* Just in case... */
|
||||
}
|
||||
return blo_bhead_next(fd, bhead);
|
||||
}
|
||||
DEBUG_PRINTF("nothing...\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* read libblock */
|
||||
fd->are_memchunks_identical = true;
|
||||
id = read_struct(fd, bhead, "lib block");
|
||||
const short idcode = id != NULL ? GS(id->name) : 0;
|
||||
|
||||
BHead *id_bhead = bhead;
|
||||
/* Used when undoing from memfile, we swap changed IDs into their old addresses when found. */
|
||||
ID *id_old = NULL;
|
||||
bool do_id_swap = false;
|
||||
|
||||
if (id != NULL) {
|
||||
const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
|
||||
|
||||
if (id_bhead->code != ID_LINK_PLACEHOLDER) {
|
||||
/* need a name for the mallocN, just for debugging and sane prints on leaks */
|
||||
allocname = dataname(idcode);
|
||||
|
||||
/* read all data into fd->datamap */
|
||||
/* TODO: instead of building oldnewmap here we could just quickly check the bheads... could
|
||||
* save some more ticks. Probably not worth it though, bottleneck is full depsgraph rebuild
|
||||
* and evaluate, not actual file reading. */
|
||||
bhead = read_data_into_oldnewmap(fd, id_bhead, allocname);
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"%s: ID %s is unchanged: %d\n", __func__, id->name, fd->are_memchunks_identical);
|
||||
|
||||
if (fd->memfile != NULL) {
|
||||
BLI_assert(fd->old_idmap != NULL || !do_partial_undo);
|
||||
/* This code should only ever be reached for local data-blocks. */
|
||||
BLI_assert(main->curlib == NULL);
|
||||
|
||||
/* Find the 'current' existing ID we want to reuse instead of the one we would read from
|
||||
* the undo memfile. */
|
||||
DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
|
||||
id->name,
|
||||
id->session_uuid);
|
||||
id_old = do_partial_undo ? BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid) :
|
||||
NULL;
|
||||
bool can_finalize_and_return = false;
|
||||
|
||||
if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) {
|
||||
/* Read WindowManager, Screen and WorkSpace IDs are never actually used during undo (see
|
||||
* `setup_app_data()` in `blendfile.c`).
|
||||
* So we can just abort here, just ensuring libmapping is set accordingly. */
|
||||
can_finalize_and_return = true;
|
||||
}
|
||||
else if (id_old != NULL && fd->are_memchunks_identical) {
|
||||
/* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as
|
||||
* this is only for do_version-like code), but for sake of consistency, and also because
|
||||
* it will tell us which ID is re-used from old Main, and which one is actually new. */
|
||||
id_old->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED;
|
||||
id_old->lib = main->curlib;
|
||||
id_old->us = ID_FAKE_USERS(id_old);
|
||||
/* Do not reset id->icon_id here, memory allocated for it remains valid. */
|
||||
/* Needed because .blend may have been saved with crap value here... */
|
||||
id_old->newid = NULL;
|
||||
id_old->orig_id = NULL;
|
||||
|
||||
/* About recalc: since that ID did not change at all, we know that its recalc fields also
|
||||
* remained unchanged, so no need to handle neither recalc nor recalc_undo_future here.
|
||||
*/
|
||||
|
||||
Main *old_bmain = fd->old_mainlist->first;
|
||||
ListBase *old_lb = which_libbase(old_bmain, idcode);
|
||||
ListBase *new_lb = which_libbase(main, idcode);
|
||||
BLI_remlink(old_lb, id_old);
|
||||
BLI_addtail(new_lb, id_old);
|
||||
|
||||
can_finalize_and_return = true;
|
||||
}
|
||||
|
||||
if (can_finalize_and_return) {
|
||||
DEBUG_PRINTF("Re-using existing ID %s instead of newly read one\n", id_old->name);
|
||||
oldnewmap_insert(fd->libmap, id_bhead->old, id_old, id_bhead->code);
|
||||
oldnewmap_insert(fd->libmap, id_old, id_old, id_bhead->code);
|
||||
|
||||
if (r_id) {
|
||||
*r_id = id_old;
|
||||
}
|
||||
|
||||
if (do_partial_undo) {
|
||||
/* Even though we re-use the old ID as-is, it does not mean that we are 100% safe from
|
||||
* needing some depsgraph updates for it (it could depend on another ID which address
|
||||
* did not change, but which actual content might have been re-read from the memfile).
|
||||
* IMPORTANT: Do not fully overwrite recalc flag here, depsgraph may not have been ran
|
||||
* yet for previous undo step(s), we do not want to erase flags set by those.
|
||||
*/
|
||||
if (fd->undo_direction < 0) {
|
||||
/* We are coming from the future (i.e. do an actual undo, and not a redo), we use our
|
||||
* old reused ID's 'accumulated recalc flags since last memfile undo step saving' as
|
||||
* recalc flags. */
|
||||
id_old->recalc |= id_old->recalc_undo_accumulated;
|
||||
}
|
||||
else {
|
||||
/* We are coming from the past (i.e. do a redo), we use the saved 'accumulated recalc
|
||||
* flags since last memfile undo step saving' from the newly read ID as recalc flags.
|
||||
*/
|
||||
id_old->recalc |= id->recalc_undo_accumulated;
|
||||
}
|
||||
/* There is no need to flush the depsgraph's CoWs here, since that ID's data itself did
|
||||
* not change. */
|
||||
|
||||
/* We need to 'accumulate' the accumulated recalc flags of all undo steps until we
|
||||
* actually perform a depsgraph update, otherwise we'd only ever use the flags from one
|
||||
* of the steps, and never get proper flags matching all others. */
|
||||
id_old->recalc_undo_accumulated |= id->recalc_undo_accumulated;
|
||||
}
|
||||
|
||||
MEM_freeN(id);
|
||||
oldnewmap_free_unused(fd->datamap);
|
||||
oldnewmap_clear(fd->datamap);
|
||||
|
||||
return bhead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do after read_struct, for dna reconstruct */
|
||||
lb = which_libbase(main, idcode);
|
||||
if (lb) {
|
||||
/* Some re-used old IDs might also use newly read ones, so we have to check for old memory
|
||||
* addresses for those as well. */
|
||||
if (fd->memfile != NULL && do_partial_undo && id->lib == NULL) {
|
||||
BLI_assert(fd->old_idmap != NULL);
|
||||
DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
|
||||
id->name,
|
||||
id->session_uuid);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
|
||||
if (id_old != NULL) {
|
||||
BLI_assert(MEM_allocN_len(id) == MEM_allocN_len(id_old));
|
||||
/* UI IDs are always re-used from old bmain at higher-level calling code, so never swap
|
||||
* those. Besides maybe custom properties, no other ID should have pointers to those
|
||||
* anyway...
|
||||
* And linked IDs are handled separately as well. */
|
||||
do_id_swap = !ELEM(idcode, ID_WM, ID_SCR, ID_WS) &&
|
||||
!(id_bhead->code == ID_LINK_PLACEHOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, we know we are going to keep that newly read & allocated ID, so we need to
|
||||
* reallocate it to ensure we actually get a unique memory address for it. */
|
||||
if (!do_id_swap) {
|
||||
DEBUG_PRINTF("using newly-read ID %s to a new mem address\n", id->name);
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("using newly-read ID %s to its old, already existing address\n", id->name);
|
||||
}
|
||||
|
||||
/* for ID_LINK_PLACEHOLDER check */
|
||||
ID *id_target = do_id_swap ? id_old : id;
|
||||
oldnewmap_insert(fd->libmap, id_bhead->old, id_target, id_bhead->code);
|
||||
oldnewmap_insert(fd->libmap, id_old, id_target, id_bhead->code);
|
||||
|
||||
BLI_addtail(lb, id);
|
||||
|
||||
if (fd->memfile == NULL) {
|
||||
/* When actually reading a file , we do want to reset/re-generate session uuids.
|
||||
* In unod case, we want to re-use existing ones. */
|
||||
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
|
||||
}
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
else {
|
||||
/* unknown ID type */
|
||||
printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8));
|
||||
MEM_freeN(id);
|
||||
id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_id) {
|
||||
*r_id = do_id_swap ? id_old : id;
|
||||
}
|
||||
if (!id) {
|
||||
return blo_bhead_next(fd, id_bhead);
|
||||
}
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
|
||||
id->lib = main->curlib;
|
||||
id->us = ID_FAKE_USERS(id);
|
||||
|
@ -9517,36 +9241,25 @@ static BHead *read_libblock(FileData *fd,
|
|||
id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */
|
||||
id->orig_id = NULL;
|
||||
|
||||
/* this case cannot be direct_linked: it's just the ID part */
|
||||
if (id_bhead->code == ID_LINK_PLACEHOLDER) {
|
||||
/* That way, we know which data-lock needs do_versions (required currently for linking). */
|
||||
id->tag = tag | LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
|
||||
|
||||
if (placeholder_set_indirect_extern) {
|
||||
if (id->flag & LIB_INDIRECT_WEAK_LINK) {
|
||||
id->tag |= LIB_TAG_INDIRECT;
|
||||
}
|
||||
else {
|
||||
id->tag |= LIB_TAG_EXTERN;
|
||||
}
|
||||
}
|
||||
|
||||
return blo_bhead_next(fd, id_bhead);
|
||||
if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
/* For placeholder we only need to set the tag, no further data to read. */
|
||||
id->tag = tag;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* init pointers direct data */
|
||||
direct_link_id(fd, id, id_old);
|
||||
/* Read part of datablock that is common between real and embedded datablocks. */
|
||||
direct_link_id_common(fd, id, id_old, tag);
|
||||
|
||||
/* That way, we know which data-lock needs do_versions (required currently for linking). */
|
||||
/* Note: doing this after direct_link_id(), which resets that field. */
|
||||
id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
|
||||
/* XXX Very weakly handled currently, see comment in read_libblock() before trying to
|
||||
* use it for anything new. */
|
||||
bool success = true;
|
||||
|
||||
switch (idcode) {
|
||||
switch (GS(id->name)) {
|
||||
case ID_WM:
|
||||
direct_link_windowmanager(fd, (wmWindowManager *)id);
|
||||
break;
|
||||
case ID_SCR:
|
||||
wrong_id = direct_link_screen(fd, (bScreen *)id);
|
||||
success = direct_link_screen(fd, (bScreen *)id);
|
||||
break;
|
||||
case ID_SCE:
|
||||
direct_link_scene(fd, (Scene *)id);
|
||||
|
@ -9661,10 +9374,307 @@ static BHead *read_libblock(FileData *fd,
|
|||
break;
|
||||
}
|
||||
|
||||
oldnewmap_free_unused(fd->datamap);
|
||||
return success;
|
||||
}
|
||||
|
||||
static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *allocname)
|
||||
{
|
||||
bhead = blo_bhead_next(fd, bhead);
|
||||
|
||||
while (bhead && bhead->code == DATA) {
|
||||
void *data;
|
||||
#if 0
|
||||
/* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
|
||||
short* sp = fd->filesdna->structs[bhead->SDNAnr];
|
||||
char* tmp = malloc(100);
|
||||
allocname = fd->filesdna->types[sp[0]];
|
||||
strcpy(tmp, allocname);
|
||||
data = read_struct(fd, bhead, tmp);
|
||||
#else
|
||||
data = read_struct(fd, bhead, allocname);
|
||||
#endif
|
||||
|
||||
if (data) {
|
||||
oldnewmap_insert(fd->datamap, bhead->old, data, 0);
|
||||
}
|
||||
|
||||
bhead = blo_bhead_next(fd, bhead);
|
||||
}
|
||||
|
||||
return bhead;
|
||||
}
|
||||
|
||||
static BHead *read_libblock(FileData *fd,
|
||||
Main *main,
|
||||
BHead *bhead,
|
||||
const int tag,
|
||||
const bool placeholder_set_indirect_extern,
|
||||
ID **r_id)
|
||||
{
|
||||
/* This routine reads a libblock and its direct data. Lib link functions will
|
||||
* set points between datablocks. */
|
||||
|
||||
/* In undo case, most libs and linked data should be kept as is from previous state
|
||||
* (see BLO_read_from_memfile).
|
||||
* However, some needed by the snapshot being read may have been removed in previous one,
|
||||
* and would go missing.
|
||||
* This leads e.g. to disappearing objects in some undo/redo case, see T34446.
|
||||
* That means we have to carefully check whether current lib or
|
||||
* libdata already exits in old main, if it does we merely copy it over into new main area,
|
||||
* otherwise we have to do a full read of that bhead... */
|
||||
if (fd->memfile && ELEM(bhead->code, ID_LI, ID_LINK_PLACEHOLDER)) {
|
||||
const char *idname = blo_bhead_id_name(fd, bhead);
|
||||
|
||||
DEBUG_PRINTF("Checking %s...\n", idname);
|
||||
|
||||
if (bhead->code == ID_LI) {
|
||||
Main *libmain = fd->old_mainlist->first;
|
||||
/* Skip oldmain itself... */
|
||||
for (libmain = libmain->next; libmain; libmain = libmain->next) {
|
||||
DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>");
|
||||
if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) {
|
||||
Main *oldmain = fd->old_mainlist->first;
|
||||
DEBUG_PRINTF("FOUND!\n");
|
||||
/* In case of a library, we need to re-add its main to fd->mainlist,
|
||||
* because if we have later a missing ID_LINK_PLACEHOLDER,
|
||||
* we need to get the correct lib it is linked to!
|
||||
* Order is crucial, we cannot bulk-add it in BLO_read_from_memfile()
|
||||
* like it used to be. */
|
||||
BLI_remlink(fd->old_mainlist, libmain);
|
||||
BLI_remlink_safe(&oldmain->libraries, libmain->curlib);
|
||||
BLI_addtail(fd->mainlist, libmain);
|
||||
BLI_addtail(&main->libraries, libmain->curlib);
|
||||
|
||||
if (r_id) {
|
||||
*r_id = NULL; /* Just in case... */
|
||||
}
|
||||
return blo_bhead_next(fd, bhead);
|
||||
}
|
||||
DEBUG_PRINTF("nothing...\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("... in %s (%s): ",
|
||||
main->curlib ? main->curlib->id.name : "<NULL>",
|
||||
main->curlib ? main->curlib->name : "<NULL>");
|
||||
ID *id = BKE_libblock_find_name(main, GS(idname), idname + 2);
|
||||
if (id != NULL) {
|
||||
DEBUG_PRINTF("FOUND!\n");
|
||||
/* Even though we found our linked ID,
|
||||
* there is no guarantee its address is still the same. */
|
||||
if (id != bhead->old) {
|
||||
oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
|
||||
}
|
||||
|
||||
/* No need to do anything else for ID_LINK_PLACEHOLDER,
|
||||
* it's assumed already present in its lib's main. */
|
||||
if (r_id) {
|
||||
*r_id = NULL; /* Just in case... */
|
||||
}
|
||||
return blo_bhead_next(fd, bhead);
|
||||
}
|
||||
DEBUG_PRINTF("nothing...\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* read libblock */
|
||||
fd->are_memchunks_identical = true;
|
||||
ID *id = read_struct(fd, bhead, "lib block");
|
||||
const short idcode = id != NULL ? GS(id->name) : 0;
|
||||
|
||||
BHead *id_bhead = bhead;
|
||||
/* Used when undoing from memfile, we swap changed IDs into their old addresses when found. */
|
||||
ID *id_old = NULL;
|
||||
bool do_id_swap = false;
|
||||
|
||||
if (id != NULL) {
|
||||
const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
|
||||
|
||||
if (id_bhead->code != ID_LINK_PLACEHOLDER) {
|
||||
/* need a name for the mallocN, just for debugging and sane prints on leaks */
|
||||
const char *allocname = dataname(idcode);
|
||||
|
||||
/* read all data into fd->datamap */
|
||||
/* TODO: instead of building oldnewmap here we could just quickly check the bheads... could
|
||||
* save some more ticks. Probably not worth it though, bottleneck is full depsgraph rebuild
|
||||
* and evaluate, not actual file reading. */
|
||||
bhead = read_data_into_oldnewmap(fd, id_bhead, allocname);
|
||||
|
||||
DEBUG_PRINTF(
|
||||
"%s: ID %s is unchanged: %d\n", __func__, id->name, fd->are_memchunks_identical);
|
||||
|
||||
if (fd->memfile != NULL) {
|
||||
BLI_assert(fd->old_idmap != NULL || !do_partial_undo);
|
||||
/* This code should only ever be reached for local data-blocks. */
|
||||
BLI_assert(main->curlib == NULL);
|
||||
|
||||
/* Find the 'current' existing ID we want to reuse instead of the one we would read from
|
||||
* the undo memfile. */
|
||||
DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
|
||||
id->name,
|
||||
id->session_uuid);
|
||||
id_old = do_partial_undo ? BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid) :
|
||||
NULL;
|
||||
bool can_finalize_and_return = false;
|
||||
|
||||
if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) {
|
||||
/* Read WindowManager, Screen and WorkSpace IDs are never actually used during undo (see
|
||||
* `setup_app_data()` in `blendfile.c`).
|
||||
* So we can just abort here, just ensuring libmapping is set accordingly. */
|
||||
can_finalize_and_return = true;
|
||||
}
|
||||
else if (id_old != NULL && fd->are_memchunks_identical) {
|
||||
/* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as
|
||||
* this is only for do_version-like code), but for sake of consistency, and also because
|
||||
* it will tell us which ID is re-used from old Main, and which one is actually new. */
|
||||
id_old->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED;
|
||||
id_old->lib = main->curlib;
|
||||
id_old->us = ID_FAKE_USERS(id_old);
|
||||
/* Do not reset id->icon_id here, memory allocated for it remains valid. */
|
||||
/* Needed because .blend may have been saved with crap value here... */
|
||||
id_old->newid = NULL;
|
||||
id_old->orig_id = NULL;
|
||||
|
||||
/* About recalc: since that ID did not change at all, we know that its recalc fields also
|
||||
* remained unchanged, so no need to handle neither recalc nor recalc_undo_future here.
|
||||
*/
|
||||
|
||||
Main *old_bmain = fd->old_mainlist->first;
|
||||
ListBase *old_lb = which_libbase(old_bmain, idcode);
|
||||
ListBase *new_lb = which_libbase(main, idcode);
|
||||
BLI_remlink(old_lb, id_old);
|
||||
BLI_addtail(new_lb, id_old);
|
||||
|
||||
can_finalize_and_return = true;
|
||||
}
|
||||
|
||||
if (can_finalize_and_return) {
|
||||
DEBUG_PRINTF("Re-using existing ID %s instead of newly read one\n", id_old->name);
|
||||
oldnewmap_insert(fd->libmap, id_bhead->old, id_old, id_bhead->code);
|
||||
oldnewmap_insert(fd->libmap, id_old, id_old, id_bhead->code);
|
||||
|
||||
if (r_id) {
|
||||
*r_id = id_old;
|
||||
}
|
||||
|
||||
if (do_partial_undo) {
|
||||
/* Even though we re-use the old ID as-is, it does not mean that we are 100% safe from
|
||||
* needing some depsgraph updates for it (it could depend on another ID which address
|
||||
* did not change, but which actual content might have been re-read from the memfile).
|
||||
* IMPORTANT: Do not fully overwrite recalc flag here, depsgraph may not have been ran
|
||||
* yet for previous undo step(s), we do not want to erase flags set by those.
|
||||
*/
|
||||
if (fd->undo_direction < 0) {
|
||||
/* We are coming from the future (i.e. do an actual undo, and not a redo), we use our
|
||||
* old reused ID's 'accumulated recalc flags since last memfile undo step saving' as
|
||||
* recalc flags. */
|
||||
id_old->recalc |= id_old->recalc_undo_accumulated;
|
||||
}
|
||||
else {
|
||||
/* We are coming from the past (i.e. do a redo), we use the saved 'accumulated recalc
|
||||
* flags since last memfile undo step saving' from the newly read ID as recalc flags.
|
||||
*/
|
||||
id_old->recalc |= id->recalc_undo_accumulated;
|
||||
}
|
||||
/* There is no need to flush the depsgraph's CoWs here, since that ID's data itself did
|
||||
* not change. */
|
||||
|
||||
/* We need to 'accumulate' the accumulated recalc flags of all undo steps until we
|
||||
* actually perform a depsgraph update, otherwise we'd only ever use the flags from one
|
||||
* of the steps, and never get proper flags matching all others. */
|
||||
id_old->recalc_undo_accumulated |= id->recalc_undo_accumulated;
|
||||
}
|
||||
|
||||
MEM_freeN(id);
|
||||
oldnewmap_clear(fd->datamap);
|
||||
|
||||
return bhead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do after read_struct, for dna reconstruct */
|
||||
ListBase *lb = which_libbase(main, idcode);
|
||||
if (lb) {
|
||||
/* Some re-used old IDs might also use newly read ones, so we have to check for old memory
|
||||
* addresses for those as well. */
|
||||
if (fd->memfile != NULL && do_partial_undo && id->lib == NULL) {
|
||||
BLI_assert(fd->old_idmap != NULL);
|
||||
DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
|
||||
id->name,
|
||||
id->session_uuid);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
|
||||
if (id_old != NULL) {
|
||||
BLI_assert(MEM_allocN_len(id) == MEM_allocN_len(id_old));
|
||||
/* UI IDs are always re-used from old bmain at higher-level calling code, so never swap
|
||||
* those. Besides maybe custom properties, no other ID should have pointers to those
|
||||
* anyway...
|
||||
* And linked IDs are handled separately as well. */
|
||||
do_id_swap = !ELEM(idcode, ID_WM, ID_SCR, ID_WS) &&
|
||||
!(id_bhead->code == ID_LINK_PLACEHOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, we know we are going to keep that newly read & allocated ID, so we need to
|
||||
* reallocate it to ensure we actually get a unique memory address for it. */
|
||||
if (!do_id_swap) {
|
||||
DEBUG_PRINTF("using newly-read ID %s to a new mem address\n", id->name);
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("using newly-read ID %s to its old, already existing address\n", id->name);
|
||||
}
|
||||
|
||||
/* for ID_LINK_PLACEHOLDER check */
|
||||
ID *id_target = do_id_swap ? id_old : id;
|
||||
oldnewmap_insert(fd->libmap, id_bhead->old, id_target, id_bhead->code);
|
||||
oldnewmap_insert(fd->libmap, id_old, id_target, id_bhead->code);
|
||||
|
||||
BLI_addtail(lb, id);
|
||||
}
|
||||
else {
|
||||
/* unknown ID type */
|
||||
printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8));
|
||||
MEM_freeN(id);
|
||||
id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_id) {
|
||||
*r_id = do_id_swap ? id_old : id;
|
||||
}
|
||||
if (!id) {
|
||||
return blo_bhead_next(fd, id_bhead);
|
||||
}
|
||||
|
||||
/* Set tag for new datablock to indicate lib linking and versioning needs
|
||||
* to be done still. */
|
||||
int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
|
||||
|
||||
if (id_bhead->code == ID_LINK_PLACEHOLDER) {
|
||||
/* Tag to get replaced by the actual linked datablock. */
|
||||
id_tag |= LIB_TAG_ID_LINK_PLACEHOLDER;
|
||||
|
||||
if (placeholder_set_indirect_extern) {
|
||||
if (id->flag & LIB_INDIRECT_WEAK_LINK) {
|
||||
id_tag |= LIB_TAG_INDIRECT;
|
||||
}
|
||||
else {
|
||||
id_tag |= LIB_TAG_EXTERN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read datablock contents. */
|
||||
const bool success = direct_link_id(fd, main, id_tag, id, id_old);
|
||||
|
||||
/* For placeholders we are done here. */
|
||||
if (id_bhead->code == ID_LINK_PLACEHOLDER) {
|
||||
return blo_bhead_next(fd, id_bhead);
|
||||
}
|
||||
|
||||
oldnewmap_clear(fd->datamap);
|
||||
|
||||
if (wrong_id) {
|
||||
if (!success) {
|
||||
/* XXX This is probably working OK currently given the very limited scope of that flag.
|
||||
* However, it is absolutely **not** handled correctly: it is freeing an ID pointer that has
|
||||
* been added to the fd->libmap mapping, which in theory could lead to nice crashes...
|
||||
|
@ -10151,7 +10161,6 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
|
|||
user->edit_studio_light = 0;
|
||||
|
||||
/* free fd->datamap again */
|
||||
oldnewmap_free_unused(fd->datamap);
|
||||
oldnewmap_clear(fd->datamap);
|
||||
|
||||
return bhead;
|
||||
|
|
Loading…
Reference in New Issue