Unity 性能优化基础

这篇具有很好参考价值的文章主要介绍了Unity 性能优化基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


首先贴出官方文档。

一、基础概念

DrawCall
即绘制调用命令,CPU在准备好渲染数据并设置渲染状态后,会通过Drawcall命令通知GPU进行渲染。

Canvas
Canvas是一个 Native 层实现的Unity组件,被 Unity 渲染系统用于在游戏世界空间中渲染分层几何体(layered geometry)。
Canvas 负责把它们包含的Mesh合批,生成合适的渲染命令发送给 Unity 图形系统。以上行为都是在Native C++代码中完成,我们称之为 Rebatch 或者Batch Build,当一个 Canvas 中包含的几何体需要Rebacth时,这个Canvas就会被标记为Dirty状态。
Canvas 组件可以嵌套在另一个 Canvas 组件下,我们称为子Canvas,子 Canvas 可以把它的子物体与父Canvas分离,使得当子Canvas被标记为Dirty时,并不会强制让父 Canvas 也强制 Rebuild,反之亦然。但在某些特殊情况下,使用子Canvas进行分离的方法可能会失效,例如当对父Canvas的更改导致子Canvas的大小发生变化时。
可以在Profiler中通过查看标志性函数 Canvas.BuildBatch 的耗时,来了解 Rebatch 的性能消耗。

Canvas Renderer
几何体(layered geometry)数据是通过 Canvas Renderer 组件被提交到 Canvas 中。

VertexHelper
顶点辅助类,用于保存UI的顶点、颜色、法线、uv、三角形索引等信息。

Graphic
Graphic 是UGUI的C#库提供的一个基类。它是为Canvas提供可绘制几何图形的所有UGUI的C#类的基类。大多数Unity内置的继承 Graphic 的类都是通过继承一个叫 MaskableGraphic 的子类来实现,这使得他们可以通过IMaskable 接口来被隐藏。Drawable 类的子类主要是Image和Text,且UGUI已提供了同名组件。

Layout
Layout控制着RectTransform的大小和位置,通常用于创建复杂的布局,这些布局需要对其内容进行相对大小调整或相对位置调整。Layout仅依赖于RectTransforms,并且仅影响其关联RectTransforms的属性。这些Layout类不依赖于Graphic类,可以独立于UGUI的Graphic类之外使用。

CanvasUpdateRegistry
这个单例类维护了 m_LayoutRebuildQueue 和 m_GraphicRebuildQueue 两个重建队列,在构造函数中监听了Canvas的 willRenderCanvases 事件,这个事件会在渲染前进行每帧调用。在回调函数 PerformUpdate() 函数中,遍历两个重建队列进行UI重建,并执行ClipperRegistry的Cull方法。

Rebuild
Rebuild是指 Layout 和 Graphic 组件的网格被重新计算,这个过程在 CanvasUpdateRegistry 中执行。
可以在Profiler中通过查看标志性函数 Canvas.SendWillRenderCanvas 的耗时,来了解Mesh重建的性能消耗。

ICanvasElement
ICanvasElement接口,重建的时候会调用它的Rebuild方法,继承它的类都会对这个函数进行重写,Unity中几乎所有的UI组件都继承自这个接口。
Unity 性能优化基础

二、UI Batching

Batching是指Canvas通过合并UI元素的网格,生成合适的渲染命令发送给Unity图形渲染流水线。Batch的结果被缓存复用,直到这个Canvas被标为dirty,当Canvas中某一个构成的网格改变的时候就会被标记为dirty。
从CPU把数据发送到显卡相对较慢,合批是为了一次性发送尽可能多的数据。
batch build、batching、rebatch等都是同一个概念。
计算批次需要按深度对网格进行排序,并检查它们是否有重叠、以及材质和纹理贴图是否相同等。

首先进行深度排序:按照Hierarchy窗口从上往下的顺序

  1. 不渲染的UI元素Depth为 -1(setactive为false,canvasgroup.alpha为0,disable),UI下没有和其他UI相交时,该UI的Depth为0。(相交指网格有重叠)
  2. 当前UI下面有一个UI与其相交,若两者贴图和材质相同时,它们Depth相同,否则上面的UI的Depth是下面UI的Depth+1。
  3. 当前UI下面与多个UI相交,则取多个UI中Depth最高的元素(Max)与当前UI比较,若两者贴图和材质相同,则它们Depth相同,否则Depth = Max + 1。

