赞
踩
GPU与CPU不同,所以侧重点自然也不一样。GPU的瓶颈主要存在在如下的方面:
那么针对以上4点,其实仔细分析我们就可以发现,影响的GPU性能的无非就是2大方面,一方面是顶点数量过多,像素计算过于复杂。另一方面就是GPU的显存带宽。那么针锋相对的两方面举措也就十分明显了。
那么第一个方面的优化也就是减少顶点数量,简化复杂度,具体的举措就总结如下了:
第二个方向呢?压缩图片,减小显存带宽的压力。
这里匹夫要着重介绍一下MipMap到底是啥。因为有人说过MipMap会占用内存呀,但为何又会优化显存带宽呢?那就不得不从MipMap是什么开始聊起。一张图其实就能解决这个疑问。
这是一个mipmap 如何储存的例子,左边的主图伴有一系列逐层缩小的备份小图
///
GPU的瓶颈主要存在在如下的方面:
那么我们要做的就是
1.1 OverDraw优化
1.1.1. EmptyRaycast
UGUI中 Alpha=0的不可见Image参与Raycast,譬如在屏幕空白处点击的响应,然而这些元素虽然在屏幕上不可见、但依然参与了绘制! 用EmptyRaycast 作为点击事件的响应媒介,可以减少绘制,减少OverDraw
- using UnityEngine;
- using System.Collections;
- namespace UnityEngine.UI
- {
- public class Empty4Raycast : MaskableGraphic
- {
- protected Empty4Raycast()
- {
- useLegacyMeshGeneration = false;
- }
- protected override void OnPopulateMesh(VertexHelper toFill)
- {
- toFill.Clear();
- }
- }
- }
1.1.2. Fill center
如果texture是中心镂空且切图为九宫格时,可以去除fill center,以减少OverDraw。
1.1.3. 粒子优化
- 单个粒子的发射数量不超过50个。
- 减少粒子的尺寸,面积越大就会消耗更多的性能。
- 粒子贴图必须是2的N次方,尽量控制64x64以内,极少量128x128或256x256,最大不超过256x256。
- 尽可能去掉粒子贴图的Alpha通道。
- 尽量不用Alpha Test。
- 尽量使用已有的材质,提高合并渲染的优化概率。
- 材质优先用Mobile目录下的材质。
- 尽可能不用模型做粒子,如果使用,要控制模型面数在100以内,最大粒子数在5以内。
- 单个特效渲染数据限制:
- 小型特效(如受击特效、Buff特效)的面数和顶点数在80以内,贴图在64*64以内,材质数2个以内。
- 中型特效(如技能特效)的面数和顶点数在150以内,贴图在128*128以内,材质数4个以内。
- 大型特效(如全局特效、大火球)的面数和顶点数在300以内,贴图在256*256以内,材质数6个以内。
1.1.4. 少用Outline,改用Shadow
1.2 使用光照纹理(lightmap)而非实时灯光。
1.3 使用LOD,好处就是对那些离得远,看不清的物体的细节可以忽略。
1.4 遮挡剔除(Occlusion culling)
压缩图片,减小显存带宽的压力。
/
在游戏开发过程中,经常遇到一些性能问题,可以通过 Profiler 工具检测,是否是渲染引起的问题。在 Unity 官方网站上找到了优化渲染的进阶教程,于是翻译出来方便阅读。
在这篇文章,我们将会学到当 Unity 渲染一帧图像的时候屏幕后面会发生什么和渲染的时候会遇到什么性能问题以及如何解决渲染相关的性能问题。
阅读文章之前,首先要明白改善渲染性能问题没有放之四海而皆准的方法。我们游戏的渲染性能问题受很多方面的影响,并且高度依赖运行游戏的硬件和操作系统。要记住最有用的一点是,我们通过调查、实验和严格的分析来解决性能问题。
这篇文章包含最常见的渲染性能问题,并提供了如何修复它们的建议及进一步阅读的链接。也存在这种情况,我们的游戏有一个或多个问题这篇文章是没有提及的。这篇文章仍能帮助我们了解我们的问题,并给我们知识和词汇,以便可以有效的寻找解决方案。
在此之前,让我们简单快速的看一下 Unity 渲染一帧会发生什么事。理解接下来的事件和正确的术语将对我们理解、研究和努力修复性能问题有帮助。
在这篇文章,我们使用“对象”来代表我们游戏中可能被渲染的对象。任何游戏物品挂载了 Renderer 组件的,将会被当做这样一个对象。
在最基本层面上,渲染可以被描述成这样:
现在让我们仔细看看到底发生了什么。我们会在文章的后面提及更详细的步骤,但现在我们只理解这些用语,并且明白 CPU 和 GPU 在渲染中起什么作用。
经常使用渲染管线来描述渲染,这是一个非常有用的概念。高效的渲染就是保持信息流畅。
对于渲染的每一帧,CPU 执行了以下的任务:
对于每个包含 draw call 的批处理来说,CPU 必须执行以下操作:
同时,GPU 做以下的工作:
现在我们明白了当 Unity 渲染一帧的时发生了什么,让我们考虑一下渲染时可能会出现的问题。
理解渲染问题最重要是:渲染一帧,CPU 和 GPU 必须完成它们所有的任务。如果它们当中的任何一个任务完成时间太长,将会造成渲染延迟。
渲染问题有两个基本的原因:第一种是:低效的管道。当渲染管道中当一个或多个任务完成时间耗时过长会造成管道效率低,会中断数据的流程度。管道内的低效率,也被称为瓶颈。第二种是向管道推送了太多的数据。即使是最高效的管道,单帧处理的数据也会有一个上限。
当我们的游戏花费太长时间渲染一帧是因为 CPU 花费太长时间执行渲染任务,那我们的游戏是 CPU 密集。当我们的游戏花费太长时间渲染一帧是因为 GPU 花费太长时间执行渲染任务,那我们的游戏是 GPU 密集。
在我们做出调整之前,使 用profiling 工具去了解造成性能问题的原因是很重要的。不同的问题需要不同的解决方案。测量我们每次做出的修改的效果也是同样重要的。修复性能问题是一种平衡的行为,修复一方面的问题可能引起另一些问题。
我们将会使用两种工具帮助我们理解和修复渲染性能问题: Profiler 工具和 Fame Debugger 。这两个工具都是 Unity 自带的。
Profiler工具
Profiler 允许我们看到游戏实时执行的数据。我们可以使用 Profiler 看到我们游戏的许多方面的数据,包括内存使用、渲染管道和脚本的性能。
如果你对 Profiler 还不熟悉,请看这个介绍和这个教程。
帧调试器
帧调试器允许我们看到一帧是如何一步步渲染的。使用帧调试器,我们可以看到每个 draw call 绘制了什么,每个 draw call 的 shader 属性和发送给 GPU 的事件顺序等详细信息。这些信息帮助我们理解我们的游戏是如何渲染的以及我们可以改善那些性能问题。
如果你对使用 Frame Debugger 还不熟悉,请看这个介绍和这个教程。
查找造成性能问题的原因
在我们尝试改善我们游戏的渲染性能问题之前,我们必须先确定我们游戏运行缓慢是渲染问题造成的。如果造成我们问题的原因是过度复杂的脚本,尝试优化我们的渲染性能是没有意义的。如果你还不确定你的性能问题是否与渲染相关,请看这篇教程。
一旦我们确定我们的问题与渲染相关,我们必须了解我们的游戏是 CPU 密集还是 GPU 密集。不同的问题需要不同的解决方案,在尝试修复问题之前,了解造成问题的原因是非常重要的。如果你还不确定你的游戏是 CPU 密集还是 GPU 密集,请查看这篇教程。
如果我们确定了我们的问题与渲染相关,并且我们知道我们的游戏是 CPU 密集或 GPU 密集,我们接着往下读。
从广义上讲,CPU 渲染一帧必须要完成的工作分成三类:
这些宽泛的类别包含许多单个任务,这些任务可以跨多个线程执行。线程允许多个独立的任务同时执行。当一个线程执行一个任务,另一个线程可以执行一个完全独立的任务。这就意味着工作可以更快的完成。当渲染任务被拆分跨多个独立的线程时,这就是多线程渲染。
在 Unity 渲染进程中会调用三种类型的线程:main thread, render thread 和 worker threads。主线程执行我们游戏的主要任务,包括一些渲染任务。渲染线程是一个特殊的线程,负责发送命令给 GPU。每个 worker 线程执行一个单独的任务,比如裁剪或网格蒙皮。哪一个任务被哪一个线程执行依赖于我们的游戏设置和游戏运行的硬件。比如,目标设备的 CPU 的内核越多,产生的 worker 线程越多。因此,我们的游戏在目标设备上分析是非常重要的。我们的游戏在不同的设备执行结果会截然不同。
因为多线程选渲染是复杂且依赖硬件的,在我们尝试改善性能问题之前,我们必须明白哪个任务是造成 CPU 密集的原因。如果我们游戏运行缓慢是因为裁剪操作在一个线程耗时太长了,通过减少另一个发送命令给 GPU 的线程的耗时是没有什么帮助的。
不是所有的平台都支持多线程渲染。在编写这篇文章的时候,WebGL 还不支持这个特性。在一个不支持多线程渲染的平台,所有的 CPU 任务都在同一个线程执行。如果 CPU 密集的运行在这样的平台,CPU 上任何优化都会改善 CPU 的性能。这种情况下,我们应该阅读下面的章节并考虑哪种优化最适合我们的游戏。
图形处理
Graphics jobs 选项在 Player Settings 。它决定 Unity 是否使用 worker 线程执行在 main 线程上或在某些情况下渲染现线程上的任务。在支持该性能的平台上可以提供相当大的性能提升。如果我们希望使用这个特性,我们应该观察使用了 Graphics jobs 和没使用 Graphics jobs 的性能效果。
找到是哪个任务引起的问题
我们可以使用 Profiler 工具确定是哪个任务造成 CPU 密集。本教程展示了如何确定问题所在。
现在我们明白了哪个任务是造成 CPU 密集的原因,让我们看下几种常见的问题和解决方案。
发送命令到 GPU
发送命令到 GPU 的耗时是游戏 CPU 密集最常见的原因。这个任务在大多数平台上是在 render 线程执行,但是在某些平台上(比如PlayStation 4)是在 worker 线程执行的。
发送命令到 GPU 的时候,最消耗的操作是 SetPass call 。如果我们游戏的 CPU 密集是由发送命令到 GPU 造成,减少 SetPass calls 的数量可能是最好的改善性能的方法。
我们在 Unity 的渲染分析器中看到当前有多少 SetPass calls 和 batches 正在发送。在性能不下降的前提下,SetPass calls 数量高度依赖目标硬件。在不降低性能的情况下,一个高端的 PC 机比一个手机能发送更多的 SetPass calls 。
SetPass calls 的数量以及它与 batches 数量的关系,取决于几个因素,我们稍后在文章进行详细的介绍。通常会有以下几种情况:
如果减少了 batches 的数量,没有减少 SetPass calls 的数量,这仍然导致自身性能的改善。这是因为 CPU 处理单个 batch 比处理几个 batches 要高效,即使他们的网格数据同样多。
大体上有三种减少 batches 和 SetPass calls 数量的方法。我们将会更深入的研究每个方法:
不同的技巧适应不同的游戏,所以我们考虑这里所有的选项,决定哪些在我们的游戏和实验中能够发挥作用。
减少被渲染物体的数量
减少被渲染物体的数量是减少 batches 和 SetPass calls 数量的最简单的方法。这里有几种可以减少渲染物体的方法:
减少每个物体被渲染的次数
实时灯光、阴影和反射为游戏增加了很多真实性,但是这非常耗性能。使用这些特性可以导致物体被渲染多次,这会大大的影响性能。
这些特性的确切的影响取决于我们为游戏选择的渲染路径(rendering path)。渲染路径是绘制场景时计算的顺序的术语,渲染路径的主要差别在于它们如何处理实时灯光、阴影和反射。一般来说,如果我们的游戏运行在高端硬件上,并且使用了一些实时灯光、阴影、反射,那么延迟渲染( Deferred Rendering )可能是一个更好的选择。如果我们的游戏运行在低端设备上,并且没有使用这些特性,那么正向渲染( Forward Rendering )可能更合适。这是一个非常复杂的问题,如果我们希望使用好实时灯光、阴影、反射,最好是研究这个主题并实验。这个 Unity 手册介绍了不同的渲染路径的信息。这个教程包含了一些有用的 Unity 灯光的主题信息。
不管选择什么渲染路径,使用实时灯光、阴影和反射会影响我们游戏的性能,明白如何优化它们是很重要的。
减少批处理数量
当条件满足的情况下,一个批处理包含的数据可以为多个物体使用。批处理要满足以下条件:
批处理可以改善性能,尽管和其他所有优化技巧一样,我们必须细心的分析确保批处理的消耗不会超过性能的获取。
以下有几个不同的技巧:
裁切,排序和批处理
裁切,收集将要绘制的物体的数据,将这些数据排序成 batches 并生成 GPU 命令,这些操作都可能会导致 CPU 密集。
这些任务会在 main thread 线程执行或单个 wroker 线程执行,这取决于我们的游戏设置和目标设备。
网格蒙皮
当我们使用一个叫网格动画的技术来变形网格的时候,SkinnedMeshRenderers 就会被用到。通常使用在角色动作上。渲染蒙皮网格的相关任务一般在 main thread 或单个 worker threads 上执行,这取决于我们游戏的设置和目标设备。
渲染蒙皮网格可能是一个很消耗性能的操作。如果我们在 Profiler 窗口发现渲染蒙皮网格是造成 CPU 密集的原因,那我们可以通过以下几个方法来改善性能:
与渲染无关的主线程操作
很多跟渲染无关的 CPU 任务会发生在主线程,明白这一点很重要。这意味着,如果我们主线程是 CPU 密集,我们减少 CPU 在非渲染任务上的耗时可能可以改善性能。
比如,我们游戏主线程在某些时候执行耗时的渲染操作和运行耗时的用户脚本造成了 CPU 密集。如果我们已经在尽可能不失真的情况下优化渲染操作,我们可能要减少用户脚本的消耗以改善性能。
如果我们的游戏是 GPU 密集,首先要找出造成 GPU 瓶颈的原因。GPU 性能问题通常由填充率( fill rate ) 受限制引起的,特别是手机设备上,但内存带宽些问题并了解是什么原因造成的、分析并修复这些问题。
填充率
填充率指是 GPU 每秒在屏幕上渲染的像素数量。如果我们游戏是填充率受限制,这意味着我们游戏尝试绘制的像素超过 GPU 每帧能处理的像素。
很容易检测我们游戏 GPU 密集是否是由填充率造成的:
如果填充率是造成问题的原因,下面有几个方面可能能帮助我们修复问题。
内存带宽是指 GPU 在专用存储器的读写速率。如果我们游戏被内存带宽限制,通常意味着我们使用的纹理太大以至于 GPU 无法快速处理。
检测游戏是否存在带宽问题,我们需要以下几个步骤:
如果我们的游戏存在内存带宽问题,我们需要减少游戏中图片(Texture)内存的使用。我们可以通过下面几个方法优化我们的纹理:
顶点处理
顶点处理是指 GPU 必须在网格中渲染每个顶点的工作。顶点处理的消耗受两个地方的影响:必须渲染的顶点数量和每个顶点必须执行的操作数量。
如果我们的游戏确定是 GPU 密集,并且不是填充率造成的,也不是内存带宽造成的,那么可能是顶点处理造成的。如果是这种情况,尝试减少 GPU 必须处理的顶点处理数量,可能会获得性能的提升。
我们可以考虑几种可以帮助我们减少顶点数量或减少我们执行每个顶点的操作数量。
我们已经学习了 Unity 的渲染工作,渲染时会出现什么问题以及如何改善我们游戏的渲染性能。使用这些知识和分析关系,我们可以修复渲染相关的性能问题来使我们的游戏有一个更加平滑高效的渲染管线。
本文尽量保持跟原文排版一致,翻译能力有限,以下是官方源地址:https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/optimizing-graphics-rendering-unity-games?playlist=44069
若外部链接失效,csdn搜索公众号名,希望本文对你有帮助,欢迎关注公众号:hellokazhang,一个不给自己设限的终身学习者。
推荐阅读:
http://weixin.qq.com/r/TER9Zd7EsYPOrZoG9xFe (二维码自动识别)
///
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。