blender核心源碼部分之一
如之前的文章所說,下面三個部分是blender中最核心的代碼,第一次記錄會比較零散。
source/blender
source/creator
source/tools

C語言的基礎(chǔ)
????????在閱讀源碼過程中,有大量的宏定義。這一些在代碼預(yù)編譯階段就做了展開,給我們debug增加了難度,但是便于參數(shù)調(diào)節(jié)、增加運(yùn)行效率等等。這一些優(yōu)勢,反正我開始讀的時候是沒有感覺到的,希望之后可以感覺到。
????????簡單介紹一下,最常用的宏定義的一些內(nèi)容。
1. 簡單的可以分為帶參數(shù)、不帶參數(shù)的宏定義兩種。對于后者,比如
#define?PI?3.1415926
2. 我們把PI作為3.1415926的替代對象。對于前者,比如
#define?B(x,y)?x##y
3. 定義了B(x,y),提供類似函數(shù)的功能,但是在預(yù)編譯時而不是運(yùn)行時,就做到把x和y鏈接起來。常用的有四種特殊符號,#、##、#@、\
????????1.? #:?把#之后的內(nèi)容變成字符串;
????????2.? ##:將前后兩個對象拼接在一起,變成一個;
????????3.? #@:將后面一個內(nèi)容,變成單個字符,類似于加上單引號;
????????4.? \:?一行不夠,把下面一行內(nèi)容也作為#define的內(nèi)容
????????

????????第一次讀代碼,目前就想到哪里,就寫到哪里了;
????????我在使用中感覺到,需要Visual?Studio?和Visual?Studio?Code?配合,看源碼+調(diào)試會更方便。
主入口
????????從入口函數(shù)所在模塊開始?source/creator?里面內(nèi)容很少,從Visual?Studio的解決方案中也可以看到。

????????其中,buildinfo.c?這個文件,是在CMakeLists階段生成的。具體內(nèi)容如下所示,通過VSCode在整個項(xiàng)目中搜索關(guān)鍵字可以找到,在?build_files\cmake?中,將hash、date等內(nèi)容寫入到這個特殊c文件中。在編譯階段,這個c文件中的內(nèi)容就被讀取了。

下圖是cmake中的部分代碼

????????主入口函數(shù)在creator.c?中,這個文件不大,所以我們從頭開始看。
????????blender在各個文件都,都做了一些自己操作的封裝,比如像這種存粹是輸出字符串內(nèi)容到標(biāo)準(zhǔn)錯誤通道的例子;

????????fflush是為了清buffer,保證之前內(nèi)容先輸出完。否則會出現(xiàn)多次輸出順序與調(diào)用順序不一致的問題。類似的一些函數(shù),我第一次遇到的就介紹一下,相似的就不做記錄了。
????????隨后遇到的新面孔是一個在blender中非常常見的結(jié)構(gòu):一個函數(shù)指針,或者一個宏定義,加上一個函數(shù)指針或者宏定義作為參數(shù)。沒有做宏展開或者具體看指針指向的函數(shù)要干嘛之前,這種結(jié)構(gòu)真的是讓源碼讀者很無奈。這邊我第一次遇到,就展開介紹下。

????????MEM_set_error_callback?是一個函數(shù)指針,指向一個返回值為void,入?yún)楹瘮?shù)指針的函數(shù)。顯然在blender中,這個指針直接指向了?MEM_lockfree_set_error_callback?。?而MEM_lockfree_set_error_callback很明顯是在某個private的地方定義的函數(shù)。

????????搜了一圈看到,MEM_lockfree_set_error_callback?的唯一作用,是把入?yún)⒌暮瘮?shù)指針,傳給一個內(nèi)部定義的函數(shù)指針。所以轉(zhuǎn)了一圈,經(jīng)過3個調(diào)用函數(shù),main_callback_setup的作用,就是使得在creator.c中定義的函數(shù)?callback_mem_error,?傳給內(nèi)部的error_callback。


????????乍一看,是肯定會疑惑就這么一個操作為什么需要這么多次的函數(shù)調(diào)用呢?可能的理由是,這種多模塊之間的接口調(diào)用,都需要通過模塊之間相互暴露接口、通過接口訪問來實(shí)現(xiàn),所以中間就有這一些步驟,很合理。
????????到了主函數(shù)中,拋開變量的定義,首先做的是注冊在blender還沒有加載完全時就退出這種情況下的資源釋放函數(shù)。從我的角度看,BKE_blender_atexit_register?就是做這件事情了。

