成年做羞羞的視頻網(wǎng)站微信廣告投放平臺(tái)
UGUI合批
- UGUI合批規(guī)則概述
- UGUI性能查看工具
- 合批部分的特殊例子
- 一個(gè)白色image、藍(lán)色image覆蓋了Text,白色image和Text哪個(gè)先渲染
- Mask合批
- Mask為什么會(huì)產(chǎn)生兩個(gè)drawcall
- Mask為什么不能合批
- Mask注意要點(diǎn)
- RectMask2D
- 為什么RecMask2D比Mask性能更好
- 主要代碼
- RectMask2D注意要點(diǎn)
- 根據(jù)應(yīng)用場(chǎng)景選擇使用哪個(gè)(Mask、RectMask2D)
- 只需要一個(gè)遮罩
- 需要多個(gè)遮罩,遮罩下有多個(gè)子物體
UGUI合批規(guī)則概述
-
遍歷所有UI元素
-
根據(jù)深度Depth(優(yōu)先級(jí)最高,-1表示不用渲染)、材質(zhì)ID、圖片ID、渲染順序?qū)λ蠻I進(jìn)行排序
-
進(jìn)行合批處理,比如UI1和UI2(兩者必須是緊挨著的)是相同材質(zhì)、相同圖片就可以進(jìn)行合批,如果UI1和UI2之間有一個(gè)中間層UI3(根UI1和UI2材質(zhì)不同),就會(huì)打斷合批
- Text必須文字的網(wǎng)格覆蓋到image上面才算相交,Rect Transform框框相交不算
- 白色image的深度為0,最先渲染,然后判斷Text是否相交,再判斷材質(zhì)ID和圖片ID是否相同,如果相同則可以合批,因?yàn)椴幌嗤?#xff0c;所以Text深度為白色image+1為1(如果Text底下有很多image,則取最大的深度+1)
- 紅色image因?yàn)榈紫掠蠺ext和白色image,所以計(jì)算出兩個(gè)深度值分別1和0,取最大值加1,就是2
- 黃色和藍(lán)色image會(huì)進(jìn)行合批測(cè)試操作,深度值都是2(這里只是測(cè)試是否能合批,并還沒有真正合批)
- 最后會(huì)得到一個(gè)排序數(shù)組list并把所有深度為-1的值剔除掉,0、1、2、2、2,傳給合批部分的程序進(jìn)行合批操作,判斷相鄰的元素是否能進(jìn)行合批,通過判斷數(shù)組的值是否相等
UGUI性能查看工具
合批部分的特殊例子
一個(gè)白色image、藍(lán)色image覆蓋了Text,白色image和Text哪個(gè)先渲染
- 根據(jù)分析工具可以看到白色image先渲染,文本后渲染(白色圖片ID小一點(diǎn))
- 這樣得到的數(shù)組list,會(huì)先是白色image、Text、藍(lán)色image,導(dǎo)致白色和藍(lán)色無法合批
- 解決辦法是把白色image和藍(lán)色image賦值同一個(gè)texture
- 只要是滿足合批條件,合批數(shù)組緊挨著,雖然沒有覆蓋,也可以合批
- 能不能合批,在最后合批數(shù)組上才能決定
Mask合批
-
mask無法跟mask外的物體進(jìn)行合批,但是mask之間可以合批
-
mask本身會(huì)產(chǎn)生兩個(gè)drawcall,性能損耗
Mask為什么會(huì)產(chǎn)生兩個(gè)drawcall
- 第一個(gè)drawcall是mask在設(shè)置模板緩存產(chǎn)生的
- 第二個(gè)drawcall是mask在還原模板產(chǎn)生的
- mask之間可以進(jìn)行合批,設(shè)置模板緩存和還原模板時(shí)候,因?yàn)閳D片ID和材質(zhì)是一樣的,所以可以合批
- 設(shè)置模板緩存時(shí)候,會(huì)把需要顯示部分的緩存值設(shè)置為1,遮罩的部分設(shè)置為0,在渲染的時(shí)候會(huì)取出我存在當(dāng)前像素當(dāng)中的一個(gè)模板緩存值,然后判斷它呢是否為一。如果為一的話,才進(jìn)行渲染,如果不為一,就不不渲染了
- 每一個(gè)像素點(diǎn)上,創(chuàng)建了一個(gè)這樣類似于一個(gè)數(shù)據(jù)緩存,通過它的模板緩存值來判斷是否顯示,都是像素級(jí)別的單位
- 模板還原也要產(chǎn)生一次drawcall,把每個(gè)像素點(diǎn)的模板緩存清除
Mask為什么不能合批
因?yàn)镸ask會(huì)產(chǎn)生一個(gè)特殊的材質(zhì),材質(zhì)不同就不能合批
public virtual Material GetModifiedMaterial(Material baseMaterial){if (!MaskEnabled())return baseMaterial;var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas);if (stencilDepth >= 8){Debug.LogWarning("Attempting to use a stencil mask with depth > 8", gameObject);return baseMaterial;}int desiredStencilBit = 1 << stencilDepth;// if we are at the first level...// we want to destroy what is thereif (desiredStencilBit == 1){// StencilMaterial.Add是最主要的方法,這里添加了特殊的材質(zhì),因?yàn)椴馁|(zhì)不同,所以不能和mask外的物體合批var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);StencilMaterial.Remove(m_MaskMaterial);m_MaskMaterial = maskMaterial;var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0);StencilMaterial.Remove(m_UnmaskMaterial);m_UnmaskMaterial = unmaskMaterial;graphic.canvasRenderer.popMaterialCount = 1;graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);return m_MaskMaterial;}//otherwise we need to be a bit smarter and set some read / write masksvar maskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit | (desiredStencilBit - 1), StencilOp.Replace, CompareFunction.Equal, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));StencilMaterial.Remove(m_MaskMaterial);m_MaskMaterial = maskMaterial2;graphic.canvasRenderer.hasPopInstruction = true;var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));StencilMaterial.Remove(m_UnmaskMaterial);m_UnmaskMaterial = unmaskMaterial2;graphic.canvasRenderer.popMaterialCount = 1;graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);return m_MaskMaterial;}
Mask注意要點(diǎn)
- Mask剔除的部分還是會(huì)影響深度計(jì)算的,從而影響合批,增加drawcall次數(shù)
- Mask剔除的部分還是會(huì)drawcall,只不過mask把繪制的像素剔除了
- Mask下的子物體可以正常進(jìn)行合批
- mask之間只要滿足合批條件,那么他們之間的元素也是能夠進(jìn)行合批的
RectMask2D
- RectMask2D本身不占用drawcall
- 因?yàn)閷?shí)際的具體邏輯是在canvas render里進(jìn)行的,涉及到渲染具體的操作都是內(nèi)部用c++類做的,性能更好,這里通過性能分析渲染的面和點(diǎn)判斷遮蓋的部分是否被渲染出來
為什么RecMask2D比Mask性能更好
- RectMask2D本身不占用drawcall,Mask本身有兩次drawcall
- RectMask2D對(duì)于被遮罩的部分并不會(huì)繪制,Mask是把遮罩的部分剔除了
主要代碼
public virtual void PerformClipping()
{if (ReferenceEquals(Canvas, null)){return;}//TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771)// if the parents are changed// or something similar we// do a recalculate hereif (m_ShouldRecalculateClipRects){MaskUtilities.GetRectMasksForClip(this, m_Clippers);m_ShouldRecalculateClipRects = false;}// get the compound rects from// the clippers that are validbool validRect = true;// 獲取需要剪切的區(qū)域Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);// If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect// overlaps that of the root canvas.RenderMode renderMode = Canvas.rootCanvas.renderMode;bool maskIsCulled =(renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) &&!clipRect.Overlaps(rootCanvasRect, true);if (maskIsCulled){// Children are only displayed when inside the mask. If the mask is culled, then the children// inside the mask are also culled. In that situation, we pass an invalid rect to allow callees// to avoid some processing.clipRect = Rect.zero;validRect = false;}if (clipRect != m_LastClipRectCanvasSpace){foreach (IClippable clipTarget in m_ClipTargets){//設(shè)置剪切效果clipTarget.SetClipRect(clipRect, validRect);}foreach (MaskableGraphic maskableTarget in m_MaskableTargets){maskableTarget.SetClipRect(clipRect, validRect);maskableTarget.Cull(clipRect, validRect);}}else if (m_ForceClip){foreach (IClippable clipTarget in m_ClipTargets){clipTarget.SetClipRect(clipRect, validRect);}foreach (MaskableGraphic maskableTarget in m_MaskableTargets){maskableTarget.SetClipRect(clipRect, validRect);if (maskableTarget.canvasRenderer.hasMoved)maskableTarget.Cull(clipRect, validRect);}}else{foreach (MaskableGraphic maskableTarget in m_MaskableTargets){//Case 1170399 - hasMoved is not a valid check when animating on pivot of the objectmaskableTarget.Cull(clipRect, validRect);}}m_LastClipRectCanvasSpace = clipRect;m_ForceClip = false;UpdateClipSoftness();
}
RectMask2D注意要點(diǎn)
- 遮罩的部分因?yàn)闆]有繪制,所以不影響深度計(jì)算,不影響合批
- 會(huì)打斷合批,一個(gè)RectMask2D下的子物體不可以跟另一個(gè)RectMask2D下的子物體進(jìn)行合批,但RectMask2D下的子物體可以進(jìn)行合批
- RectMask2D組件的Image之間可以進(jìn)行合批
根據(jù)應(yīng)用場(chǎng)景選擇使用哪個(gè)(Mask、RectMask2D)
只需要一個(gè)遮罩
選擇用RectMask2D性能更好
需要多個(gè)遮罩,遮罩下有多個(gè)子物體
選擇用Mask可以對(duì)不同Mask之間的子物體進(jìn)行合批,這種情況就會(huì)比RectMask2D性能更好