Source/Architecture/ID/ID Type
< Source | Architecture/ID
ID Datablocks
Objects, meshes, materials, scenes, those are all examples of ID Datablocks. They are generally defined in the DNA_{some type}_types.h
header files.
ID Datablocks are blocks of memory that start with a set of common properties. In C this is modeled as a struct that embeds a struct of type ID
as the first field. If it has animation data in the form of an AnimData
struct, this MUST be the second field.
typedef struct Mesh {
ID id;
struct AnimData *adt;
…
};
ID Data
Here is a brief overview of some key information included in the ID structure. Its full definition can be found in the DNA_ID.h
file.
- Name & Type (
ID.name
) - The name of the ID (
pointer->id.name
) encodes the type (ID_Type
enum) of the datablock in its first two chars. For example, a mesh that is presented in the Blender user interface with the name "Suzanne" is actually named"MESuzanne"
in theID.name
. This can be used to cast anID *id
to the correctID_Type
. A macroGS(name)
is available to take such a name and return a constant that indicates the ID data type:if (GS(id->name) == ID_SC) …
can be used to test whether the ID is a Scene.
- ID Properties (
ID.properties
) - IDs can store random user-defined data using the
IDProperty
system.
- Tags (
ID.tag
) - Tags are runtime-only information about the data-block status: does it belong the the Main data-base or not, is it evaluated data handled by the Depsgraph, is it local or linked data, etc.
- Tags get reset on file read/write.
- Flags (
ID.flag
) - Flags are persistent information about the data-block status: does it have a 'fake user' that prevents it to be deleted automatically, is it an embedded data-block, etc.
- Recalc Flags (
ID.recalc
) - Flags used to control the Depsgraph Update process.
- Users Refcounting (
ID.us
) - A counter of how many other data-blocks are using this one.
- Currently in Blender, if an ID has no (
0
) users, it will not be saved on disk. To preserve an unused ID, theLIB_FAKEUSER
flag must be set, which enforce a non-zero user count.
ID and Relationships Between Data
Ideally, an ID should own and be responsible for all sub-data it uses, and not share any kind of ownership over it with any other ID. This includes handling of its allocation, freeing, copying, etc.
Therefore, a given ID should only reference other IDs (by pointers). Referencing sub-data of another ID should be avoided as much as possible, and never done by actual pointers (conceptually, the reference should then be composed of a pointer to the ID owning that sub-data, and some form of 'path' from this owner (e.g. the name of a modifier, bone, etc.)).
Not following those principles will severely increase complexity in ID management code, and make it much less robust and maintainble than it should be.
Bad Examples
Current code in Blender features several counter-examples to that ideal full separation of data between each ID. Those include:
|
Runtime Registration of ID Types - IDTypeInfo
Before early 2020, ID types were hard-coded in Blender, and their management code was spread all around BKE code base, often inside of giant switch
statements covering all ID types.
This is being transformed into a runtime data structure (see T73719).
Ultimately this should allow to encapsulate all ID management tasks specific to an ID type into this structure, which can then be used in a fully generic way by the core ID management code.
Runtime type information is stored in a IDTypeInfo
struct. This contains metadata of the struct, as well as pointers to functions for generic operations (create, copy, delete, make local, file read/write, looping over other ID usages, etc.). This is an example of such a struct, in this case for Objects:
IDTypeInfo IDType_ID_OB = {
/* id_code */ ID_OB,
/* id_filter */ FILTER_ID_OB,
/* main_listbase_index */ INDEX_ID_OB,
/* struct_size */ sizeof(Object),
/* name */ "Object",
/* name_plural */ "objects",
/* translation_context */ BLT_I18NCONTEXT_ID_OBJECT,
/* flags */ 0,
/* asset_type_info */ &AssetType_OB,
/* init_data */ object_init_data,
/* copy_data */ object_copy_data,
/* free_data */ object_free_data,
/* make_local */ object_make_local,
/* foreach_id */ object_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ object_foreach_path,
/* owner_get */ nullptr,
/* blend_write */ object_blend_write,
/* blend_read_data */ object_blend_read_data,
/* blend_read_lib */ object_blend_read_lib,
/* blend_read_expand */ object_blend_read_expand,
/* blend_read_undo_preserve */ nullptr,
/* lib_override_apply_post */ object_lib_override_apply_post,
};
BKE_idtype.h defines the IDTypeInfo structure, its callbacks, and an API to help with handling of ID types.
Adding a new ID Type
To add a new IDTypeInfo
you need to:
- Define the
IDTypeInfo
in the relevant BKE's implementation file (e.g.mesh.c
forID_ME
, etc.).- Put the
static
callbacks and then the IDTypeInfo struct definition at the start of those files. - If you need extra helpers, put only their forward declaration there, and implement them below the
IDTypeInfo
definition. This allows for a consistent clear separation between common info and specific APIs for each ID type implementation file. - The IDTypeInfo should be named from its idcode (e.g. IDType_ID_OB for object).
- Put the
- Add the
extern
declaration of the newIDType
inBKE_idtype.h
. - Register the new
IDTypeInfo
inid_type_init()
ofidtype.cc
.