排序完成后对Depth,材质,贴图都相同的UI进行合批。(C++实现,未开放源码)

常见的打断合批的原因:

  1. 同一深度 UI 元素使用了不同的材质或贴图,比如不同的图集或者字体。
  2. 使用了Unity的默认图片或默认字体,本质上和上面一条相同。
  3. 原本能够被合批的UI在Hierarchy层级相邻,即使Z轴不同,也能被合批。但是原本可以合批的UI的Hierarchy层级之间或下方插入了其他UI,此时如果有UI的Z坐标不为0可能会打断合批。
  4. UI使用了Mask,其本身和子节点不参与外部合批。同深度、同材质、同贴图的Mask之间可以合批,不同Mask下的子物体也可以合批。
  5. UI使用了RectMask2D,其子节点不参与外部合批。UI本身参与外部合批,不同RectMask2D下的子物体不能合批。

调试工具

1)通过 Frame Debug 查看每个DrawCall的绘制:
Unity 性能优化基础
注意:UGUI的 drawcall 根据Canvas渲染模式的不同,所在的位置也有所不同:
Screen Space - Overlay 模式时,将会出现在 Canvas.RenderOverlays 分组。
Screen Space - Camera 模式时,将会出现在所选相机的 Camera.Render 分组,作为一个 Render.TransparentGeometry 子组。
World Space 渲染模式时,将会作为一个 Render.TransparentGeometry 子组,出现在每个可以观察到该 Canvas 的相机下。

2)通过 Profiler 的 UI Details 栏目查看所有Canvas的合批情况、打断合批的原因以及每个批次绘制了哪些内容:
Unity 性能优化基础

合批优化策略

  1. UI设计的时候应尽量保持UI使用相同的材质并处于同一深度(使用图集、注意UI的遮挡关系);
  2. 不要使用默认图片和默认字体;
  3. 特殊情况可以使用艺术字代替文本参数合批(bmfont);
  4. UI的Z轴统一设置为0;
  5. 如果需要使用遮罩,仅需要使用一个的时候用RectMask2D(Mask多两个DrawCall),需要使用多个的时候使用Mask(不同Mask的子节点参与合批);

三、UI Rebuild

Rebuild分为Layout Rebuild 和Graphic Rebuild。
Layout Rebuild
要重新计算一个或者多个Layout组件所包含的UI组件的适当位置(以及可能的大小),有必要对Layout应用层次进行排序。在GameObject的hierarchy中靠近root的Layout可能会影响改变嵌套在它里面的其他Layout的位置和大小,所以必须首先计算。 为此,UGUI根据层次结构中的深度对dirty的Layout组件列表进行排序。层次结构中较高的Layout(即拥有较少的父transform)将被移到列表的前面。然后,排序好的Layout组件的列表将被rebuild,在这个步骤Layout组件控制的UI元素的位置和大小将被实际改变。关于独立的UI元素如何受Layout组件影响的详细细节,请参阅Unity Manual的UI Auto Layout章节。 [ 这就是为什么unity的布局组件一旦形成嵌套,套内组件将失效的原因 , unity也暂时未开放布局执行层级顺序的接口 , 仅在UGUI代码中可见但未公开 ]
Graphic Rebuild
当Graphic组件被rebuild的时候,UGUI将控制传递给ICanvasElement接口的Rebuild方法。Graphic执行了这一步,并在rebuild过程中的PreRender阶段运行了两个不同的rebuild步骤:1.如果顶点数据已经被标为Dirty(例如组件的RectTransform已经改变大小),则重建网格。2.如果材质数据已经被标为Dirty(例如组件的material或者texture已经被改变),则关联的Canvas Renderer的材质将被更新。Graphic的Rebuild不会按照Graphic组件的特殊顺序进行,也不会进行任何的排序操作。

Rebuild 通常会触发 Batching。

源码分析

1. Rebuild的执行过程

Unity 性能优化基础
Canvas每帧执行。
Unity 性能优化基础
CanvasUpdateRegistry在构造函数中监听并注册回调函数 PerformUpdate。

