Blender “DNA” was described by example in Chapter 2, when we talked about Blender state and serialization. This chapter covers how the Blender codebase sets “DNA” variables via “RNA” properties.1
An additional aspect to the makesrna module is that multiple source files are generated during the build process. They are then fed back into the source code compiled as the runtime Blender executable. This means that makesrna is distinct from other Blender modules. It is implemented in this way, so that objects of “RNA” properties, that is, structs definitions with property data values such as initial settings, value ranges, and accessor function pointers, are not hand-coded. The makesrna module allows Blender developers to incorporate new “DNA” types by adding required wrapper code. In addition, a paradigm of the codebase is to “bake-in” elements of data, for runtime efficiency. This was another design consideration of the makesrna module, taking its direction from makesdna. However, makedna uses code generation to a lesser extent than makesrna, as will be seen.
The Blender makesrna Module
makesrna is somewhat non-standard in the Blender codebase. We explore its various parts, in the next few sections. There is a distinction between the Data API and the broader makesrna module API. As usual, the makerna API function names start with an RNA_* prefix.
Some RNA_* functions are used more by non-runtime portions of the makesrna module for generating Data API backend functions (generated makesrna source code). The Data API RNA_* interface is provided in [repository root]/source/blender/makesrna/RNA_access.h, whereas the makesrna RNA_* API interface, used heavily by the non-runtime makesrna code, is contained in [repository root]/source/blender/makesrna/RNA_define.h.
Runtime vs. Non-runtime Code
The makesrna module has both a runtime and non-runtime portion. The non-runtime portion is dedicated to source code generation.
An individual file in the repository’s makesrna module may include both runtime and non-runtime code. Runtime code becomes part of the Blender executable. Non-runtime code is compiled as part of the tools that generate runtime code.
This chapter references generated files which are created by a build and written to the build directory. Therefore, we preface each path with either [repository root] or [build root].
During the build process, the makesrna program is run. It is compiled from [repository root]/source/blender/makesrna/intern/makesrna.c. makesrna.c contains no runtime code. Many of the files in [repository root]/source/blender/makesrna/intern/ have a separate runtime and non-runtime portion that is included (or excluded) from compilation by the preprocessor.
At the top of every generated source file is the macro #define RNA_RUNTIME. The source code in generated files belongs to the runtime. By defining the RNA_RUNTIME macro, the generated files affect which part of a repository source file is compiled. As such, many of the source files in the repository are compiled twice, first for the non-runtime and then for the runtime after RNA_RUNTIME is defined by the generated source.
Repository “RNA” Code
![../images/495914_1_En_6_Chapter/495914_1_En_6_Fig1_HTML.jpg](../images/495914_1_En_6_Chapter/495914_1_En_6_Fig1_HTML.jpg)
The makesrna top-level directory contents
The primary source files in the top-level directory of the makesrna are RNA_types.h, RNA_enum_types.h, RNA_access.h, and RNA_define.h.2 This layout is similar to many Blender modules, in that there is an intern subdirectory containing the implementation of the module’s API and functions used internally by the module itself.
RNA_types.h
“RNA” types used directly outside of makesrna are defined in RNA_types.h . For example, PointerRNA is used extensively in the python module. However, StructRNA is only used indirectly via the PointerRNA type and therefore defined in [repository root]/source/blender/makesrna/intern/rna_internal_types.h.
RNA_enum_types.h
The EnumPropertyItem struct defined in RNA_types.h
RNA_access.h
Example of float property Data API accessors, prototyped in [repository root]/source/blender/makesrna/RNA_access.h
RNA_define.h
RNA_define.h is a set of function prototypes for creating instances of “RNA” structs used by both runtime and non-runtime portions of makesrna. These functions are named with an RNA_def_* prefix and implemented in [repository root]/source/blender/makesrna/intern/rna_access.c.
Client code of the prototyped API in RNA_define.h is a binary program compiled from [repository root]/source/blender/makesrna/intern/makesrna.c. makesrna.c is analogous to makesdna.c, from Chapter 2. It is responsible for generating source files for the runtime Data API backend, for each “DNA” type.
The intern Subdirectory Contents
makesrna’s intern subdirectory contains source files prefixed by rna_*. There is an rna_*.c source file for every “DNA” type that is wrapped by “RNA.” rna_*_api.c functions implement callbacks triggered when an object is updated by the Data API, usually in response to values changed in the user interface or Python API call via an operator.
There are two parts to these files, a runtime and a non-runtime portion. The runtime portion is used for additional Data API backend functionality not written to the generated source files. This code is responsible for “refine” functions. These functions return a StructRNA, which represents a “DNA” type refinement, for example, a point light.
The non-runtime calls RNA_def_* API, setting fields on either BlenderRNA or StructRNA objects for a “DNA” type. There is an upcoming example for the Light struct (“DNA”) later in the chapter. That example will discuss makesrna/intern/rna_light.c.
“RNA” Types
There are two categories of “RNA.” The first type is accessed only through the RNA_* API and implemented in rna_internal_types.c. The second type of “RNA” is defined in RNA_types.h and manipulated by functions defined outside of makesrna. For instance, PointerRNA is from RNA_types.h and therefore accessed by code external to makesrna—mostly in the python module. The following descriptions are of the main “RNA” types.
StructRNA
StructRNA from rna_internal_types.h. Used in both the “RNA” runtime and makesrna.c
![../images/495914_1_En_6_Chapter/495914_1_En_6_Fig2_HTML.jpg](../images/495914_1_En_6_Chapter/495914_1_En_6_Fig2_HTML.jpg)
Collaboration between StructRNA and PropertyRNA objects . Other aggregations are clipped
“RNA” is used to encapsulate information pertaining to “DNA,” as used in operators for data definitions and settings.
RNA_Light definition from [build root]/source/blender/makerna/intern/rna_light_gen.c. rna_Light_type is in boldface, as it is important for later discussion
ContainerRNA
The ContainerRNA data type implemented in [repository root]/source/blender/makesrna/intern/rna_internal_types.h. The next and prev fields point to StructRNA objects at the location of their “base” types, which are ContainerRNA objects. These are properties of an object’s underlying “DNA” type
BlenderRNA
The BlenderRNA implementation from [repository root]/source/blender/makesrna/intern/rna_internal_types.h
RNA_create() from [repository root]/source/blender/makesrna/intern/rna_define.c
Definition of RNA_BlendData
PointerRNA
PointerRNA type defined in [repository root]/source/blender/makesrna/RNA_types.h. The data field is a pointer to the “DNA” struct that the “RNA” is wrapping
![../images/495914_1_En_6_Chapter/495914_1_En_6_Fig3_HTML.jpg](../images/495914_1_En_6_Chapter/495914_1_En_6_Fig3_HTML.jpg)
Illustration of PointerRNA and its aggregation of ID struct
The PointerRNA defined for the Main struct ([repository root]/source/blender/makesrna/intern/rna_access.c). PointerRNA encapsulates “RNA” types used outside of the “RNA” API. RNA_BlendData is a variable defined by generated code in [build root]/source/blender/makesrna/intern/rna_main_gen.c
PropertyRNA
The PropertyRNA struct definition from [repository root]/source/blender/makesrna/intern/rna_internal_types.h. The identifier and srna fields are in boldface
The struct BoolPropertyRNA definition from [repository root]/source/blender/makesrna/intern/rna_internal_types.h
An EnumPropertyRNA object defined in [repository root]/source/blender/makesrna/intern/rna_light_gen.c. This is the rna_Light_type EnumPropertyRNA object shown as a property defined on RNA_Light (a StructRNA object)
Generated “RNA” Code
As we have been alluding to, there is a substantial amount of code in makesrna that is generated. The generated code is written to the build directory and mirrors the rna_*_.c files from the intern subdirectory of the repository’s makesrna module.
This code contains the appropriate “RNA” structs populated with values for extents, enumerations, and backend Data API functions. It is the RNA_* API that uses the functions in the generated code to directly interface “DNA.”
The makesrna.c file is compiled to an executable and generates rna_*_gen.c files during the build process. Is there a list specifying each file and “DNA” type to be wrapped by the struct definitions from the generated source?
RNAProcessItems and PROCESS_ITEMS[] from [repository root]/source/blender/makesrna/intern/makesrna.c. The RNAProcessItem for RNA_def_light is in boldface
Example of RNA_define.h’s API for creating “RNA” from Light “DNA”
Setting Blender “DNA” Using “RNA”
The Data API provides a way for operators to update Blender “DNA.” Operators use “RNA” properties to interface Blender “DNA.” “RNA” properties map to “DNA” fields they abstract. This section’s example is of an operator adding a point light object to a Blender scene. The example illustrates use of the Data API and its related “RNA” structs.
wmOperatorType
The wmOperatorType implementation, showing relevant “RNA” related fields. The srna field is shown in boldface. srna points to a StructRNA
The wmOperatorType struct contains groups of related “RNA” properties derived from the underlying “DNA” type it acts upon. The operator may either “get” or “set” these individual properties via the Data API.
Adding a Light
We just covered the preliminaries. We now discuss the data structures and function calls for extracting settings information in an operator using the Data API and “RNA.”
The Blender Python API function bpy.ops.object.light_add() creates a new light object. It is an operator invoked using bpy.ops (the operators submodule of bpy).
Operators are implemented in C code, but it is possible to write operators in Python as well. Python-based operators are run by the Blender Python extended interpreter. In this chapter, however, we show the Blender Python API calling a C based operator.
![../images/495914_1_En_6_Chapter/495914_1_En_6_Fig4_HTML.jpg](../images/495914_1_En_6_Chapter/495914_1_En_6_Fig4_HTML.jpg)
Call stack starting from pyop_call() in [repository root]/source/blender/python/intern/bpy_operator.c
Relevant “RNA”
In this example, the operator’s action simply creates a new point light. The pertinent attributes (“properties”) are ones associated with a point light. Only a subset of attributes are relevant in the “DNA” Light, for example, its area_shape field is not used.
The RNA_PointLight global variable defined in [build root]/source/blender/makesrna/intern/rna_light_gen.c. The cont field (ContainerRNA type) assignment is shown in boldface, along with the FloatPropertyRNA type rna_PointLight_energy, just one of the multiple “RNA” properties
Relevant “RNA” properties are stored by wmOperatorType’s srna field, a pointer to a StructRNA. Note that each of the “DNA” types has a set of global StructRNA variables defined in the “RNA” generated code. For the “DNA” type instance, Light from [repository root]/source/blender/makesdna/DNA_light_types.h has its StructRNA definitions in [build root]/source/blender/makesrna/intern/rna_light_gen.c. We just saw an example of this with RNA_PointLight. A StructRNA’s “RNA” properties are each represented by a PropertyRNA object. There is a “derived” PropertyRNA type for each “RNA” property: BoolPropertyRNA, IntPropertyRNA, etc.
Recall that there is no language mechanism in C that explicitly enables inheritance. It is approximated by including the base type as a header in the “derived” struct. We saw this with PyObject in Chapter 5.
The rna_PointLight_energy “RNA” property, from [build root]/source/blender/makesrna/intern/rna_light_gen.c. The associated “getter” and “setter” for this property are also shown. These accessor functions directly manipulate the Light “DNA” struct
The light object “types” are all eventually represented by the general Light struct (a “DNA” type). Each, however, is represented only by a subset of Light’s fields. Thus, the related properties for each light “type” (e.g., point, area, etc.) are chained together in the cont field of StructRNA, for access during an operator’s action.
The container of properties will be used to set the correct variables in Light. Recalling Listing 6-17, notice the container of properties in RNA_PointLight’s cont field.
Calling the Data API
Start of the operator in pyop_call() from [repository root]/source/blender/python/intern/bpy_operator.c. A PointerRNA is created
We see that a PointerRNA object is defined in a call to WM_operator_properties_create_ptr(), using the correct operator type ot, retrieved from WM_operatortype_find().
WM_operator_call_py() is passed the ptr variable with the appropriate StructRNA properties for the operator type obtained earlier by WM_operatortype_find().
wm_operator_invoke() from [repository root]/source/blender/windowmanager/intern/wm_event_system.c
Elided struct wmOperator defined in [repository root]/source/blender/makesdna/DNA_windowmanager_types.h
Snippet from the object_light_add_exec() definition, in [repository root]/source/blender/editors/object/object_add.c
ED_object_add_generic_get_opts() obtains settings required by wmOperator, using the Data API. Here, we see the Data API call RNA_float_get_array() for the location variable loc. The loc variable is gathered from the PointerRNA data field, which points to an ID (struct header) contained in the Object struct (not shown) of the Light “DNA” object
“DNA” via the blenkernel Module
For completion of an operator’s action, we look at ED_object_add_type(). This function is called by object_light_add_exec() and uses the BKE_* API. We do not trace the code to BKE_light_add(). However, this operator eventually inserts the new light into the appropriate “collection” (a linked list of objects using ListBase as the field type) in the Main struct. ED_object_add_type() passes settings garnered by the Data API and passes them to the blenkernel API. See Listing 6-24.
Up to this point in the call stack, we have wended through different modules. We can see the editors module plays a key role in operator actions. We will look more at the editors module in Chapter 7 and 8.
ED_object_add_type() from [repository root]/source/blender/editors/object/object_add.c. We now have the proper settings from the “RNA” and can use the copied data to write to “DNA.” Notice the CTX_* API from the blenkernel module, to get bmain, scene, and view_Layer from the bContext parameter
The python Module and Blender “RNA”
We just saw an example of a Blender Python API operator call. But how does the Blender Python API connect to the “RNA” Data API in the first place?
We already know how the Blender Python API extensions are created in the python module from Chapter 5. There, we had shown that each extension module is represented by a PyObject “derived” type, using PyTypeObject from the CPython library to define the classes and methods (or functions) that compose a Blender Python API module.
Now, we need to investigate the specific PyObject plus PyTypeObject derivatives with connections to Blender “RNA.” We will do this for the bpy.data Python API submodule, which provides access to the Main struct of the Blender “DNA.” (The mechanism for doing this is similar for other Blender Python API connections to Blender “RNA,” such as the bpy.ops.object.light_add() call.) For access to the Main “DNA” struct, we need pyrna_struct_getattro() and pyrna_struct_setattro()—their function definitions provided in [repository root]/source/blender/python/intern/bpy_rna.c.
Both are invoked by pyop_call(). The path to their execution is similar to the call stack in Figure 6-4. However, these functions need to be connected to the respective PyObject. How does this happen?
struct BPy_StructRNA from [repository root]/source/blender/python/intern/bpy_rna.h. The struct is elided to reveal the PointerRNA field, shown in boldface. We know struct BPy_StructRNA is a PyObject because it contains a PyObject_HEAD
![../images/495914_1_En_6_Chapter/495914_1_En_6_Fig5_HTML.jpg](../images/495914_1_En_6_Chapter/495914_1_En_6_Fig5_HTML.jpg)
The BPy_StructRNA collaboration diagram
We see that BPy_StructRNA really serves to hold the PointerRNA object, which encapsulates the underlying StructRNA. The StructRNA allows the execution path to lead to the backend Data API functions that access the Blender “DNA.” As shown previously in this chapter, we need to point the data field of PointerRNA , to its “DNA” object.
Wrapping the Main struct with a PointerRNA object and then creating the corresponding struct BPy_StructRNA to register as a PyObject. This will provide access to the embedded Python interpreter. This is performed by BPY_rna_module(), from [repository root]/source/blender/python/intern/bpy_rna.c
The struct BPy_StructRNA functions for access to the Main struct “DNA” are defined in [repository root]/source/blender/python/intern/bpy_rna.c. The tp_getattro and tp_setattro function pointer fields of PyTypeObject are used for pyrna_struct_getattro() and pyrna_struct_setattro(), respectively
Summary
This chapter covered “RNA” and the Data API. We have seen that the makerna module is different from other Blender modules. The purpose of makesrna is to implement wrappers for “DNA” types, so that operators can interface “DNA” using value ranges, accessor functions, etc.
Much of the code is generated from function calls using the makesrna API, that is, functions with the prefix RNA_def_*. The other makesrna API functions form the “RNA” Data API interface. They are prototyped in the RNA_access.h file.
Generated and repository source files, together, provide the backend for the Data API. This code performs the data manipulations on “DNA” structs.
Finally, we saw how the Blender Python API, as implemented in the python module, connects the PyObjects which represent the extended Blender CPython to the “RNA” interface of the Data API.