Blender
Created at 2017-06-29T23:43:55+09:00

Build system

Build from source:

$ mkdir out/Default
$ cd out/Default
$ mkdir -p _install
$ cmake -G Ninja -DPYTHON_VERSION=3.6 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$PWD/_install ../..
$ ninja -t browse # check build artifacts dependency for feeling overview
$ ninja install
$ ./_install/blender

File structure:

source/creator/ (main entrypoint)

source/blender/
  blenkernel/ (BKE)
  blenlib/    (BLI)
  modifiers/  (MOD)
  depsgraph/  (DAG)
  makesdna/, makesrna/ (Struct DNA)
  editors/    (?)
  nodes/      (?)
  python/     (?)
  ...

intern/
  cycles/ (? why is it not loaded)
  ghost/ (platform window system abstraction layer?)
  ...

extern/
  ...

Built time files

  • makesrna/intern/RNA_blender_cpp.h (all structure used in blender ?)
  • makesdna/intern/dna.c (DNAstr, file format ?)
  • release/datafiles/startup.blend.c (startup.blend in memory)

Build info

$ out/Default/bin/blender -v
Blender 2.78 (sub 5)
    build date: 2017-07-01
    build time: 11:03:42
    build commit date: 2017-06-29
    build commit time: 08:09
    build hash: a57a7975a16
    build platform: Linux
    build type: Release
    build c flags:  -Wall -Wcast-align -Werror=implicit-function-declaration -Werror=return-type -Werror=vla -Wstrict-prototypes -Wmissing-prototypes -Wno-char-subscripts -Wno-unknown-pragmas -Wpointer-arith -Wunused-parameter -Wwrite-strings -Wlogical-op -Wundef -Winit-self -Wnonnull -Wmissing-include-dirs -Wno-div-by-zero -Wtype-limits -Wformat-signedness -Wuninitialized -Wredundant-decls -Wshadow -Wno-error=unused-but-set-variable -Wimplicit-fallthrough=5  -fuse-ld=gold -fopenmp -std=gnu11   -msse -pipe -fPIC -funsigned-char -fno-strict-aliasing -msse2
    build c++ flags:  -Wredundant-decls -Wall -Wno-invalid-offsetof -Wno-sign-compare -Wlogical-op -Winit-self -Wmissing-include-dirs -Wno-div-by-zero -Wtype-limits -Werror=return-type -Werror=implicit-function-declaration -Wno-char-subscripts -Wno-unknown-pragmas -Wpointer-arith -Wunused-parameter -Wwrite-strings -Wundef -Wformat-signedness -Wuninitialized -Wundef -Wmissing-declarations -Wimplicit-fallthrough=5  -fuse-ld=gold -fopenmp -std=c++11   -msse -pipe -fPIC -funsigned-char -fno-strict-aliasing -msse2
    build link flags:
    build system: CMake

Overview

  • file loading
  • material and node editor
  • object editor
[ Data structure ]
Global (aka G)
'-* Main (linked list)
  '-' EvaluationContext
  '-' a bunch of list (ListBase) (e.g. wm, scene, object, ...)

bContext (aka C)
'-' wm
  '-' wmWindowManager
    '-2 wmWindow (windrawable and winactive)
    '-* windows
    '-* operators
  '-' wmWindow
  '-' bScreen
'-' data
  '-' Main
  '-' py_context

