UGUI优化篇--UGUI合批

打印 上一主题 下一主题

主题 1016|帖子 1016|积分 3048

UGUI合批规则概述

  

  • 遍历所有UI元素
  • 根据深度Depth(优先级最高,-1表示不消渲染)、材质ID、图片ID、渲染顺序对所有UI进行排序
  • 进行合批处理,比如UI1和UI2(两者必须是紧挨着的)是相同材质、相同图片就可以进行合批,如果UI1和UI2之间有一个中间层UI3(根UI1和UI2材质不同),就会打断合批

  

  • Text必须文字的网格覆盖到image上面才算相交,Rect Transform框框相交不算
  • 白色image的深度为0,最先渲染,然后判断Text是否相交,再判断材质ID和图片ID是否相同,如果相同则可以合批,由于不相同,以是Text深度为白色image+1为1(如果Text底下有许多image,则取最大的深度+1)
  • 红色image由于底下有Text和白色image,以是计算出两个深度值分别1和0,取最大值加1,就是2
  • 黄色和蓝色image会进行合批测试操作,深度值都是2(这里只是测试是否能合批,并还没有真正合批)
  • 最后会得到一个排序数组list并把所有深度为-1的值剔除掉,0、1、2、2、2,传给合批部分的程序进行合批操作,判断相邻的元素是否能进行合批,通过判断数组的值是否相称
  UGUI性能检察工具

  

  

  

  合批部分的特殊例子

  一个白色image、蓝色image覆盖了Text,白色image和Text哪个先渲染

  

  

  • 根据分析工具可以看到白色image先渲染,文本后渲染(白色图片ID小一点)
  • 如许得到的数组list,会先是白色image、Text、蓝色image,导致白色和蓝色无法合批
  • 解决办法是把白色image和蓝色image赋值同一个texture

  • 只要是满足合批条件,合批数组紧挨着,固然没有覆盖,也可以合批
  • 能不能合批,在最后合批数组上才气决定
  Mask合批

  

  • mask无法跟mask外的物体进行合批,但是mask之间可以合批
  • mask自己会产生两个drawcall,性能损耗
  Mask为什么会产生两个drawcall

  

  • 第一个drawcall是mask在设置模板缓存产生的
  • 第二个drawcall是mask在还原模板产生的
  • mask之间可以进行合批,设置模板缓存和还原模板时间,由于图片ID和材质是一样的,以是可以合批
  • 设置模板缓存时间,会把必要显示部分的缓存值设置为1,遮罩的部分设置为0,在渲染的时间会取出我存在当前像素当中的一个模板缓存值,然后判断它呢是否为一。如果为一的话,才进行渲染,如果不为一,就不不渲染了
  • 每一个像素点上,创建了一个如许类似于一个数据缓存,通过它的模板缓存值来判断是否显示,都是像素级别的单位
  • 模板还原也要产生一次drawcall,把每个像素点的模板缓存扫除
  Mask为什么不能合批

  由于Mask会产生一个特殊的材质,材质不同就不能合批

  1.     public virtual Material GetModifiedMaterial(Material baseMaterial)
  2.     {
  3.         if (!MaskEnabled())
  4.             return baseMaterial;
  5.         var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
  6.         var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas);
  7.         if (stencilDepth >= 8)
  8.         {
  9.             Debug.LogWarning("Attempting to use a stencil mask with depth > 8", gameObject);
  10.             return baseMaterial;
  11.         }
  12.         int desiredStencilBit = 1 << stencilDepth;
  13.         // if we are at the first level...
  14.         // we want to destroy what is there
  15.         if (desiredStencilBit == 1)
  16.         {
  17.             // StencilMaterial.Add是最主要的方法,这里添加了特殊的材质,因为材质不同,所以不能和mask外的物体合批
  18.             var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);
  19.             StencilMaterial.Remove(m_MaskMaterial);
  20.             m_MaskMaterial = maskMaterial;
  21.             var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0);
  22.             StencilMaterial.Remove(m_UnmaskMaterial);
  23.             m_UnmaskMaterial = unmaskMaterial;
  24.             graphic.canvasRenderer.popMaterialCount = 1;
  25.             graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);
  26.             return m_MaskMaterial;
  27.         }
  28.         //otherwise we need to be a bit smarter and set some read / write masks
  29.         var maskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit | (desiredStencilBit - 1), StencilOp.Replace, CompareFunction.Equal, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
  30.         StencilMaterial.Remove(m_MaskMaterial);
  31.         m_MaskMaterial = maskMaterial2;
  32.         graphic.canvasRenderer.hasPopInstruction = true;
  33.         var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
  34.         StencilMaterial.Remove(m_UnmaskMaterial);
  35.         m_UnmaskMaterial = unmaskMaterial2;
  36.         graphic.canvasRenderer.popMaterialCount = 1;
  37.         graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);
  38.         return m_MaskMaterial;
  39.     }