下面是 PerformUpdate() 源码:

		private void PerformUpdate()
        {
            UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
            //清理Queue中值为null或者被销毁的元素
            CleanInvalidItems();

            m_PerformingLayoutUpdate = true;
             //根据父节点多少排序(层级)
            m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);

            for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
            {
                UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);

                for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
                {
                    var rebuild = m_LayoutRebuildQueue[j];
                    try
                    {
                        //布局重建
                        if (ObjectValidForUpdate(rebuild))
                            rebuild.Rebuild((CanvasUpdate)i);
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e, rebuild.transform);
                    }
                }
                UnityEngine.Profiling.Profiler.EndSample();
            }

            //通知布局重建完成
            for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i)
                m_LayoutRebuildQueue[i].LayoutComplete();

            m_LayoutRebuildQueue.Clear();
            m_PerformingLayoutUpdate = false;
            
            UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
            UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);

            // now layout is complete do culling...
            UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
            
            //执行裁剪(cull)操作
            ClipperRegistry.instance.Cull();
            UnityEngine.Profiling.Profiler.EndSample();

            m_PerformingGraphicUpdate = true;

            for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
            {
                UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
                for (var k = 0; k < m_GraphicRebuildQueue.Count; k++)
                {
                    try
                    {
                        var element = m_GraphicRebuildQueue[k];
                         //图形重建
                        if (ObjectValidForUpdate(element))
                            element.Rebuild((CanvasUpdate)i);
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
                    }
                }
                UnityEngine.Profiling.Profiler.EndSample();
            }
            
            //通知图形重建完成
            for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
                m_GraphicRebuildQueue[i].GraphicUpdateComplete();

            m_GraphicRebuildQueue.Clear();
            m_PerformingGraphicUpdate = false;
            UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
        }

2. UI是怎么加入重建队列的

查看源码发现,主要通过以下两个函数将待重建的ICanvasElement加入重建队列中:
Unity 性能优化基础
Unity 性能优化基础
一般通过脏标记来实现,以Graphic为例:
通过 SetLayoutDirty() 触发 LayoutRebuilder.MarkLayoutForRebuild(rectTransform) 将UI加入m_LayoutRebuildQueue 重建队列中。
通过 SetVerticesDirty()、SetMaterialDirty()、以及 OnCullingChanged() 的调用将UI加入m_GraphicRebuildQueue 重建队列中。

通过查看源码中哪些地方调用了这几个函数,就能知道什么情况下会触发UI的Rebuild了。

常见触发Rebuild的操作:

  1. RectTransform 的 Width,Height,Anchor,Pivot改变。
  2. Text 的内容及颜色变化、设置是否支持富文本、更改对齐方式、设置字体大小等。
  3. Image 组件颜色变化、更换Sprite。
  4. Slider 组件每次滑动时。
  5. ScrollBar 组价每次滑动时。
  6. SetActive、Enable为true时。
  7. Mask 勾选/取消勾选 Show Mask Graphic。
  8. Material改变。等等…

经测试,改变Position,Rotation,Scale不会引起UI重建,但是会直接触发Batching。

反射查看Rebuild队列:
可以在运行时查看哪些元素引起UI重建。

using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;

public class LogRebuildInfo : MonoBehaviour
{
    IList<ICanvasElement> m_LayoutRebuildQueue;
    IList<ICanvasElement> m_GraphicRebuildQueue;

    private void Awake()
    {
        System.Type type = typeof(CanvasUpdateRegistry);
        FieldInfo field = type.GetField("m_LayoutRebuildQueue", BindingFlags.NonPublic | BindingFlags.Instance);
        m_LayoutRebuildQueue = (IList<ICanvasElement>)field.GetValue(CanvasUpdateRegistry.instance);
        field = type.GetField("m_GraphicRebuildQueue", BindingFlags.NonPublic | BindingFlags.Instance);
        m_GraphicRebuildQueue = (IList<ICanvasElement>)field.GetValue(CanvasUpdateRegistry.instance);
    }

    private void Update()
    {
        for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
        {
            var element = m_LayoutRebuildQueue[j];
            if (ObjectValidForUpdate(element))
            {
                Debug.LogErrorFormat("{0} 引起 {1} 网格布局重建", element.transform.name, element.transform.GetComponentInParent<Canvas>().name);
            }
        }

        for (int j = 0; j < m_GraphicRebuildQueue.Count; j++)
        {
            var element = m_GraphicRebuildQueue[j];
            if (ObjectValidForUpdate(element))
            {
                Debug.LogErrorFormat("{0} 引起 {1} 网格图形重建", element.transform.name, element.transform.GetComponentInParent<Canvas>().name);
            }
        }
    }
    
