原文鏈接:https://blog.csdn.net/zzx023/article/details/85319733
渲染方面優化主要集中在如何降低draw call上,draw call越多,渲染的壓力也就越大,對應的幀率可能就會下降,正常情況下如果draw call超過100就有可能帶來卡頓,所以要注意這方面的優化
自動圖集批處理
這方面相關的官方文檔比較少,可以參考:BMFont 與 UI 合圖自動批處理,但需要注意,這個文檔比較舊了,現在新版本的情況都沒有更新上去,只能參考一下。
draw call是openGL的描繪次數
一個簡單的openGL的繪圖次序是:設置顏色→繪圖方式→頂點座標→繪制→結束。
每幀都會重復以上的步驟。這就是一次draw call
通常每一張圖片都是一個紋理,一次draw call
如果有兩個紋理,那么就需要兩次draw call
自動圖集批處理所做的事情就是將兩張圖片放在一張紋理里面進行繪制,這樣的話每幀就只有1個draw call
目前creator的自動圖集批處理機制為,根據節點renderCmd的順序,將相鄰順序的所需紋理放入到同一個批次進行處理。實際表現的話大致可以理解為節點樹中相鄰的節點會進入同一批次。
例如下面這樣,場景中只有一個單色精靈時,它的draw call就為1。(這里解釋一下為什么左下角顯示為2,這是因為左下角的信息顯示也需要一次draw call,本文后面所說的drawcall都是去掉這個fps顯示draw call的數量)
下面這個渲染了2個紋理,3個精靈節點,同樣draw call也是1,這就是因為自動圖集批處理將2個紋理進行了合並處理,將原本需要2個draw call才能完成的事情合並成1個draw call
以上就是關於draw call和自動圖集批處理的簡單介紹。
那在實際的項目過程中,既然已經有了自動圖集批處理的機制,為何我們的draw call還居高不下?
這是因為並不是所有的紋理都可以放到自動圖集批處理中。
使用自動圖集批處理有幾個限制:
1、單圖尺寸小於512512並且大於88
2、渲染buffer必須為MeshBuffer類型的資源才能批處理
3、對節點部分特殊的操作不能進入批處理
第1點比較好理解,第2點的話需要各位自己去看引擎關於各個renderComponent的源碼了。
像bmfont label和sprite都是MeshBuffer,所以它們可以進入到批處理中。而像9ScaleSprite,Spine這些所使用的為QuadBuffer,因此沒辦法進行批處理。
上面記住第1條就好,其他的感興趣自己可以去研究研究,這里總結了一個表:
在creator 2.x(2.0~2.0.6)的版本中,支持Bmfont Label和Sprite進入批處理,其他的節點或者操作會打斷批處理。這里所說的Color和Opacity是指node節點上的color屬性或者是opacity屬性不相同的話,就算使用同一張圖片資源,也會打斷批處理。
在creator 2.1版本和2.0.7(公測版)中,Bmfont,Sprite,9ScaleSprite,Color,Opacity都會進行自動批處理。
例如將第二個節點的node color改為(255,0,0),這種情況下,在2.0.5的版本中將會打斷批處理,從而draw call變為3
因此想要優化我們的draw call,就必須盡量讓我們的節點都能夠進入到自動圖集批處理中
例如我們的一個icon,它由邊框,icon圖標,等級標簽,名字標簽組成。如果按照下面的節點結構,我們的批處理將會被打斷。
而如果是下面這樣的結構,至少我們的icon和邊框(iconBg節點)就可以合並到一起進行批處理
以上就是自動圖集批處理在項目渲染優化中需要注意的地方,在了解了具體機制后結合實際項目情況進行處理。
利用RenderTexture減少Draw Call
通過官方文檔我們知道,利用Camera和RenderTexture可以完成截圖功能
其實對於一些不需要實時更新的UI,我們也可以利用RenderTexture和Camera去實現降低draw call 的效果。
例如商城界面,或者是背包界面。我們需要顯示很多的icon,這些icon可能包含icon圖標、外框、等級或者數量、價格標簽、名字標簽等等。假設我們一個icon的prefab是這樣:
由於一些顯示上的需求,沒辦法必須得將批處理打斷掉。比如這個商城界面需要顯示20個這樣的圖標:
每個icon就是4個draw call,20個圖標就是80個draw call,再加上界面上其他元素的UI,算下來可能就要100+的draw call,這樣子在一些低端機上必然會很卡。
這時我們利用RenderTexture和Camera對節點進行快照,將獲取到的快照保存下來,使用的時候利用sprite以圖片精靈的形式進行顯示,而不是之前的多節點prefab。這樣的話整個draw call可以降低到非常低的情況:
從之前的80個draw call降低到了只有1個draw call。
是不是非常的有效果!
但要注意的是,這個方法的局限性也比較大。適用於刷新率不高,或者刷新情況比較固定的顯示。
例如一個icon可以被選中,那么我們就需要一個未選中的快照緩存和一個選中的快照緩存。將這兩個緩存通過DrawTextureAt放到一張RenderTexture上。
下面是代碼,大家可以參考:
cc.Class({ extends: cc.Component, properties: { camera : cc.Camera, layout : cc.Node, icon : cc.Node }, start () { //創建節點快照緩存
let renderTexture = new cc.RenderTexture(); let gl = cc.game._renderContext; let width = 120; let height = 130; renderTexture.initWithSize(width, height, gl.STENCIL_INDEX8); this.camera.targetTexture = renderTexture; this.camera.render(); //利用緩存顯示UI
for (let i = 0; i < 20; i++) { var node = new cc.Node(); node.addComponent(cc.Sprite); node.getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(renderTexture); node.parent = this.layout; } //刪除不用的節點
this.camera.targetTexture = null; this.camera.enabled = false; this.icon.active = false; }, });
參考demo下載地址:https://pan.baidu.com/s/1L1x3MRZ9q3b0l_m2lWN5ng 提取碼: qafm
請參考demo中的RenderTexture2場景代碼