Core: Make ID::runtime an allocated pointer.
Will avoid having to reshuffle all ID types' DNA alignement when modifying runtime data, avoid writing garbage data in blendfile, allow usage of non-trivial C++ data in that runtime struct, etc. NOTE: Trigger for this refactor was this commit in the upcoming packed data PR (!133801): https://projects.blender.org/blender/blender/commit/34a2ad81fbdcf7f Co-authored-by: Brecht Van Lommel <brecht@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/146046
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "DNA_action_defaults.h"
|
||||
#include "DNA_action_types.h"
|
||||
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_report.hh"
|
||||
@@ -138,7 +139,7 @@ void convert_legacy_animato_action(bAction &dna_action)
|
||||
|
||||
void tag_action_user_for_slotted_actions_conversion(ID &animated_id)
|
||||
{
|
||||
animated_id.runtime.readfile_data->tags.action_assignment_needs_slot = true;
|
||||
animated_id.runtime->readfile_data->tags.action_assignment_needs_slot = true;
|
||||
}
|
||||
|
||||
void tag_action_users_for_slotted_actions_conversion(Main &bmain)
|
||||
|
||||
@@ -42,8 +42,10 @@
|
||||
#include "DNA_userdef_enums.h"
|
||||
|
||||
struct BlendWriter;
|
||||
struct Depsgraph;
|
||||
struct GHash;
|
||||
struct ID;
|
||||
struct ID_Readfile_Data;
|
||||
struct Library;
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
@@ -51,6 +53,41 @@ struct PointerRNA;
|
||||
struct PropertyRNA;
|
||||
struct bContext;
|
||||
|
||||
namespace blender::bke::id {
|
||||
|
||||
/** Status used and counters created during id-remapping. */
|
||||
struct ID_Runtime_Remap {
|
||||
/** Status during ID remapping. */
|
||||
int status = 0;
|
||||
/** During ID remapping the number of skipped use cases that refcount the data-block. */
|
||||
int skipped_refcounted = 0;
|
||||
/**
|
||||
* During ID remapping the number of direct use cases that could be remapped
|
||||
* (e.g. obdata when in edit mode).
|
||||
*/
|
||||
int skipped_direct = 0;
|
||||
/** During ID remapping, the number of indirect use cases that could not be remapped. */
|
||||
int skipped_indirect = 0;
|
||||
};
|
||||
|
||||
struct ID_Runtime {
|
||||
ID_Runtime_Remap remap = {};
|
||||
/**
|
||||
* The depsgraph that owns this data block. This is only set on data-blocks which are
|
||||
* copied-on-eval by the depsgraph. Additional data-blocks created during depsgraph evaluation
|
||||
* are not owned by any specific depsgraph and thus this pointer is null for those.
|
||||
*/
|
||||
Depsgraph *depsgraph = nullptr;
|
||||
|
||||
/**
|
||||
* This data is only allocated & used during the readfile process. After that, the memory is
|
||||
* freed and the pointer set to `nullptr`.
|
||||
*/
|
||||
ID_Readfile_Data *readfile_data = nullptr;
|
||||
};
|
||||
|
||||
} // namespace blender::bke::id
|
||||
|
||||
/**
|
||||
* Get allocation size of a given data-block type and optionally allocation `r_name`.
|
||||
*/
|
||||
@@ -58,6 +95,9 @@ size_t BKE_libblock_get_alloc_info(short type, const char **r_name);
|
||||
/**
|
||||
* Allocates and returns memory of the right size for the specified block type,
|
||||
* initialized to zero.
|
||||
*
|
||||
* \note: Typically, caller also needs to immediately call #BKE_libblock_runtime_ensure on the
|
||||
* allocated ID data.
|
||||
*/
|
||||
ID *BKE_libblock_alloc_notest(short type) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
@@ -90,6 +130,14 @@ void *BKE_libblock_alloc_in_lib(Main *bmain,
|
||||
*/
|
||||
void BKE_libblock_init_empty(ID *id) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Ensure that the given ID does have a valid runtime data.
|
||||
*
|
||||
* Low-level API, should not be needed in typical ID usages, where ID::runtime can always be
|
||||
* assumed valid.
|
||||
*/
|
||||
void BKE_libblock_runtime_ensure(ID &id);
|
||||
|
||||
/**
|
||||
* Reset the runtime counters used by ID remapping.
|
||||
*/
|
||||
@@ -445,20 +493,35 @@ enum {
|
||||
/**
|
||||
* Low-level ID freeing functions.
|
||||
*
|
||||
* \note These functions do NOT cover embedded IDs. Those are managed by the
|
||||
* \note These `BKE_libblock_free_` functions do NOT cover embedded IDs. Those are managed by the
|
||||
* owning ID, and are typically allocated/freed from the IDType callbacks.
|
||||
*/
|
||||
void BKE_libblock_free_datablock(ID *id, int flag) ATTR_NONNULL();
|
||||
void BKE_libblock_free_data(ID *id, bool do_id_user) ATTR_NONNULL();
|
||||
void BKE_libblock_free_runtime_data(ID *id) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Only free generic Python instance data (ID::py_instance).
|
||||
*
|
||||
* In most cases #BKE_id_free_ex handles this, when lower level functions are called directly
|
||||
* this function will need to be called too, if Python has access to the data.
|
||||
*
|
||||
* ID data-blocks such as #Material.nodetree are not stored in #Main.
|
||||
*/
|
||||
void BKE_libblock_free_data_py(ID *id);
|
||||
/**
|
||||
* Only free generic runtime data (ID::runtime).
|
||||
*
|
||||
* In most cases #BKE_libblock_free_data handles this, but in rare cases (currently in readfile,
|
||||
* when freeing linked ID placeholders), it is necessary.
|
||||
*/
|
||||
void BKE_libblock_free_runtime_data(ID *id);
|
||||
|
||||
/** Free generic ID data, including the runtime and animation data, but not the python data. */
|
||||
void BKE_libblock_free_data(ID *id, bool do_id_user) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Free IDtype-specific data (does _not_ free generic ID data, use
|
||||
* #BKE_libblock_free_data for that).
|
||||
*/
|
||||
void BKE_libblock_free_datablock(ID *id, int flag) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Complete ID freeing, extended version for corner cases.
|
||||
|
||||
@@ -1300,6 +1300,13 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
|
||||
*
|
||||
* **************************** */
|
||||
|
||||
void BKE_libblock_runtime_ensure(ID &id)
|
||||
{
|
||||
if (!id.runtime) {
|
||||
id.runtime = MEM_new<blender::bke::id::ID_Runtime>(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
size_t BKE_libblock_get_alloc_info(short type, const char **r_name)
|
||||
{
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(type);
|
||||
@@ -1322,7 +1329,8 @@ ID *BKE_libblock_alloc_notest(short type)
|
||||
const char *name;
|
||||
size_t size = BKE_libblock_get_alloc_info(type, &name);
|
||||
if (size != 0) {
|
||||
return static_cast<ID *>(MEM_callocN(size, name));
|
||||
ID *id = static_cast<ID *>(MEM_callocN(size, name));
|
||||
return id;
|
||||
}
|
||||
BLI_assert_msg(0, "Request to allocate unknown data type");
|
||||
return nullptr;
|
||||
@@ -1339,6 +1347,7 @@ void *BKE_libblock_alloc_in_lib(Main *bmain,
|
||||
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_LOCAL) == 0);
|
||||
|
||||
ID *id = BKE_libblock_alloc_notest(type);
|
||||
BKE_libblock_runtime_ensure(*id);
|
||||
|
||||
if (id) {
|
||||
if ((flag & LIB_ID_CREATE_NO_MAIN) != 0) {
|
||||
@@ -1443,10 +1452,10 @@ void BKE_libblock_init_empty(ID *id)
|
||||
|
||||
void BKE_libblock_runtime_reset_remapping_status(ID *id)
|
||||
{
|
||||
id->runtime.remap.status = 0;
|
||||
id->runtime.remap.skipped_refcounted = 0;
|
||||
id->runtime.remap.skipped_direct = 0;
|
||||
id->runtime.remap.skipped_indirect = 0;
|
||||
id->runtime->remap.status = 0;
|
||||
id->runtime->remap.skipped_refcounted = 0;
|
||||
id->runtime->remap.skipped_direct = 0;
|
||||
id->runtime->remap.skipped_indirect = 0;
|
||||
}
|
||||
|
||||
/* ********** ID session-wise UID management. ********** */
|
||||
@@ -1550,6 +1559,7 @@ void BKE_libblock_copy_in_lib(Main *bmain,
|
||||
* Clear and initialize it similar to BKE_libblock_alloc_in_lib. */
|
||||
const size_t size = BKE_libblock_get_alloc_info(GS(id->name), nullptr);
|
||||
memset(new_id, 0, size);
|
||||
BKE_libblock_runtime_ensure(*new_id);
|
||||
STRNCPY(new_id->name, id->name);
|
||||
new_id->us = 0;
|
||||
new_id->tag |= ID_TAG_NOT_ALLOCATED | ID_TAG_NO_MAIN | ID_TAG_NO_USER_REFCOUNT;
|
||||
|
||||
@@ -71,9 +71,9 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user)
|
||||
MEM_freeN(id->library_weak_reference);
|
||||
}
|
||||
|
||||
BKE_libblock_free_runtime_data(id);
|
||||
|
||||
BKE_animdata_free(id, do_id_user);
|
||||
|
||||
BKE_libblock_free_runtime_data(id);
|
||||
}
|
||||
|
||||
void BKE_libblock_free_datablock(ID *id, const int /*flag*/)
|
||||
@@ -92,11 +92,15 @@ void BKE_libblock_free_datablock(ID *id, const int /*flag*/)
|
||||
|
||||
void BKE_libblock_free_runtime_data(ID *id)
|
||||
{
|
||||
/* During "normal" file loading this data is released when versioning ends. Some versioning code
|
||||
* also deletes IDs, though. For example, in the startup blend file, brushes that were replaced
|
||||
* by assets are deleted. This means that the regular "delete this ID" flow (aka this code here)
|
||||
* also needs to free this data. */
|
||||
BLO_readfile_id_runtime_data_free(*id);
|
||||
if (id->runtime) {
|
||||
/* During "normal" file loading this data is released when versioning ends. Some versioning
|
||||
* code also deletes IDs, though. For example, in the startup blend file, brushes that were
|
||||
* replaced by assets are deleted. This means that the regular "delete this ID" flow (aka this
|
||||
* code here) also needs to free this data. */
|
||||
BLO_readfile_id_runtime_data_free(*id);
|
||||
|
||||
MEM_SAFE_DELETE(id->runtime);
|
||||
}
|
||||
}
|
||||
|
||||
static int id_free(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag)
|
||||
|
||||
@@ -82,21 +82,21 @@ static void foreach_libblock_remap_callback_skip(const ID * /*id_owner*/,
|
||||
BLI_assert(id != nullptr);
|
||||
|
||||
if (is_indirect) {
|
||||
id->runtime.remap.skipped_indirect++;
|
||||
id->runtime->remap.skipped_indirect++;
|
||||
}
|
||||
else if (violates_never_null || is_obj_editmode || is_reference) {
|
||||
id->runtime.remap.skipped_direct++;
|
||||
id->runtime->remap.skipped_direct++;
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
if (cb_flag & IDWALK_CB_USER) {
|
||||
id->runtime.remap.skipped_refcounted++;
|
||||
id->runtime->remap.skipped_refcounted++;
|
||||
}
|
||||
else if (cb_flag & IDWALK_CB_USER_ONE) {
|
||||
/* No need to count number of times this happens, just a flag is enough. */
|
||||
id->runtime.remap.status |= ID_REMAP_IS_USER_ONE_SKIPPED;
|
||||
id->runtime->remap.status |= ID_REMAP_IS_USER_ONE_SKIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner,
|
||||
ID *new_id = violates_never_null ? nullptr : *id_ptr;
|
||||
|
||||
if (!is_indirect && new_id) {
|
||||
new_id->runtime.remap.status |= ID_REMAP_IS_LINKED_DIRECT;
|
||||
new_id->runtime->remap.status |= ID_REMAP_IS_LINKED_DIRECT;
|
||||
}
|
||||
|
||||
if (skip_user_refcount) {
|
||||
@@ -444,7 +444,7 @@ static void libblock_remap_data_update_tags(ID *old_id, ID *new_id, IDRemap *id_
|
||||
}
|
||||
|
||||
if (new_id != nullptr && (new_id->tag & ID_TAG_INDIRECT) &&
|
||||
(new_id->runtime.remap.status & ID_REMAP_IS_LINKED_DIRECT))
|
||||
(new_id->runtime->remap.status & ID_REMAP_IS_LINKED_DIRECT))
|
||||
{
|
||||
new_id->tag &= ~ID_TAG_INDIRECT;
|
||||
new_id->flag &= ~ID_FLAG_INDIRECT_WEAK_LINK;
|
||||
@@ -569,13 +569,13 @@ static void libblock_remap_foreach_idpair(ID *old_id, ID *new_id, Main *bmain, i
|
||||
* count has actually been incremented for that, we have to decrease once more its user
|
||||
* count... unless we had to skip some 'user_one' cases. */
|
||||
if ((old_id->tag & ID_TAG_EXTRAUSER_SET) &&
|
||||
!(old_id->runtime.remap.status & ID_REMAP_IS_USER_ONE_SKIPPED))
|
||||
!(old_id->runtime->remap.status & ID_REMAP_IS_USER_ONE_SKIPPED))
|
||||
{
|
||||
id_us_clear_real(old_id);
|
||||
}
|
||||
}
|
||||
|
||||
const int skipped_refcounted = old_id->runtime.remap.skipped_refcounted;
|
||||
const int skipped_refcounted = old_id->runtime->remap.skipped_refcounted;
|
||||
if (old_id->us - skipped_refcounted < 0) {
|
||||
CLOG_ERROR(&LOG,
|
||||
"Error in remapping process from '%s' (%p) to '%s' (%p): "
|
||||
@@ -587,7 +587,7 @@ static void libblock_remap_foreach_idpair(ID *old_id, ID *new_id, Main *bmain, i
|
||||
old_id->us - skipped_refcounted);
|
||||
}
|
||||
|
||||
const int skipped_direct = old_id->runtime.remap.skipped_direct;
|
||||
const int skipped_direct = old_id->runtime->remap.skipped_direct;
|
||||
if (skipped_direct == 0) {
|
||||
/* old_id is assumed to not be used directly anymore... */
|
||||
if (old_id->lib && (old_id->tag & ID_TAG_EXTERN)) {
|
||||
|
||||
@@ -2191,29 +2191,29 @@ static int direct_link_id_restore_recalc(const FileData *fd,
|
||||
|
||||
static void readfile_id_runtime_data_ensure(ID &id)
|
||||
{
|
||||
if (id.runtime.readfile_data) {
|
||||
if (id.runtime->readfile_data) {
|
||||
return;
|
||||
}
|
||||
id.runtime.readfile_data = MEM_callocN<ID_Readfile_Data>(__func__);
|
||||
id.runtime->readfile_data = MEM_callocN<ID_Readfile_Data>(__func__);
|
||||
}
|
||||
|
||||
ID_Readfile_Data::Tags BLO_readfile_id_runtime_tags(ID &id)
|
||||
{
|
||||
if (!id.runtime.readfile_data) {
|
||||
if (!id.runtime->readfile_data) {
|
||||
return ID_Readfile_Data::Tags{};
|
||||
}
|
||||
return id.runtime.readfile_data->tags;
|
||||
return id.runtime->readfile_data->tags;
|
||||
}
|
||||
|
||||
ID_Readfile_Data::Tags &BLO_readfile_id_runtime_tags_for_write(ID &id)
|
||||
{
|
||||
readfile_id_runtime_data_ensure(id);
|
||||
return id.runtime.readfile_data->tags;
|
||||
return id.runtime->readfile_data->tags;
|
||||
}
|
||||
|
||||
void BLO_readfile_id_runtime_data_free(ID &id)
|
||||
{
|
||||
MEM_SAFE_FREE(id.runtime.readfile_data);
|
||||
MEM_SAFE_FREE(id.runtime->readfile_data);
|
||||
}
|
||||
|
||||
void BLO_readfile_id_runtime_data_free_all(Main &bmain)
|
||||
@@ -2246,6 +2246,9 @@ static void direct_link_id_common(BlendDataReader *reader,
|
||||
const int id_tag,
|
||||
const ID_Readfile_Data::Tags id_read_tags)
|
||||
{
|
||||
BLI_assert(id->runtime == nullptr);
|
||||
BKE_libblock_runtime_ensure(*id);
|
||||
|
||||
if (!BLO_read_data_is_undo(reader)) {
|
||||
/* When actually reading a file, we do want to reset/re-generate session UIDS.
|
||||
* In undo case, we want to re-use existing ones. */
|
||||
@@ -2271,13 +2274,8 @@ static void direct_link_id_common(BlendDataReader *reader,
|
||||
id->tag = id_tag;
|
||||
}
|
||||
|
||||
if (!BLO_read_data_is_undo(reader)) {
|
||||
/* Reset the runtime data, as there were versions of Blender that did not do
|
||||
* this before writing to disk. */
|
||||
id->runtime = ID_Runtime{};
|
||||
}
|
||||
readfile_id_runtime_data_ensure(*id);
|
||||
id->runtime.readfile_data->tags = id_read_tags;
|
||||
id->runtime->readfile_data->tags = id_read_tags;
|
||||
|
||||
if ((id_tag & ID_TAG_TEMP_MAIN) == 0) {
|
||||
BKE_lib_libblock_session_uid_ensure(id);
|
||||
@@ -2561,6 +2559,7 @@ static ID *create_placeholder(Main *mainvar,
|
||||
{
|
||||
ListBase *lb = which_libbase(mainvar, idcode);
|
||||
ID *ph_id = BKE_libblock_alloc_notest(idcode);
|
||||
BKE_libblock_runtime_ensure(*ph_id);
|
||||
|
||||
*((short *)ph_id->name) = idcode;
|
||||
BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2);
|
||||
@@ -4953,15 +4952,19 @@ static void read_library_linked_ids(FileData *basefd, FileData *fd, Main *mainva
|
||||
/* Transfer the readfile data from the placeholder to the real ID, but
|
||||
* only if the real ID has no readfile data yet. The same realid may be
|
||||
* referred to by multiple placeholders. */
|
||||
if (realid && !realid->runtime.readfile_data) {
|
||||
realid->runtime.readfile_data = id->runtime.readfile_data;
|
||||
id->runtime.readfile_data = nullptr;
|
||||
if (realid && !realid->runtime->readfile_data) {
|
||||
realid->runtime->readfile_data = id->runtime->readfile_data;
|
||||
id->runtime->readfile_data = nullptr;
|
||||
}
|
||||
|
||||
/* The 'readfile' runtime data needs to be freed here, as this ID placeholder does not go
|
||||
* through versioning (the usual place where this data is freed). Since `id` is not a real
|
||||
* ID, this shouldn't follow any pointers to embedded IDs. */
|
||||
BLO_readfile_id_runtime_data_free(*id);
|
||||
/* Ensure that the runtime pointer, and its 'readfile' sub-data, are properly freed, as
|
||||
* this ID placeholder does not go through versioning (the usual place where this data is
|
||||
* freed). Since `id` is not a real ID, this shouldn't follow any pointers to embedded IDs.
|
||||
*
|
||||
* WARNING! This placeholder ID is only an ID struct, with a very small subset of regular
|
||||
* ID common data actually valid and needing to be freed. Therefore, calling
|
||||
* #BKE_libblock_free_data on it would not work. */
|
||||
BKE_libblock_free_runtime_data(id);
|
||||
|
||||
MEM_freeN(id);
|
||||
}
|
||||
|
||||
@@ -1336,6 +1336,7 @@ BLO_Write_IDBuffer::BLO_Write_IDBuffer(ID &id, const bool is_undo, const bool is
|
||||
}
|
||||
temp_id->us = 0;
|
||||
temp_id->icon_id = 0;
|
||||
temp_id->runtime = nullptr;
|
||||
/* 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. */
|
||||
@@ -1349,8 +1350,6 @@ BLO_Write_IDBuffer::BLO_Write_IDBuffer(ID &id, const bool is_undo, const bool is
|
||||
* when we need to re-read the ID into its original address, this is currently cleared in
|
||||
* #direct_link_id_common in `readfile.cc` anyway. */
|
||||
temp_id->py_instance = nullptr;
|
||||
/* Clear runtime data struct. */
|
||||
temp_id->runtime = ID_Runtime{};
|
||||
}
|
||||
|
||||
BLO_Write_IDBuffer::BLO_Write_IDBuffer(ID &id, BlendWriter *writer)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "BKE_action.hh" /* XXX: BKE_pose_channel_find_name */
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
@@ -283,7 +284,7 @@ const ID *DEG_get_original_id(const ID *id)
|
||||
|
||||
Depsgraph *DEG_get_depsgraph_by_id(const ID &id)
|
||||
{
|
||||
return id.runtime.depsgraph;
|
||||
return id.runtime->depsgraph;
|
||||
}
|
||||
|
||||
bool DEG_is_original_id(const ID *id)
|
||||
|
||||
@@ -753,9 +753,15 @@ ID *deg_expand_eval_copy_datablock(const Depsgraph *depsgraph, const IDNode *id_
|
||||
DEG_COW_PRINT(
|
||||
"Expanding datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow);
|
||||
|
||||
/* Sanity checks. */
|
||||
/* Sanity checks.
|
||||
*
|
||||
* At this point, `id_cow` is essentially considered as a (partially dirty) allocated buffer (it
|
||||
* has been freed, but not fully cleared, as a result of calling #deg_free_eval_copy_datablock on
|
||||
* it). It is not expected to have any valid sub-data, not even a valid `ID::runtime` pointer.
|
||||
*/
|
||||
BLI_assert(check_datablock_expanded(id_cow) == false);
|
||||
BLI_assert(id_cow->py_instance == nullptr);
|
||||
BLI_assert(id_cow->runtime == nullptr);
|
||||
|
||||
/* Copy data from original ID to a copied version. */
|
||||
/* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference
|
||||
@@ -1022,7 +1028,7 @@ void deg_tag_eval_copy_id(deg::Depsgraph &depsgraph, ID *id_cow, const ID *id_or
|
||||
/* This ID is no longer localized, is a self-sustaining copy now. */
|
||||
id_cow->tag &= ~ID_TAG_LOCALIZED;
|
||||
id_cow->orig_id = (ID *)id_orig;
|
||||
id_cow->runtime.depsgraph = &reinterpret_cast<::Depsgraph &>(depsgraph);
|
||||
id_cow->runtime->depsgraph = &reinterpret_cast<::Depsgraph &>(depsgraph);
|
||||
}
|
||||
|
||||
bool deg_eval_copy_is_expanded(const ID *id_cow)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "DNA_ID.h"
|
||||
|
||||
struct ID;
|
||||
struct Depsgraph;
|
||||
|
||||
/* Uncomment this to have verbose log about original and evaluated pointers
|
||||
* logged, with detailed information when they are allocated, expanded
|
||||
|
||||
@@ -81,6 +81,8 @@ void IDNode::init_copy_on_write(ID *id_cow_hint)
|
||||
}
|
||||
else if (deg_eval_copy_is_needed(id_orig)) {
|
||||
id_cow = BKE_libblock_alloc_notest(GS(id_orig->name));
|
||||
/* No need to call #BKE_libblock_runtime_ensure here, this will be done by
|
||||
* #BKE_libblock_copy_in_lib when #deg_expand_eval_copy_datablock is executed. */
|
||||
DEG_COW_PRINT(
|
||||
"Allocate memory for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
struct ARegion;
|
||||
struct AutoComplete;
|
||||
struct Depsgraph;
|
||||
struct EnumPropertyItem;
|
||||
struct FileSelectParams;
|
||||
struct ID;
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#ifdef __cplusplus
|
||||
# include <type_traits>
|
||||
|
||||
namespace blender::bke::id {
|
||||
struct ID_Runtime;
|
||||
}
|
||||
namespace blender::bke {
|
||||
struct PreviewImageRuntime;
|
||||
}
|
||||
@@ -29,6 +32,7 @@ struct IDPropertyGroupChildrenSet;
|
||||
namespace blender::bke::library {
|
||||
struct LibraryRuntime;
|
||||
}
|
||||
using ID_RuntimeHandle = blender::bke::id::ID_Runtime;
|
||||
using PreviewImageRuntimeHandle = blender::bke::PreviewImageRuntime;
|
||||
using LibraryRuntimeHandle = blender::bke::library::LibraryRuntime;
|
||||
using IDPropertyGroupChildrenSet = blender::bke::idprop::IDPropertyGroupChildrenSet;
|
||||
@@ -36,6 +40,7 @@ using IDPropertyGroupChildrenSet = blender::bke::idprop::IDPropertyGroupChildren
|
||||
typedef struct PreviewImageRuntimeHandle PreviewImageRuntimeHandle;
|
||||
typedef struct LibraryRuntimeHandle LibraryRuntimeHandle;
|
||||
typedef struct IDPropertyGroupChildrenSet IDPropertyGroupChildrenSet;
|
||||
typedef struct ID_RuntimeHandle ID_RuntimeHandle;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -45,11 +50,9 @@ extern "C" {
|
||||
struct FileData;
|
||||
struct GHash;
|
||||
struct ID;
|
||||
struct ID_Readfile_Data;
|
||||
struct Library;
|
||||
struct PackedFile;
|
||||
struct UniqueName_Map;
|
||||
struct Depsgraph;
|
||||
|
||||
typedef struct IDPropertyUIData {
|
||||
/** Tool-tip / property description pointer. Owned by the #IDProperty. */
|
||||
@@ -380,37 +383,6 @@ enum {
|
||||
ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1,
|
||||
};
|
||||
|
||||
/** Status used and counters created during id-remapping. */
|
||||
typedef struct ID_Runtime_Remap {
|
||||
/** Status during ID remapping. */
|
||||
int status;
|
||||
/** During ID remapping the number of skipped use cases that refcount the data-block. */
|
||||
int skipped_refcounted;
|
||||
/**
|
||||
* During ID remapping the number of direct use cases that could be remapped
|
||||
* (e.g. obdata when in edit mode).
|
||||
*/
|
||||
int skipped_direct;
|
||||
/** During ID remapping, the number of indirect use cases that could not be remapped. */
|
||||
int skipped_indirect;
|
||||
} ID_Runtime_Remap;
|
||||
|
||||
typedef struct ID_Runtime {
|
||||
ID_Runtime_Remap remap;
|
||||
/**
|
||||
* The depsgraph that owns this data block. This is only set on data-blocks which are
|
||||
* copied-on-eval by the depsgraph. Additional data-blocks created during depsgraph evaluation
|
||||
* are not owned by any specific depsgraph and thus this pointer is null for those.
|
||||
*/
|
||||
struct Depsgraph *depsgraph;
|
||||
|
||||
/**
|
||||
* This data is only allocated & used during the readfile process. After that, the memory is
|
||||
* freed and the pointer set to `nullptr`.
|
||||
*/
|
||||
struct ID_Readfile_Data *readfile_data;
|
||||
} ID_Runtime;
|
||||
|
||||
typedef struct ID {
|
||||
/* There's a nasty circular dependency here.... 'void *' to the rescue! I
|
||||
* really wonder why this is needed. */
|
||||
@@ -513,7 +485,17 @@ typedef struct ID {
|
||||
*/
|
||||
struct LibraryWeakReference *library_weak_reference;
|
||||
|
||||
struct ID_Runtime runtime;
|
||||
/**
|
||||
* Allocated runtime data, never written on disk or in undo steps.
|
||||
*
|
||||
* _Always_ valid for code handling IDs managed by the `BKE_lib_id` API.
|
||||
*
|
||||
* Internal low-level implementation of ID creation/copying/deletion, and code handling IDs
|
||||
* themselves in non-standard ways (mainly the CoW IDs in depsgraph, and some temporary IDs in
|
||||
* readfile) may have to manage this pointer themselves (see also #BKE_libblock_runtime_ensure
|
||||
* and #BKE_libblock_free_runtime_data).
|
||||
*/
|
||||
ID_RuntimeHandle *runtime;
|
||||
} ID;
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,8 +43,6 @@ typedef struct Mask {
|
||||
int flag;
|
||||
char _pad[4];
|
||||
|
||||
void *_pad1;
|
||||
|
||||
Mask_Runtime runtime;
|
||||
} Mask;
|
||||
|
||||
|
||||
@@ -90,8 +90,6 @@ typedef struct MovieClip {
|
||||
/** Grease pencil data. */
|
||||
struct bGPdata *gpd;
|
||||
|
||||
void *_pad1;
|
||||
|
||||
/** Data for SfM tracking. */
|
||||
struct MovieTracking tracking;
|
||||
/**
|
||||
|
||||
@@ -2113,7 +2113,6 @@ typedef struct Scene {
|
||||
ListBase base DNA_DEPRECATED;
|
||||
/** Active base. */
|
||||
struct Base *basact DNA_DEPRECATED;
|
||||
void *_pad1;
|
||||
|
||||
/** 3d cursor location. */
|
||||
View3DCursor cursor;
|
||||
@@ -2138,7 +2137,6 @@ typedef struct Scene {
|
||||
|
||||
/** Default allocated now. */
|
||||
struct ToolSettings *toolsettings;
|
||||
void *_pad4;
|
||||
struct DisplaySafeAreas safe_areas;
|
||||
|
||||
/* Migrate or replace? depends on some internal things... */
|
||||
@@ -2186,7 +2184,6 @@ typedef struct Scene {
|
||||
/** Physics simulation settings. */
|
||||
struct PhysicsSettings physics_settings;
|
||||
|
||||
void *_pad8;
|
||||
/**
|
||||
* XXX: runtime flag for drawing, actually belongs in the window,
|
||||
* only used by #BKE_object_handle_update()
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef struct bSound {
|
||||
|
||||
ID id;
|
||||
|
||||
void *_pad1;
|
||||
|
||||
/**
|
||||
* The path to the sound file.
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,8 @@ typedef struct Text {
|
||||
|
||||
ID id;
|
||||
|
||||
void *_pad1;
|
||||
|
||||
/**
|
||||
* Optional file path, when NULL text is considered internal.
|
||||
* Otherwise this path will be used when saving/reloading.
|
||||
|
||||
@@ -81,10 +81,11 @@ typedef struct Tex {
|
||||
/** Animation data (must be immediately after id for utilities to use it). */
|
||||
struct AnimData *adt;
|
||||
|
||||
void *_pad3;
|
||||
|
||||
float noisesize, turbul;
|
||||
float bright, contrast, saturation, rfac, gfac, bfac;
|
||||
float filtersize;
|
||||
char _pad2[4];
|
||||
|
||||
/* newnoise: musgrave parameters */
|
||||
float mg_H, mg_lacunarity, mg_octaves, mg_offset, mg_gain;
|
||||
@@ -122,7 +123,6 @@ typedef struct Tex {
|
||||
int sfra DNA_DEPRECATED;
|
||||
|
||||
float checkerdist, nabla;
|
||||
char _pad1[4];
|
||||
|
||||
struct ImageUser iuser;
|
||||
|
||||
|
||||
@@ -49,17 +49,15 @@ typedef struct World {
|
||||
* bit 0: Do mist
|
||||
*/
|
||||
short mode;
|
||||
char _pad2[6];
|
||||
|
||||
/** Assorted settings. */
|
||||
short flag;
|
||||
|
||||
float misi, miststa, mistdist, misthi;
|
||||
|
||||
/** Ambient occlusion. */
|
||||
float aodist, aoenergy;
|
||||
|
||||
/** Assorted settings. */
|
||||
short flag;
|
||||
char _pad3[2];
|
||||
|
||||
/** Eevee settings. */
|
||||
/**
|
||||
* Resolution of the world probe when baked to a texture. Contains `eLightProbeResolution`.
|
||||
@@ -73,11 +71,9 @@ typedef struct World {
|
||||
float sun_shadow_maximum_resolution;
|
||||
float sun_shadow_jitter_overblur;
|
||||
float sun_shadow_filter_radius;
|
||||
char _pad4[4];
|
||||
|
||||
short pr_texture;
|
||||
short use_nodes DNA_DEPRECATED;
|
||||
char _pad[4];
|
||||
|
||||
/* previews */
|
||||
struct PreviewImage *preview;
|
||||
@@ -88,6 +84,8 @@ typedef struct World {
|
||||
/** Light-group membership information. */
|
||||
struct LightgroupMembership *lightgroup;
|
||||
|
||||
void *_pad1;
|
||||
|
||||
/** Runtime. */
|
||||
ListBase gpumaterial;
|
||||
/* The Depsgraph::update_count when this World was last updated. */
|
||||
|
||||
Reference in New Issue
Block a user