blendfile write: Fix handling of embedded IDs. #106935
|
@ -137,8 +137,13 @@ static void light_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
|
||||
/* Node-tree is integral part of lights, no libdata. */
|
||||
if (la->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, la->nodetree);
|
||||
ntreeBlendWrite(writer, la->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &la->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(
|
||||
writer, bNodeTree, la->nodetree, BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(writer, (bNodeTree *)BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
|
||||
BKE_previewimg_blend_write(writer, la->preview);
|
||||
|
|
|
@ -463,8 +463,17 @@ static void linestyle_blend_write(BlendWriter *writer, ID *id, const void *id_ad
|
|||
}
|
||||
}
|
||||
if (linestyle->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, linestyle->nodetree);
|
||||
ntreeBlendWrite(writer, linestyle->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &linestyle->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
bNodeTree,
|
||||
linestyle->nodetree,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(
|
||||
writer,
|
||||
reinterpret_cast<bNodeTree *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,8 +185,15 @@ static void material_blend_write(BlendWriter *writer, ID *id, const void *id_add
|
|||
|
||||
/* nodetree is integral part of material, no libdata */
|
||||
if (ma->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, ma->nodetree);
|
||||
ntreeBlendWrite(writer, ma->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &ma->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(
|
||||
writer, bNodeTree, ma->nodetree, BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(
|
||||
writer,
|
||||
reinterpret_cast<bNodeTree *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
|
||||
BKE_previewimg_blend_write(writer, ma->preview);
|
||||
|
|
|
@ -947,6 +947,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
{
|
||||
Scene *sce = (Scene *)id;
|
||||
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
|
||||
if (BLO_write_is_undo(writer)) {
|
||||
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
|
||||
/* XXX This UI data should not be stored in Scene at all... */
|
||||
|
@ -1074,8 +1076,15 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
}
|
||||
|
||||
if (sce->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, sce->nodetree);
|
||||
ntreeBlendWrite(writer, sce->nodetree);
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &sce->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
bNodeTree,
|
||||
sce->nodetree,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(
|
||||
writer,
|
||||
reinterpret_cast<bNodeTree *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
}
|
||||
|
||||
BKE_color_managed_view_settings_blend_write(writer, &sce->view_settings);
|
||||
|
@ -1102,8 +1111,15 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
}
|
||||
|
||||
if (sce->master_collection) {
|
||||
BLO_write_struct(writer, Collection, sce->master_collection);
|
||||
BKE_collection_blend_write_nolib(writer, sce->master_collection);
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &sce->master_collection->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
Collection,
|
||||
sce->master_collection,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
BKE_collection_blend_write_nolib(
|
||||
writer,
|
||||
reinterpret_cast<Collection *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
}
|
||||
|
||||
/* Eevee Light-cache */
|
||||
|
@ -1116,6 +1132,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
|
||||
/* Freed on `do_versions()`. */
|
||||
BLI_assert(sce->layer_properties == nullptr);
|
||||
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
|
||||
static void direct_link_paint_helper(BlendDataReader *reader, const Scene *scene, Paint **paint)
|
||||
|
|
|
@ -107,8 +107,17 @@ static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_a
|
|||
|
||||
/* nodetree is integral part of simulation, no libdata */
|
||||
if (simulation->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, simulation->nodetree);
|
||||
ntreeBlendWrite(writer, simulation->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &simulation->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
bNodeTree,
|
||||
simulation->nodetree,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(
|
||||
writer,
|
||||
reinterpret_cast<bNodeTree *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,8 +155,17 @@ static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_addr
|
|||
|
||||
/* nodetree is integral part of texture, no libdata */
|
||||
if (tex->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, tex->nodetree);
|
||||
ntreeBlendWrite(writer, tex->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &tex->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
bNodeTree,
|
||||
tex->nodetree,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(
|
||||
writer,
|
||||
reinterpret_cast<bNodeTree *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
|
||||
BKE_previewimg_blend_write(writer, tex->preview);
|
||||
|
|
|
@ -144,8 +144,15 @@ static void world_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
|
||||
/* nodetree is integral part of world, no libdata */
|
||||
if (wrld->nodetree) {
|
||||
BLO_write_struct(writer, bNodeTree, wrld->nodetree);
|
||||
ntreeBlendWrite(writer, wrld->nodetree);
|
||||
BLO_Write_IDBuffer *temp_embedded_id_buffer = BLO_write_allocate_id_buffer();
|
||||
BLO_write_init_id_buffer_from_id(
|
||||
temp_embedded_id_buffer, &wrld->nodetree->id, BLO_write_is_undo(writer));
|
||||
BLO_write_struct_at_address(writer,
|
||||
bNodeTree,
|
||||
wrld->nodetree,
|
||||
BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
ntreeBlendWrite(writer, (bNodeTree *)BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer));
|
||||
BLO_write_destroy_id_buffer(&temp_embedded_id_buffer);
|
||||
}
|
||||
|
||||
BKE_previewimg_blend_write(writer, wrld->preview);
|
||||
|
|
|
@ -157,6 +157,23 @@ void blo_write_id_struct(BlendWriter *writer,
|
|||
#define BLO_write_id_struct(writer, struct_name, id_address, id) \
|
||||
blo_write_id_struct(writer, BLO_get_struct_id(writer, struct_name), id_address, id)
|
||||
|
||||
/**
|
||||
* Specific code to prepare IDs to be written.
|
||||
*
|
||||
* Required for writing properly embedded IDs currently.
|
||||
*
|
||||
* \note: Once there is a better generic handling of embedded IDs, this may go back to private code
|
||||
* in writefile.c
|
||||
*/
|
||||
typedef struct BLO_Write_IDBuffer BLO_Write_IDBuffer;
|
||||
|
||||
BLO_Write_IDBuffer *BLO_write_allocate_id_buffer(void);
|
||||
void BLO_write_init_id_buffer_from_id(BLO_Write_IDBuffer *id_buffer,
|
||||
struct ID *id,
|
||||
const bool is_undo);
|
||||
struct ID *BLO_write_get_id_buffer_temp_id(BLO_Write_IDBuffer *id_buffer);
|
||||
void BLO_write_destroy_id_buffer(BLO_Write_IDBuffer **id_buffer);
|
||||
|
||||
/**
|
||||
* Write raw data.
|
||||
*/
|
||||
|
|
|
@ -1088,6 +1088,78 @@ static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
|
|||
/** \name File Writing (Private)
|
||||
* \{ */
|
||||
|
||||
#define ID_BUFFER_STATIC_SIZE 8192
|
||||
|
||||
typedef struct BLO_Write_IDBuffer {
|
||||
const struct IDTypeInfo *id_type;
|
||||
ID *temp_id;
|
||||
char id_buffer_static[ID_BUFFER_STATIC_SIZE];
|
||||
} BLO_Write_IDBuffer;
|
||||
|
||||
static void id_buffer_init_for_id_type(BLO_Write_IDBuffer *id_buffer, const IDTypeInfo *id_type)
|
||||
{
|
||||
if (id_type != id_buffer->id_type) {
|
||||
const size_t idtype_struct_size = id_type->struct_size;
|
||||
if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
|
||||
CLOG_ERROR(&LOG,
|
||||
"ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, "
|
||||
"which needs %lu bytes",
|
||||
ID_BUFFER_STATIC_SIZE,
|
||||
id_type->name,
|
||||
idtype_struct_size);
|
||||
id_buffer->temp_id = static_cast<ID *>(MEM_mallocN(idtype_struct_size, __func__));
|
||||
}
|
||||
else {
|
||||
if (static_cast<void *>(id_buffer->temp_id) != id_buffer->id_buffer_static) {
|
||||
MEM_SAFE_FREE(id_buffer->temp_id);
|
||||
}
|
||||
id_buffer->temp_id = reinterpret_cast<ID *>(id_buffer->id_buffer_static);
|
||||
}
|
||||
id_buffer->id_type = id_type;
|
||||
}
|
||||
}
|
||||
|
||||
static void id_buffer_init_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
|
||||
{
|
||||
BLI_assert(id_buffer->id_type == BKE_idtype_get_info_from_id(id));
|
||||
|
||||
if (is_undo) {
|
||||
/* Record the changes that happened up to this undo push in
|
||||
* recalc_up_to_undo_push, and clear `recalc_after_undo_push` again
|
||||
* to start accumulating for the next undo push. */
|
||||
id->recalc_up_to_undo_push = id->recalc_after_undo_push;
|
||||
id->recalc_after_undo_push = 0;
|
||||
}
|
||||
|
||||
/* Copy ID data itself into buffer, to be able to freely modify it. */
|
||||
const size_t idtype_struct_size = id_buffer->id_type->struct_size;
|
||||
ID *temp_id = id_buffer->temp_id;
|
||||
memcpy(temp_id, id, idtype_struct_size);
|
||||
|
||||
/* Clear runtime data to reduce false detection of changed data in undo/redo context. */
|
||||
if (is_undo) {
|
||||
temp_id->tag &= LIB_TAG_KEEP_ON_UNDO;
|
||||
}
|
||||
else {
|
||||
temp_id->tag = 0;
|
||||
}
|
||||
temp_id->us = 0;
|
||||
temp_id->icon_id = 0;
|
||||
/* Those listbase data change every time we add/remove an ID, and also often when
|
||||
* renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
|
||||
* detections between undo steps. */
|
||||
temp_id->prev = nullptr;
|
||||
temp_id->next = nullptr;
|
||||
/* Those runtime pointers should never be set during writing stage, but just in case clear
|
||||
* them too. */
|
||||
temp_id->orig_id = nullptr;
|
||||
temp_id->newid = nullptr;
|
||||
/* Even though in theory we could be able to preserve this python instance across undo even
|
||||
* when we need to re-read the ID into its original address, this is currently cleared in
|
||||
* #direct_link_id_common in `readfile.c` anyway. */
|
||||
temp_id->py_instance = nullptr;
|
||||
}
|
||||
|
||||
/* Helper callback for checking linked IDs used by given ID (assumed local), to ensure directly
|
||||
* linked data is tagged accordingly. */
|
||||
static int write_id_direct_linked_data_process_cb(LibraryIDLinkCallbackData *cb_data)
|
||||
|
@ -1187,11 +1259,11 @@ static bool write_file_handle(Main *mainvar,
|
|||
nullptr :
|
||||
BKE_lib_override_library_operations_store_init();
|
||||
|
||||
#define ID_BUFFER_STATIC_SIZE 8192
|
||||
/* This outer loop allows to save first data-blocks from real mainvar,
|
||||
* then the temp ones from override process,
|
||||
* if needed, without duplicating whole code. */
|
||||
Main *bmain = mainvar;
|
||||
BLO_Write_IDBuffer *id_buffer = BLO_write_allocate_id_buffer();
|
||||
do {
|
||||
ListBase *lbarray[INDEX_ID_MAX];
|
||||
int a = set_listbasepointers(bmain, lbarray);
|
||||
|
@ -1202,19 +1274,8 @@ static bool write_file_handle(Main *mainvar,
|
|||
continue; /* Libraries are handled separately below. */
|
||||
}
|
||||
|
||||
char id_buffer_static[ID_BUFFER_STATIC_SIZE];
|
||||
void *id_buffer = id_buffer_static;
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
|
||||
const size_t idtype_struct_size = id_type->struct_size;
|
||||
if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
|
||||
CLOG_ERROR(&LOG,
|
||||
"ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, "
|
||||
"which needs %lu bytes",
|
||||
ID_BUFFER_STATIC_SIZE,
|
||||
id_type->name,
|
||||
id_type->struct_size);
|
||||
id_buffer = MEM_mallocN(idtype_struct_size, __func__);
|
||||
}
|
||||
id_buffer_init_for_id_type(id_buffer, id_type);
|
||||
|
||||
for (; id; id = static_cast<ID *>(id->next)) {
|
||||
/* We should never attempt to write non-regular IDs
|
||||
|
@ -1249,57 +1310,12 @@ static bool write_file_handle(Main *mainvar,
|
|||
BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
|
||||
}
|
||||
|
||||
if (wd->use_memfile) {
|
||||
/* Record the changes that happened up to this undo push in
|
||||
* recalc_up_to_undo_push, and clear `recalc_after_undo_push` again
|
||||
* to start accumulating for the next undo push. */
|
||||
id->recalc_up_to_undo_push = id->recalc_after_undo_push;
|
||||
id->recalc_after_undo_push = 0;
|
||||
|
||||
bNodeTree *nodetree = ntreeFromID(id);
|
||||
if (nodetree != nullptr) {
|
||||
nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
|
||||
nodetree->id.recalc_after_undo_push = 0;
|
||||
}
|
||||
if (GS(id->name) == ID_SCE) {
|
||||
Scene *scene = (Scene *)id;
|
||||
if (scene->master_collection != nullptr) {
|
||||
scene->master_collection->id.recalc_up_to_undo_push =
|
||||
scene->master_collection->id.recalc_after_undo_push;
|
||||
scene->master_collection->id.recalc_after_undo_push = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mywrite_id_begin(wd, id);
|
||||
|
||||
memcpy(id_buffer, id, idtype_struct_size);
|
||||
|
||||
/* Clear runtime data to reduce false detection of changed data in undo/redo context. */
|
||||
if (wd->use_memfile) {
|
||||
((ID *)id_buffer)->tag &= LIB_TAG_KEEP_ON_UNDO;
|
||||
}
|
||||
else {
|
||||
((ID *)id_buffer)->tag = 0;
|
||||
}
|
||||
((ID *)id_buffer)->us = 0;
|
||||
((ID *)id_buffer)->icon_id = 0;
|
||||
/* Those listbase data change every time we add/remove an ID, and also often when
|
||||
* renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
|
||||
* detections between undo steps. */
|
||||
((ID *)id_buffer)->prev = nullptr;
|
||||
((ID *)id_buffer)->next = nullptr;
|
||||
/* Those runtime pointers should never be set during writing stage, but just in case clear
|
||||
* them too. */
|
||||
((ID *)id_buffer)->orig_id = nullptr;
|
||||
((ID *)id_buffer)->newid = nullptr;
|
||||
/* Even though in theory we could be able to preserve this python instance across undo even
|
||||
* when we need to re-read the ID into its original address, this is currently cleared in
|
||||
* #direct_link_id_common in `readfile.c` anyway, */
|
||||
((ID *)id_buffer)->py_instance = nullptr;
|
||||
id_buffer_init_from_id(id_buffer, id, wd->use_memfile);
|
||||
|
||||
if (id_type->blend_write != nullptr) {
|
||||
id_type->blend_write(&writer, (ID *)id_buffer, id);
|
||||
id_type->blend_write(&writer, static_cast<ID *>(id_buffer->temp_id), id);
|
||||
}
|
||||
|
||||
if (do_override) {
|
||||
|
@ -1309,14 +1325,12 @@ static bool write_file_handle(Main *mainvar,
|
|||
mywrite_id_end(wd, id);
|
||||
}
|
||||
|
||||
if (id_buffer != id_buffer_static) {
|
||||
MEM_SAFE_FREE(id_buffer);
|
||||
}
|
||||
|
||||
mywrite_flush(wd);
|
||||
}
|
||||
} while ((bmain != override_storage) && (bmain = override_storage));
|
||||
|
||||
BLO_write_destroy_id_buffer(&id_buffer);
|
||||
|
||||
if (override_storage) {
|
||||
BKE_lib_override_library_operations_store_finalize(override_storage);
|
||||
override_storage = nullptr;
|
||||
|
@ -1571,6 +1585,39 @@ bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int w
|
|||
return (err == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* API to handle writing IDs while clearing some of their runtime data.
|
||||
*/
|
||||
|
||||
BLO_Write_IDBuffer *BLO_write_allocate_id_buffer()
|
||||
{
|
||||
return MEM_cnew<BLO_Write_IDBuffer>(__func__);
|
||||
}
|
||||
|
||||
void BLO_write_init_id_buffer_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
|
||||
{
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
|
||||
id_buffer_init_for_id_type(id_buffer, id_type);
|
||||
id_buffer_init_from_id(id_buffer, id, is_undo);
|
||||
}
|
||||
|
||||
ID *BLO_write_get_id_buffer_temp_id(BLO_Write_IDBuffer *id_buffer)
|
||||
{
|
||||
return id_buffer->temp_id;
|
||||
}
|
||||
|
||||
void BLO_write_destroy_id_buffer(BLO_Write_IDBuffer **id_buffer)
|
||||
{
|
||||
if (static_cast<void *>((*id_buffer)->temp_id) != (*id_buffer)->id_buffer_static) {
|
||||
MEM_SAFE_FREE((*id_buffer)->temp_id);
|
||||
}
|
||||
MEM_SAFE_FREE(*id_buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* API to write chunks of data.
|
||||
*/
|
||||
|
||||
void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
|
||||
{
|
||||
writedata(writer->wd, BLO_CODE_DATA, size_in_bytes, data_ptr);
|
||||
|
|
Loading…
Reference in New Issue