全文共5000+字缆镣,分為8個章節(jié)踊兜,由本人歷時一周整理而來兽掰。由于篇幅問題芭碍,將本文分為8個章節(jié)分開發(fā)布。全文 (不) 詳細(xì)描述了cocoscreator 引擎的2.40版本中孽尽,web平臺的js部分引擎的渲染流程窖壕。請將文章配合源碼一起食用!
?由于我尚在學(xué)習(xí)引擎源碼中杉女,文章可能有不正確的部分瞻讽,所以我會不斷更新內(nèi)容。如有錯誤或補(bǔ)充熏挎,請留言交流速勇!
全部章節(jié)鏈接:
三 RenderFlow 的運(yùn)行邏輯
RenderFlow : 渲染流,用以遍歷場景下所有節(jié)點(diǎn)婆瓜,根據(jù)每個節(jié)點(diǎn)的_renderFlag , 處理節(jié)點(diǎn)的位置快集,顏色,透明度廉白,更新并渲染。
3.1 性能優(yōu)化
在v1.x版本中乖寒,每次渲染都會進(jìn)行很多動態(tài)判斷猴蹂,需要去判斷每個節(jié)點(diǎn)是否需要更新位置矩陣,是否需要渲染楣嘁,在這些過程中會有很多無用分支判斷磅轻,消耗性能。
所以在v2.x版本中逐虚,RenderFlow根據(jù)渲染過程中調(diào)用的頻繁度劃分出多個渲染狀態(tài)聋溜,比如 Transform,Render叭爱,Children 等撮躁,而每個渲染狀態(tài)都對應(yīng)了一個函數(shù)。在 RenderFlow 的初始化過程中买雾,會預(yù)先根據(jù)這些狀態(tài)創(chuàng)建好對應(yīng)的渲染分支把曼,這些分支會把對應(yīng)的狀態(tài)依次鏈接在一起。在渲染前會更新該節(jié)點(diǎn)的_renderFlag ,在渲染該節(jié)點(diǎn)時就可以直接根據(jù) _renderFlag的值漓穿,進(jìn)行相應(yīng)分支的處理嗤军,不用進(jìn)行多余的狀態(tài)判斷。
例如一個節(jié)點(diǎn)在當(dāng)前幀需要更新矩陣晃危,以及需要渲染自己叙赚,那么這個節(jié)點(diǎn)會更新他的 flag 為
node._renderFlag = RenderFlow.FLAG_TRANSFORM | RenderFlow.FLAG_RENDER。
更加詳細(xì)的內(nèi)容可見文末的相關(guān)鏈接中 : RenderFlow的性能優(yōu)化.
3.2 RenderFlow 內(nèi)的鏈?zhǔn)椒椒ǖ膭?chuàng)建與調(diào)用
RenderFlow中根據(jù) _renderFlag 獲取渲染流的代碼如下:
function getFlow (flag) {
let flow = null;
let tFlag = FINAL;
while (tFlag > 0) {
if (tFlag & flag)// 如果flag標(biāo)識匹配,則添加新的渲染流
flow = createFlow(tFlag, flow);// 需要把上一步創(chuàng)建flow傳入震叮,作為子流
tFlag = tFlag >> 1;// 標(biāo)志右移一位
}
return flow;
}
createFlow() 中會根據(jù)flag創(chuàng)建對應(yīng)的渲染流沿量,并加入鏈中,代碼如下:
function createFlow (flag, next) {
let flow = new RenderFlow();
flow._next = next || EMPTY_FLOW;// 將本次創(chuàng)建的flow加入鏈表首部
// 根據(jù)不同的flag設(shè)置不同的處理方法
switch (flag) {
case DONOTHING: flow._func = flow._doNothing; break;
case BREAK_FLOW: flow._func = flow._doNothing; break;
case LOCAL_TRANSFORM: flow._func = flow._localTransform; break;
case WORLD_TRANSFORM: flow._func = flow._worldTransform; break;
case OPACITY: flow._func = flow._opacity; break;
case COLOR: flow._func = flow._color; break;
case UPDATE_RENDER_DATA: flow._func = flow._updateRenderData; break;
case RENDER: flow._func = flow._render; break;
case CHILDREN: flow._func = flow._children; break;
case POST_RENDER: flow._func = flow._postRender; break;
}
return flow;
}
RenderFlow是根據(jù)node節(jié)點(diǎn)上的_renderFlag 來進(jìn)行不同的渲染流程冤荆,所以當(dāng)node節(jié)點(diǎn)上的位置朴则,顏色,透明度等參數(shù)改變后钓简,需要同步修改_renderFlag乌妒。這樣在渲染時會去根據(jù)flag處理對應(yīng)的流程。
3.3 詳解 RenderFlow 的不同操作
RenderFlow根據(jù) _renderFlag 創(chuàng)建了鏈?zhǔn)戒秩玖魍獾耍鱾€不同的FLAG對應(yīng)的方法撤蚊,都做了些什么,下面會詳細(xì)說明损话。
- _localTransform 方法
更新本地坐標(biāo)矩陣侦啸。(Tips:節(jié)點(diǎn)的位置通過本地坐標(biāo)矩陣和世界坐標(biāo)矩陣管理,通過矩陣叉乘來進(jìn)行高效的坐標(biāo)轉(zhuǎn)換丧枪,具體內(nèi)容待繼續(xù)學(xué)習(xí)了解光涂。。拧烦。) - _worldTransform 方法
更新世界坐標(biāo)矩陣忘闻。 - _opacity 方法
處理透明度。 - _color 方法
更新 renderCompent 的顏色 - _updateRenderData 方法
更新渲染數(shù)據(jù)恋博,調(diào)用 Assembler 里的 updateRenderData 方法齐佳,主要是更新uv和頂點(diǎn)數(shù)據(jù)。 - _render 方法
_proto._render = function (node) {
let comp = node._renderComponent;
comp._checkBacth(_batcher, node._cullingMask);
comp._assembler.fillBuffers(comp, _batcher);
this._next._func(node);
};
調(diào)用 RenderComponent 的 _checkBacth 檢測合批债沮。
調(diào)用 Assembler 的 fillBuffers 填充數(shù)據(jù)炼吴。
- _children 方法
遍歷子節(jié)點(diǎn)進(jìn)行子節(jié)點(diǎn)的渲染流程。 - _postRender 方法
(暫時不理解 todo)
相關(guān)鏈接
RenderFlow的性能優(yōu)化:http://docs.cocos.com/creator/manual/zh/advanced-topics/render-flow.html#
自定義渲染合批之自定義頂點(diǎn)格式: https://forum.cocos.org/t/demo/95087
自定義RenderFlow疫衩,處理背包等場景下drawcall過多:https://forum.cocos.org/t/ui/80026
材質(zhì)系統(tǒng):https://docs.cocos.com/creator3d/manual/zh/material-system/overview.html