    private bool ObjectValidForUpdate(ICanvasElement element)
    {
        var valid = element != null;

        var isUnityObject = element is Object;
        if (isUnityObject)
            valid = (element as Object) != null; //Here we make use of the overloaded UnityEngine.Object == null, that checks if the native object is alive.

        return valid;
    }
}

3. Rebuild具体做了些什么

以Graphic为例。

3.1 图形重建过程:
        public virtual void Rebuild(CanvasUpdate update)
        {
            if (canvasRenderer == null || canvasRenderer.cull)
                return;

            switch (update)
            {
                case CanvasUpdate.PreRender:
                    if (m_VertsDirty)
                    {
                        UpdateGeometry();
                        m_VertsDirty = false;
                    }
                    if (m_MaterialDirty)
                    {
                        UpdateMaterial();
                        m_MaterialDirty = false;
                    }
                    break;
            }
        }
  • UpdateGeometry()
    Graphic中有个静态对象s_VertexHelper保存每次生成的Mesh信息(包括顶点,三角形索引,UV,顶点色等数据),使用完后会立即清理掉等待下个Graphic对象使用。
    Unity 性能优化基础
    我们可以看到,s_VertexHelper中的数据通过OnPopulateMesh函数,进行填充,它是一个虚函数会在各自的类中实现,我们可以在自己的UI类中,重写OnPopulateMesh方法,实现自定义的UI。
    s_VertexHelper数据填充之后,调用FillMesh() 方法生成真正的Mesh,然后调用 canvasRenderer.SetMesh() 方法来提交。SetMesh() 方法最终在C++中实现,这也是UGUI的效率比NGUI高一些的原因,因为NGUI的Mesh合并是在C#中完成的,而UGUI的Mesh合并是在C++中底层完成的。

  • UpdateMaterial()
    UpdateMaterial() 方法会通过canvasRenderer来更新Material与Texture。
    Unity 性能优化基础

3.2 布局重建过程:

LayoutRebuilder 的 Rebuild() 方法:
Unity 性能优化基础
PerformLayoutCalculation() 方法会递归计算UI元素的宽高(先计算子元素,然后计算自身元素)。
ILayoutElement.CalculateLayoutInputXXXXXX() 在具体的实现类中计算该UI的大小。
PerformLayoutControl() 方法会递归设置UI元素的宽高(先设置自身元素,然后设置子元素)。
ILayoutController.SetLayoutXXXXX() 在具体的实现类中设置该UI的大小。

UI重建优化策略

  1. 动静分离:细分Canvas,把相对静态的、不会变动的UI放在一个Canvas里,而相对变化比较频繁的UI就放在另一个Canvas里。注意:新增Canvas会打断合批,增加DrawCall。
  2. 隐藏界面时,可用CanvasGroup.Alpha=0,或者从Camera渲染层级里移除等方法隐藏,代替SetActive。
  3. 对于血条、角色头顶名称、小地图标记等频繁更新位置的UI,可尽量减低更新频率,如隔帧更新,并设定更新阈值,当位移大于一定数值时再赋值(重复赋相同的值,也会SetDirty触发重建)。
  4. 注意合理设计UI的层级,由于布局重建需要对UI进行排序,层级太深影响排序消耗。

四、OverDraw

Overdraw是指一帧当中,同一个像素被重复绘制的次数。Fill Rate(填充率)是指显卡每帧每秒能够渲染的像素数。在每帧绘制中,如果一个像素被反复绘制的次数越多,那么它占用的资源也必然更多。Overdraw与Fill Rate成正比,目前在移动设备上,FillRate的压力主要来自半透明物体。因为多数情况下,半透明物体需要开启 Alpha Blend 且关闭 ZTest和 ZWrite,同时如果我们绘制像 alpha=0 这种实际上不会产生效果的颜色上去,也同样有 Blend 操作,这是一种极大的浪费。
不幸的是,Canvas绘制的所有几何体都在透明队列中绘制。也就是说,Unity UI生成的几何体将始终使用 Alpha 混合从前向后绘制。从多边形栅格化后的每个像素都将被采样,即使它完全由其他不透明多边形覆盖。在移动设备上,这种高水平的透支可以快速超过GPU的填充率容量。
在场景【scene】下拉列表中选择overdraw就能看见,越亮的地方就是overdraw最多的部分。