复制代码
Mask留意要点

  

  • Mask剔除的部分还是会影响深度计算的,从而影响合批,增长drawcall次数
  • Mask剔除的部分还是会drawcall,只不外mask把绘制的像素剔除了
  • Mask下的子物体可以正常进行合批
  • mask之间只要满足合批条件,那么他们之间的元素也是可以或许进行合批的

  RectMask2D

  

  • RectMask2D自己不占用drawcall
  • 由于现实的详细逻辑是在canvas render里进行的,涉及到渲染详细的操作都是内部用c++类做的,性能更好,这里通过性能分析渲染的面和点判断遮盖的部分是否被渲染出来

  为什么RecMask2D比Mask性能更好

  

  • RectMask2D自己不占用drawcall,Mask自己有两次drawcall
  • RectMask2D对于被遮罩的部分并不会绘制,Mask是把遮罩的部分剔除了
  重要代码

  1. public virtual void PerformClipping()
  2. {
  3.     if (ReferenceEquals(Canvas, null))
  4.     {
  5.         return;
  6.     }
  7.     //TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771)
  8.     // if the parents are changed
  9.     // or something similar we
  10.     // do a recalculate here
  11.     if (m_ShouldRecalculateClipRects)
  12.     {
  13.         MaskUtilities.GetRectMasksForClip(this, m_Clippers);
  14.         m_ShouldRecalculateClipRects = false;
  15.     }
  16.     // get the compound rects from
  17.     // the clippers that are valid
  18.     bool validRect = true;
  19.     // 获取需要剪切的区域
  20.     Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);
  21.     // If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect
  22.     // overlaps that of the root canvas.
  23.     RenderMode renderMode = Canvas.rootCanvas.renderMode;
  24.     bool maskIsCulled =
  25.         (renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) &&
  26.         !clipRect.Overlaps(rootCanvasRect, true);
  27.     if (maskIsCulled)
  28.     {
  29.         // Children are only displayed when inside the mask. If the mask is culled, then the children
  30.         // inside the mask are also culled. In that situation, we pass an invalid rect to allow callees
  31.         // to avoid some processing.
  32.         clipRect = Rect.zero;
  33.         validRect = false;
  34.     }
  35.     if (clipRect != m_LastClipRectCanvasSpace)
  36.     {
  37.         foreach (IClippable clipTarget in m_ClipTargets)
  38.         {
  39.             //设置剪切效果
  40.             clipTarget.SetClipRect(clipRect, validRect);
  41.         }
  42.         foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
  43.         {
  44.             maskableTarget.SetClipRect(clipRect, validRect);
  45.             maskableTarget.Cull(clipRect, validRect);
  46.         }
  47.     }
  48.     else if (m_ForceClip)
  49.     {
  50.         foreach (IClippable clipTarget in m_ClipTargets)
  51.         {
  52.             clipTarget.SetClipRect(clipRect, validRect);
  53.         }
  54.         foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
  55.         {
  56.             maskableTarget.SetClipRect(clipRect, validRect);
  57.             if (maskableTarget.canvasRenderer.hasMoved)
  58.                 maskableTarget.Cull(clipRect, validRect);
  59.         }
  60.     }
  61.     else
  62.     {
  63.         foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
  64.         {
  65.             //Case 1170399 - hasMoved is not a valid check when animating on pivot of the object
  66.             maskableTarget.Cull(clipRect, validRect);
  67.         }
  68.     }
  69.     m_LastClipRectCanvasSpace = clipRect;
  70.     m_ForceClip = false;
  71.     UpdateClipSoftness();
  72. }
复制代码
RectMask2D留意要点

  

  • 遮罩的部分由于没有绘制,以是不影响深度计算,不影响合批
  • 会打断合批,一个RectMask2D下的子物体不可以跟另一个RectMask2D下的子物体进行合批,但RectMask2D下的子物体可以进行合批
  • RectMask2D组件的Image之间可以进行合批
  根据应用场景选择使用哪个(Mask、RectMask2D)

  只必要一个遮罩

  选择用RectMask2D性能更好
  必要多个遮罩,遮罩下有多个子物体

  选择用Mask可以对不同Mask之间的子物体进行合批,这种情况就会比RectMask2D性能更好

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! 更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

知者何南

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表