????????隨后通過 CommandLineToArgvW(GetCommandLineW(),?&argc) 來獲取命令行參數(shù),并提供給argv。
????????這是WIN32的獲取命令行參數(shù)的方式,在其他OS中直接通過argv就可以拿到參數(shù)了。隨后有一些為了特殊模式做的特殊初始化,比如對于debug模式要做從無鎖分配器到完全保護(hù)分配器的切換(具體意義是什么并不清楚)。對于日志信息的初始化,也在這部分做,日志信息是目前看到在blender的context初始化之前,最后一個初始化的內(nèi)容。這個很合理,畢竟日志模塊需要記錄blender的上下文以及其他模塊的各種日志信息。
????????在blender中,有幾個特殊的全局變量,上下文context是其中一個。在整個源碼中,都用大寫字母C來表示,確實(shí)是很隨意。而在做context內(nèi)存分配時候,則使用了blender內(nèi)建的MEM_callocN。這個分配函數(shù)所在的模塊為?blender\intern\guardedalloc?。blender\intern?與?blender\source?同級。在blender中這種自定義的內(nèi)存分配隨處可見,目的在于做類似單例的實(shí)現(xiàn)。以MEM_callocN為例,入?yún)⒎謩e為待分配字節(jié)數(shù)量,以及一個靜態(tài)字符串。被分配的內(nèi)存與這個靜態(tài)字符串綁定。
????????在上下文創(chuàng)建之后,準(zhǔn)備開始做各個子模塊的初始化了。看起來比較有意思,比如最開始初始化,就首先找到當(dāng)前自己所在完整路徑。這一系列初始化,都使用了blender自定義的內(nèi)存分配函數(shù),構(gòu)建了一堆“內(nèi)存空間”-“靜態(tài)字符串”?的映射。這部分內(nèi)容相對比較繁雜,也沒有深入看的意義??梢哉J(rèn)為,幾乎所有與啟動相關(guān)的、子系統(tǒng)相關(guān)的初始化,都在這邊執(zhí)行了。需要注意的是,如果是子模塊的初始化函數(shù)的話,子模塊內(nèi)部幾乎都還有一堆初始化要做。這塊我們先跳過。

????????在大量初始化之后,需要注冊程序退出時調(diào)用的回調(diào)函數(shù),以及在main最后做了一次是否為background執(zhí)行的判斷。一般的,對于background執(zhí)行方式,所有計(jì)算,都已經(jīng)在上述初始化之內(nèi),以及CPython中執(zhí)行完畢了,此時會直接退出。之所以能直接退出,是因?yàn)樗斜匾K初始化完成之后,background執(zhí)行的python腳本任務(wù),已經(jīng)在同步的CPython的執(zhí)行邏輯中,執(zhí)行完畢了。對于非后臺任務(wù)模式,需要以Editor模式來運(yùn)行(Editor模式相關(guān)的源碼,幾乎全部集中在??/source/blender/editors?目錄下,46+個模塊)。此時會進(jìn)入WM_Main(C)。?這個定義在?source\blender\windowmanager\intern\wm.c?中的函數(shù),才是正常我們看到的blender頁面的渲染循環(huán)的入口。

????????下圖是WM_Main的事件、渲染循環(huán)代碼,其中的邏輯,主要是為了做UI操作的監(jiān)聽,以及監(jiān)聽之后的事件觸發(fā),以及事件通知,渲染繪制更新。Blender的代碼嚴(yán)格遵循MVC模式。具體的,Model為Blender中實(shí)現(xiàn)的各種功能,View就是與用戶交互的界面,Controller就是接受用戶的交互并將需要的操作告訴Model,以及把Model計(jì)算結(jié)果反饋給View來展示給用戶。從循環(huán)中也可以看出來,Model的數(shù)據(jù)更新在前,View的更新在最后。

????????到此處為止,我們已經(jīng)把blender入口以及整個運(yùn)行tick部分的地方找到了,下面針對性查詢感興趣的模塊的源碼就行。

通過官方提供的文件夾結(jié)構(gòu)與說明,記錄以下我比較感興趣的一些模塊地址:
Object級別的修改器;?
/source/blender/modifiers/
/intern/dualcon/
渲染模塊
底層渲染
/source/blender/draw/
/source/blender/render/
?/intern/cycles/
渲染在editor部分的節(jié)點(diǎn)交互
/source/blender/editors/space_node/
/source/blender/nodes/
渲染與editor的交互
?/source/blender/editors/render/
UV Editing部分
?/source/blender/editors/uvedit/
Mesh級別的Edit操作
/source/blender/editors/mesh/