Skip to content

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 the ID.name. This can be used to cast an ID \*id to the correct ID_Type. A macro GS(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 ID property 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, the LIB_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:

  • Poses (which are Object ID sub-data) directly reference their bone by pointer (bones are Armature ID sub-data). This creates a lot of issues and make handling of Armature data changes significantly more complicated than it should be.
  • VertexGroups are somewhat 'split' between the Object ID (where their existence and names are defined) and the geometry ID (meshes, lattices etc., where their actual data [weight value for each point] is defined). This kind of data layout does not only add a very huge amount of complexity to the code, it is not only an infinite source of bugs, but it also has some intrinsic limitations that make it close to unusable when several objects share the same geometry

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.hh 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:

  1. Define the IDTypeInfo in the relevant BKE's implementation file (e.g. mesh.c for ID_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).
  2. Add the extern declaration of the new IDType in BKE_idtype.h.
  3. Register the new IDTypeInfo in id_type_init() of idtype.cc.