OverDraw优化策略

  1. 减少UI重叠层级,隐藏处于底下被完全覆盖的UI面板。
  2. 对于需要暂时隐藏的UI,不要直接把Color属性的Alpha值改为0,UGUI中这样设置后仍然会渲染,应该用CanvasGroup组件把Alpha值置零。
  3. 需要响应Raycast事件时,不要使用空Image,可以自定义组件继承自MaskableGraphic,重写OnPopulateMesh把网格清空,这样可以响应Raycast而又不需要绘制Mesh。
  4. 打开全屏界面,关闭场景摄像机。对于一些非全屏但覆盖率较高的界面,在对场景动态表现要求不高的情况下,可以记录下打开UI时的画面,作为UI背景,然后关掉场景摄像机。
  5. 裁掉无用区域,镂空,对于 Sliced 类型的 Image 可以看情况取消 Fill Center。
  6. 保持UI上的粒子特效简单,尽量不要发生重叠。

五、其他优化

  1. 所有可点击组件例如 Image、Text 在创建时默认开启 RaycastTarget。当进行点击操作时,会对所有开启RaycastTarget的组件进行遍历检测和排序。实际上大部分的组件是不需要响应点击事件的,对于这些组件我们应该取消RaycastTarget属性,最好的方式是监听组件创建,在创建时直接赋值为 false,对于需要响应事件的组件再手动开启。
  2. Text 尽量不要使用 outline 或者 shadow 组件,会使顶点数量成倍增加。字体效果考虑 Shader实现,或者直接让美术同学把阴影和描边做到字体里。

六、总结

常见UI性能问题:

  1. DrawCall过高,合批和提交批次(CPU --> GPU )花费大量CPU时间(Rebatch)。
  2. UI重建花费大量CPU时间(Rebuild)。
  3. 填充率过高,导致GPU渲染压力过大(overdraw)。
  4. 生成顶点花费大量CPU时间(通常来自文本)。

上面针对这些问题提出了一些通用的优化策略。但正如官方文档所说:
The core tension when optimizing any Unity UI is the balancing of draw calls with batching costs. While some common-sense techniques can be used to reduce one or the other, complex UIs must make trade-offs.
UI优化的核心是DrawCalls和Batching开销的平衡。可以使用一些常识性技术来减少其中之一,但复杂的UI必须在两者间进行权衡。

举例:
Unity 性能优化基础
修改 Graphic 的Color属性,其原理是修改顶点色,因此是会引起网格的Rebuild的(即Canvas.BuildBatch操作,同时也会有Canvas.SendWillRenderCanvases的开销)。而通过修改顶点色来实现UI元素变色的好处在于,修改顶点色可以保证其材质不变,因此不会产生额外的DrawCall。
在UI的默认Shader中存在一个Tint Color的变量,正常情况下,该值为常数(1,1,1),且并不会被修改。如果是用脚本访问Material,并修改其Tint Color属性时,对UI元素产生的网格信息并没有影响,因此就不会引起网格的Rebuild。但这样做因为修改了材质,所以会增加一个Draw。
这时候就得权衡一下是要更少的DrawCall,还是减少UI的重建更合适。

七、参考文章

https://edu.uwa4d.com/lesson-detail/126/482/0?isPreview=false uwa drawcall rebatch rebuild particle 等
https://www.jianshu.com/p/5a39cfa74232 UI Rebuild过程详解
https://blog.csdn.net/gaojinjingg/article/details/103565840?spm=1001.2101.3001.6650.3 Unity UGUI优化与原理
https://www.drflower.top/posts/aad79bf1/ UGUI性能优化总结
https://zhuanlan.zhihu.com/p/350778355 OverDraw详解文章来源地址https://www.toymoban.com/news/detail-461827.html