(NOTE: it's faster to understand if you inspect through Outliner Data-Blocks UI)
BlendFileData
'-' Main
'-' UserDef
'-' bScreen (UI data)
'-' Scene   (model data)
  '-' Depsgraph (TODO: is this the source of Main listBase's data ? or opposite (i.e, deps is is consructed from those objects data)?)
  '-' Object *camera
  '-' World *world
  '-' listBase base
  '-' Object *obedit
  '-' int layact (active layer)
  '-' ...

Base
'-' prev, next (doubly-linked Base together)
'-' int lay
'-' Object

Pointer > ID > Object
'-' PointerRNA
  '-' StructRNA
    '-' ??
  '-' id ?
  '-' data ?

SDNA
'-* name
'-* type
'-* struct

DEG::_depsnode_typeinfo_registry
'-* DepsNodeFactoryImpl<XXX> (e.g. IDDepsNode, ...)

SpaceType (e.g. SPACE_VIEW3D)
'-' listener
'-' keymap
'-* ARegionType (e.g. for example, main part, toolshelf, property setting area for 3d view)
  '-' draw
  '-' listener
  '-' keymap

wmOperatorType (e.g. OBJECT_OT_mode_set)
'-' exec
'-' poll

wmOperatorTypeMacro


[ Procedure ]
- main =>
  - CTX_create (alloc bContext)
  - DNA_sdna_current_init =>
    - g_sdna = DNA_sdna_from_data(DNAstr ..) =>
      - init_structDNA ..
  - BKE_blender_globals_init =>
    - G.main = BKE_main_new =>
      - alloc Main
      - DEG_evaluation_context_new => alloc EvaluationContext with DAG_EVAL_VIEWPORT
  - IMB_init =>
    - colormanagement_init => OCIO setup ..
  - BKE_modifier_init => modifier_type_init =>
    - INIT_TYPE(XXX) (e.g. Subsurf) => modifier_types[eModifierType_Subsurf] = &modifierType_Subsurf
  - DAG_init => DEG_register_node_types =>
    - DEG::_depsnode_typeinfo_registry = BLI_ghash_int_new
    - deg_register_base_depsnodes => DNTI_TIMESOURCE, DNTI_ID_REF
    - deg_register_component_depsnodes => ..
    - deg_register_operation_depsnodes => ..
  - main_args_setup (register arguments callbacks to be used along with separate 4 initialization steps)
  - BLI_argsParse(.. 1 ..) (e.g. help or debug option)
  - RNA_init => set prophash for each BLENDER_RNA.structs
  - RE_engines_init => BLI_addtail R_engines, &internal_render_type and internal_game_type
  - init_nodesystem =>
    - reigister_xxx
    - registerCompositNodes => ..
    - registerShaderNodes =>
      - register_node_type_sh_xxx (e.g. bsdf_diffuse) =>
        - static bNodeType ntype and nodeRegisterType(&ntype)
    - registerTextureNodes => ..
  - init_def_material => BKE_material_init => intialize Material global (defmaterial)
  - BLI_argsParse(.. 2 ..), BLI_argsParse(.. 3 ..)
  - WM_init =>
    - wm_ghost_init =>
      - GHOST_CreateSystem => ... new GHOST_SystemX11 (TODO: xwindow, glx setup ?)
      - (register ui event callback "ghost_event_proc" to ghost window system)
    - wm_operatortype_init =>
      - WM_operatortype_append(WM_OT_window_xxx) (e.g. WM_OT_window_close) ..
    - DAG_editors_update_cb => DEG_editors_set_update_cb => DEG_editors_set_update_cb
    - ED_spacetypes_init =>
      - ED_spacetype_xxx (e.g. ED_spacetype_view3d) =>
        - alloc SpaceType and its ARegionTypes
        - BKE_spacetype_register
      - ED_operatortypes_xxx (e.g. ED_operatortypes_object) =>
        - WM_operatortype_append(OBJECT_OT_xxx) (e.g. OBJECT_OT_mode_set) ..
          - wmOperatorType *ot = alloc
          - OBJECT_OT_mode_set(ot) =>
            - ot->name = "Set Object Mode", ot->idname = "OBJECT_OT_mode_set" ..
      - for each registered SpaceType, SpaceType.operatortypes (e.g. view3d_operatortypes) =>
        - WM_operatortype_append(VIEW3D_OT_xxx) (e.g. VIEW3D_OT_rotate) ..
      - ED_render_internal_init => ..
    - wm_homefile_read =>
      - BKE_blendfile_read_from_memory (load from buffer statically allocated by startup.blend.c) =>
        - BlendFileData *bfd = BLO_read_from_memory =>
          - FileData *fd = blo_openblendermemory =>
            - blo_decode_and_check =>
              - decode_blender_header => read "BLENDER"
              - read_file_dna => look for DNA1 from bhead and DNA_sdna_from_data =>
                - alloc SDNA *sdna and init_structDNA (construct SDNA from raw buffer)
          - blo_read_file_internal =>
            - alloc BlendFileData
            - bfd->main = BKE_main_new
            - iterate BHead from FileData e.g.
              - read_global => read_struct(.. "Global") => DNA_struct_reconstruct(current dna, loading file's dna, .. raw buffer)
              - read_libblock =>
                - ID *id = read_struct(.. "lib block") => ..
                - switch by id->name e.g.
                  - (case ID_WM) direct_link_windowmanager(fd, (wmWindowManager *)id) =>
                    - link_list(fd, &wm->windows)
                    - ..
                  - direct_link_screen ..
              - ? ..
        - BLO_update_defaults_startup_blend (fix up some values ?)
        - setup_app_data(C, bfd ..) =>
          - G.main = bfd->main
          - CTX_data_main_set(C, G.main)
          - CTX_wm_manager_set(C, G.main->wm.first)
          - CTX_wm_screen_set(C, bfd->curscreen)
          - CTX_data_scene_set(C, bfd->curscene)
          - BPY_context_update =>
            - BPy_SetContext => __py_context = C (global var)
            - bpy_import_main_set => bpy_import_main = Main (again global var)
          - BKE_scene_set_background =>
            - DAG_scene_relations_rebuild => DAG_scene_relations_update => dag_scene_build =>
              - build_dag => ..
              - ? ..
      - WM_check =>
        - wm_window_ghostwindows_ensure =>
          - wm_window_ghostwindow_add =>
            - GHOST_WindowHandle ghostwin = GHOST_CreateWindow
            - wm->windrawable = win
            - GPU_init =>
              - gpu_extensions_init => setup GPUGlobal (GG) by quering glGetIntegerv
              - gpu_codegen_init => ..
            - win->ghostwin = ghostwin
            - wm_window_swap_buffers
            - GPU_state_init => ..
          - WM_event_add_keymap_handler ..
        - ED_screens_initialize => ED_screen_refresh =>
          - win->screen->mainwin = wm_subwindow_open ..
          - for each ScrArea *sa in win->screen->areabase
            - ED_area_initialize => ? ..
          - win->screen->context = ed_screen_context
        - wm->initialized |= WM_INIT_WINDOW
      - wm_file_read_post =>
        - ED_editors_init (small fixup ?)
        - DAG_on_visible_update => dag_current_scene_layers =>
          - alloc DagSceneLayer dsl
          - dsl->layer = BKE_screen_visible_layers ..
          - dag_scene_flush_layers => ..
        - WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL) => ?
    - ED_spacemacros_init =>
      - ED_operatormacros_xxx (e.g. ED_operatormacros_mesh) =>
        - "MESH_OT_loopcut_slide", ..
    - BPY_python_start =>
      - PyImport_ExtendInittab(bpy_internal_modules) (e.g. bgl, bmesh, etc..)
      - Py_Initialize, PyEval_InitThreads
      - BPy_init_modules =>
        - PyList_Insert(sys_path .. py_modpath) (put scripts/modules into python load path)
        - PyObject *mod = PyModule_New("_bpy")
        - PyDict_SetItemString(PyImport_GetModuleDict(), "_bpy", mod) (inject _bpy into sys.modules)
        - .. defining interface e.g. app, context, etc..
        - bpy_import_test("bpy") => PyImport_ImportModuleLevel("bpy" ..) (load scripts/modules/bpy/__init__.py)
      - bpy_import_init (monkey patch builtin module's import)
  - WM_keymap_init =>
    - wm_window_keymap =>
      - WM_keymap_add_item(keymap, "WM_OT_save_mainfile", SKEY, KM_PRESS, KM_CTRL, 0) ..
    - ED_spacetypes_keymap =>
      - ED_keymap_object =>
        - WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0) ..
      - ..
    - WM_keyconfig_update
  - main_args_setup_post => BLI_argsParse(.. 4, arg_handle_load_file ..)
  - WM_main =>
    - while (1)
      - (TODO: where does "dependency graph" update propagation come in ?)
      - wm_window_process_events =>
        - (if no event sleep 5ms)
      - wm_event_do_handlers => ?
      - wm_event_do_notifiers => ?
      - wm_draw_update => ?

TODO:

  • deg (DAG_scene_relations_rebuild)
    • can we see on ui ? (what exactly does this involve ? (object, node, mesh, modifier ?))
  • node (init_nodesystem)

Editor, Space, and Operator

follow outliner as an example:

[ Data structure ]
SpaceType
'-' new, init, keymap, listener ..
'-* ARegionType
  '-' init, keymap, listener, draw

ScrArea
'-' handlers ?
'-* AZone ??
'-* ARegion
  '-' View2D (ui state info)
  '-' ARegionType
  '-' panels, handlers ?

uiBlock
'-' Panel
'-* uiBut (buttons)
  '-' uiButHandleFunc
  '-' tip
  '-' icon ..


[ Initialization ]
- WM_init => ED_spacetypes_init => ED_spacetype_outliner => ..

- WM_check => ED_screens_initialize => ED_screen_refresh => ED_area_initialize =>
  - outliner_main_region_init (as ar->type->init) => ..


[ Interaction (Event handler and Operator) ]

(keymap: "return" key (OUTLINER_OT_item_openclose))
- wm_event_do_handlers => wm_handlers_do => wm_handlers_do_intern =>
  - wm_handler_operator_call => wm_operator_invoke =>
    - outliner_item_openclose (as op->type->invoke) =>
      - ARegion *ar = CTX_wm_region(C)
      - SpaceOops *soops = CTX_wm_space_outliner(C)
      - do_outliner_item_openclose => recursively call and finally tselem->flag &= ~TSE_CLOSED
      - ED_region_tag_redraw(ar) =>
        - ar->do_draw |= RGN_DRAW
        - memset ar->drawrect

(keymap: click "+"/"-" icon (OUTLINER_OT_item_activate))
- outliner_item_activate (as ) => outliner_item_do_activate =>
  - do_outliner_item_activate =>
    - (if click location is on top of "+"/"-" icon)
      - tselem->flag &= ~TSE_CLOSED
  - ED_region_tag_redraw ..

(python: bpy.ops.outliner.item_openclose)
I thought it would work but actually if you run it from in-blender console, poll fails since outliner space's focus is required.

(ui click: click small "eye" button (or equivalent operator OUTLINER_OT_visibility_toggle "v"))
- draw_outliner => outliner_draw_restrictbuts =>
  - UI_but_func_set(bt, restrictbutton_rend_cb, scene, ob) =>
    - but->func = func ..

- wm_event_do_handlers => wm_handlers_do => wm  _handlers_do_intern =>
  - wm_handler_ui_call => ui_region_handler => ui_apply_but_funcs_after =>
    - restrictbutton_view_cb (as after.func) =>
      - ED_base_object_select(.., BA_DESELECT) => ...
        - base->flag &= ~SELECT
        - base->object->flag = base->flag
      - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene)    


[ Drawing ]
- wm_draw_update =>
  - ED_screen_refresh => ED_area_initialize => outliner_main_region_init ..
  - wm_method_draw_triple => ED_region_do_redraw => ED_region_do_draw =>
    - outliner_main_region_draw (as ar->draw) =>
      - draw_outliner =>
        - outliner_build_tree => ..
        - UI_block_begin => alloc uiBlock *block
        - outliner_draw_tree =>
          - for each TreeElement *te in SpaceOops.tree, outliner_draw_tree_element =>
            - UI_fontstyle_draw_simple(.. te->name) =>
              - BLF_draw => BLF_draw_ex => blf_glyph_render =>
                - glBindTexture
                - blf_texture_draw => glBegin(GL_QUADS), glTexCoord2f and glVertex2f
            - outliner_draw_tree_element (recursively)
        - outliner_draw_restrictbuts =>
          - uiBut *bt = uiDefIconButR_prop(block ..) =>
            - ui_def_but_rna => ui_def_but =>
              - alloc uiBut
              - BLI_addtail(&block->buttons, but)
          - UI_but_func_set(bt, restrictbutton_sel_cb ..)
        - UI_block_end => ..
        - UI_block_draw =>

Addon

- WM_init => .. => PyImport_ImportModuleLevel("bpy") =>
  - (python modules/bpy/__init__.py)
    - sys.path.extend([ .. "addons", "modules" ])
    - utils.load_scripts =>
      - for each _script_module_dirs (i.e. "modules" and "startup") =>
        - modules_from_path => _test_import => __import__
        - test_register =>  register_module_call =>
          - call getattr(mod, "register", None)
      - addon_utils._initialize =>
        - for each bpy.context.user_preferences.addons =>
          - enable =>
            - mod = __import__(module_name)
            - mod.register

3D view drawing and Cycles

[ Data structure ]

View3D
'-' drawtype (e.g. OB_BOUNDBOX, OB_WIRE, OB_SOLID, OB_TEXTURE, OB_MATERIAL or OB_RENDER)
'-' ..

RegionView3D
'-' RenderEngine
  '-' RenderEngineType
    '-' view_update, view_draw, ...
    '-' ExtensionRNA
      '-' call (e.g. bpy_class_call)
  '-' Render

BlenderSession
'-' Session
  '-' session_thread
  '-' Progress (holds update_cb and cancel_cb with mutexes)
  '-' Device
  '-' RenderBuffers
  '-' SessionParams
  '-' DisplayBuffer
    '-' device_vector<uchar4> rgba_byte
    '-' CPUDevice (< Device)
      '-' TaskPool
      '-' KernelGlobals
      '-' KernelFunctions .. (e.g. path_trace_kernel, shader_kernel ..)
'-' Scene
'-' BlenderSync
  '-' object_map (id_map<ObjectKey, Object>)
  '-' mesh_map ..
'-' BL::RenderEngine, BL::BlendData, .. (reference to blender's data structure)

TaskScheduler (singleton. it could be assigned task from multiple TaskPool_s)
'-* thread (as vector<thread*> threads)
  '-' pthread_t
  '-' function<void(void)> run_cb_
'-' list<Entry> queue

Object < Node


[ Procedure ]

({"_cycles", CCL_initPython} is listed in _inittab bpy_internal_modules[])
- CCL_initPython =>
  - CCL_python_module_init =>
    - PyObject *mod = PyModule_Create(&ccl::module) (aka "_cycles" module)

(cycles render registration from scripts/addons/cycles/__init__.py)
- class CyclesRender(bpy.types.RenderEngine) ..
- bpy.utils.register_class(CyclesRender) =>
  - (c) pyrna_register_class =>
    - StructRNA srna = pyrna_struct_as_srna(py_class ..)
    - StructRegisterFunc reg = RNA_struct_register(srna) ..
    - rna_RenderEngine_register(.. "CyclesRender" ..) (as reg) =>
      - RenderEngineType *et = alloc
      - et->ext = ..
      - et->view_update = engine_view_update
      - et->view_draw = engine_view_draw ..
      - BLI_addtail(&R_engines, et)

(drawing)
- view3d_main_region_draw =>
  - View3D *v3d = CTX_wm_view3d(C)
  - (for non-render mode (e.g. solid, wireframe, material etc ..))
    - view3d_main_region_draw_objects =>
      - view3d_draw_objects =>
  - (for render mode)
    - view3d_main_region_draw_engine =>
      - (first time)
        - RenderEngineType *type = RE_engines_find("CYCLES") => ..
        - RenderEngine *engine = RE_engine_create_ex(type, true) =>
          - alloc RenderEngine
          - engine->flag |= RE_ENGINE_USED_FOR_VIEWPORT
        - engine_view_update (as type->view_update) =>
          - PointerRNA ptr
          - RNA_pointer_create(NULL, engine->type->ext.srna, engine, &ptr) => ?
          - FunctionRNA *func = rna_RenderEngine_view_update_func (holds method name "view_update")
          - bpy_class_call (as engine->type->ext.call) =>
            - PyTypeObject *py_class = RNA_struct_py_type_get(ptr->type) ("CyclesRender" class)
            - PyObject *srna = pyrna_struct_CreatePyObject(ptr)
            - PyObject *item = PyObject_GetAttrString((PyObject *)py_class, RNA_function_identifier(func)) (i.e. "view_update" method)
            - PyObject_Call(item ..) (i.e. CyclesRender.view_update) =>
              - (python)
                - engine.create => engine.session = _cycles.create =>
                  - (cpp) ccl::create_func =>
                    - BlenderSession *session = new BlenderSession
                    - BlenderSession::create => create_session =>
                      - scene = new Scene =>
                        - new Camera, Film, Background, ..
                        - ShaderManager::create
                      - session = new Session =>
                        - TaskScheduler::init(num_threads) =>
                          - new thread(.. TaskScheduler::thread_run ..) => pthread_create
                        - Device::create =>
                          - device_cpu_create => new CPUDevice
                        - new RenderBuffers(device)
                        - new DisplayBuffer(device ..)
                      - session->progress.set_update_callback and set_cancel_callback (TODO: when does blender call them ?)
                      - sync = new BlenderSync
                      - BlenderSync::sync_view => ?
                      - BlenderSync::sync_data =>
                        - sync_objects =>
                          - (some big loop)
                            - BL::Object b_ob = ..
                            - Object *object = sync_object(b_ob ..) =>
                              - object_map.sync(Object ** ..) (id_map::sync) => new Object ..
                              - sync_mesh
                              - ..
                    - Session::start => session_thread = new thread(... Session::run ..) (SEE BELOW)
                - engine.update => _cycles.sync(engine.session) =>
                  - (cpp) ccl::sync_func => BlenderSession::synchronize => Blender::sync_data, sync_view ..
      - engine_view_draw (as type->view_draw) =>
        - (similar to engine_view_update flow) .. => call CyclesRender.view_draw =>
          - (python) engine.draw => _cycles.draw(engine.session ..) =>
            - (cpp) ccl::draw_func => BlenderSession::draw =>
              - BufferParams buffer_params = BlenderSync::get_buffer_params => ?
              - DeviceDrawParams draw_params
              - Session::draw(buffer_params, draw_params) =>
                - draw_cpu => DisplayBuffer::draw =>
                  - device_memory& rgba = rgba_data() => rgba_byte
                  - Device::draw_pixels(rgba ..) =>
                    - pixels_copy_from => (noop for cpu mode)
                    - glDrawPixels(.. rgba.data_pointer)


(session thread)
- Session::run =>
  - load_kernels => (almost noop for cpu mode)
  - run_cpu =>
    - (loop until progress.get_cancel or no_tiles = !tile_manager.next())
      - update_scene => ..
      - render =>
        - DeviceTask task(DeviceTask::RENDER) ..
        - CPUDevice::task_add(task) =>
          - DeviceTask::split(tasks) => ..
          - new CPUDeviceTask => run = function_bind(.. CPUDevice::thread_run ..)
          - TaskPool::push => TaskScheduler::push =>
            - TaskPool::num_increase
            - TaskScheduler::queue.push_front
            - TaskScheduler::queue_cond.notify_one
      - CPUDevice::task_wait => TaskPool::wait_work =>
        - while num != 0
          - num_cond.wait (wait until some worker thread TaskPool::num_decrease)
  - Progress::set_update => ..


(worker threads)
- TaskScheduler::thread_run =>
  - while
    - thread_wait_pop =>
      - queue_cond.wait (wait until session thread's queue_cond.notify_one)
      - entry = queue.front() and pop_front
    - CPUDeviceTask::run (as entry.task->run) =>
      - CPUDevice::thread_run =>
        - thread_render, thread_film_convert, or thread_shader (THESE DETAIL WILL BE ANOTHER POST)
    - delete entry.task
    - TaskPool::num_decrease => if (num == 0) num_cond.notify_all

(pixel level updates (aka DisplayBuffer::rgba_byte))
- Session::tonemap =>
  - DeviceTask task(DeviceTask::FILM_CONVERT)
  - task.rgba_byte = display->rgba_byte.device_pointer

- thread_film_convert =>
  - convert_to_byte_kernel()(&kernel_globals, (uchar4*)task.rgba_byte ..) =>
    - kernel_film_convert_to_byte =>
      - ..
      - *rgba = byte_result

Reference