到了这里,关于Unity 性能优化基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 开源预训练框架 MMPRETRAIN官方文档(概览、环境安装与验证、基础用户指南)

    MMPretrain是全新升级的开源预训练框架。它已着手提供多个强大的预训练骨干网并支持不同的预训练策略。MMPretrain 源自著名的开源项目 MMClassification 和MMSelfSup,并开发了许多令人兴奋的新功能。目前,预训练阶段对于视觉识别至关重要。凭借丰富而强大的预训练模型,我们目

    2024年02月13日
    浏览(57)
  • Unity性能优化笔记——UI优化

    NGUI: UGUI: 元素更新方式: NGUI: UIPanel.LateUpdate 1.轮询 2.UIPanel.UpdateWidgets UGUI: Canvas.SendWillRenderCanvas 1.队列 2.m_LayoutRebuildQueue 3.m_GraphicRebuildQueue UGUI如果Canvas没有变化Canvas.SendWillRenderCanvas是完全没有开销的 处理隐藏显示元素方式 NGUI:        1.大量的SetActive(false),       2.适量

    2024年02月08日
    浏览(41)
  • Unity性能优化篇(十一) 动画优化

    1.恰当地设置Animator组件的Culling Mode。Always Animate表示如果该动画不可见,也会播放它。Cull Update Transformations表示如果该动画不可见,则不会渲染该动画,但是依然会根据该动画的播放来改变游戏对象的位置、旋转、缩放,这样是常用的选项。Cull Completely表示完全不会播放该动

    2024年03月11日
    浏览(51)
  • AI作曲基础-Python编程作曲软件篇-FoxDot文档及源码分析-官方教程01

    本系列系列目录放在文尾; 本系列是AI作曲的基础,暂时和AI关系不大,但尤为重要; 借助FoxDot,从文档分析开始,然后进入源码分析环节; 暂未发现官方中文版,实践顺带翻译,会根据需要不定期校对及更新,欢迎催更~ 教程来源 FoxDot官方主页在此:https://foxdot.org/ FoxDot官

    2024年02月11日
    浏览(35)
  • Unity 性能优化一:性能标准、常用工具

    推荐耗时: 性能提现到玩家直观感受,就是帧率,为了达到要求的帧率,就要控制CPU的耗时,不同类型的游戏,对帧率要求不一样。下面是推荐耗时: 推荐内存: 避免游戏闪退的重点在于控制PSS内存(实际物理内存Proportional set size)峰值。而PSS内存的大头又在于 Reserved Total中

    2024年02月15日
    浏览(41)
  • unity 性能优化之GPU和资源优化

    众所周知,我们在unity里编写Shader使用的HLSL/CG都是高级语言,这是为了可以书写一套Shader兼容多个平台,在unity打包的时候,它会编译成对应平台可以运行的指令,而变体则是,根据宏生成的,而打包运行时,GPU会根据你设置的宏切换这些打包出来的代码,而不是我们书写那

    2024年02月02日
    浏览(64)
  • unity性能优化部分

         (1) 优化几何体 :尽可能减少模型中三角形的数目,尽可能重用顶点     (2)使用LOD(Level of detail)技术      unityLOD优化技术详解_丶博Liang的博客-CSDN博客_lod优化     (3)使用遮挡剔除(Occlusion culling)技术 (1)像素优化的重点在于减少overdraw。overdraw指的就

    2024年02月14日
    浏览(42)
  • unity 性能优化指标

    内存需要关注项目以及阈值推荐: Mono 堆内存:    影响 GC 耗时, 存在泄露风险,控制在 80M 以下。       PSS                   :    进程在 RAM 中实际使用的空间地址大小     内存峰值控制在硬件总内存 0.5~0.6  以下,闪退风险才低。中端机型 900M Reserved Total

    2024年02月07日
    浏览(48)
  • Unity性能优化分析篇

    性能优化是游戏项目开发中一个重要环节。游戏帧率过低,手机发烫, 包体太大,低端机上跑不起来等, 这些都需要来做优化,不管过去,现在,未来,性能优化都是永恒的话题。 而性能优化首先要掌握的是性能分析,通过分析才能发现问题所在。性能分析对于游戏开发是必

    2024年02月03日
    浏览(61)
  • Unity性能优化 - 动态图集

    Unity 动态图集是 Unity 引擎中用于处理游戏纹理优化的一种技术。它可以将多个纹理打包到一个图集中,减少游戏中需要加载的纹理数量,从而提高游戏性能。 在运行时,Unity 动态图集会根据游戏需要动态地生成纹理,并将它们打包到一个图集中,这样可以大幅降低游戏加载

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包