查看原文
其他

经验分享 | 优化Unity渲染器第一部分:简介

2016-06-02 赵菁菁 Gad-腾讯游戏开发者平台
在工作中,我们建立了一个小的“突击队”来优化Unity渲染的CPU。我会在博客中记录我做的部分(看起来普遍接受的做法),我不知道我会走向哪里,但那就是乐趣的一部分!
背景/基本提醒
我会在许多情况下很刺耳地说“这代码烂透了”!当试图改进代码时,你当然想要改进糟糕的部分,所以,这一部分通常是重点。但这不意味着基本代码整体上都那么糟,或者代码不能用来做好事!!就在今年三月份,我们已经有了Pillarsof Eternity, Oriand the Blind Forest and Cities:Skylines,它们都是评分很高的PC游戏,而且都是用Unity制作的。对于“只利于原型设计的终端设备”来说并没那么糟糕。
事实上,任何一段开发了很长时间并且经手一部分人的代码很容易产生某种意义上的“糟糕”。有的地方代码很怪异,有些地方没人能够想起这些代码是如何以及为何起作用。有些决策是许多年前做的,现在已经不再有什么意义了,而且也没有人有时间去修正。在一个足够大的代码库中,任何一个人都不能完全了解全部代码起作用的细节,一些决策与其他决策以微妙的方式相冲突。套用某人的话:“有烂透了的代码库,也有不用的代码库” :)。但尝试改进代码库是非常重要的!永远要坚持改进。我们已经在所有领域都做了许多改进,但是坦白讲,在过去几年里的渲染代码改进已经取得了长足进步,但没有人仔细看看代码整体,也没有人把这作为全职工作专注于改进代码。我们是时候做这件事了!很多次我会指着一些代码说:“哈哈!那太傻了!”而那些代码当初就是我写的。可能现在我更明白,或者当时那些代码有意义,或者考虑到各种各样的因素导致那些代码是有意义的(比如赶时间),或者可能我那时候就是笨。也许五年后我看自己现在的代码也会这么说。
不管怎样……
愿望列表
在图片中,这就是我们想达到的最终目标——一个高吞吐率的、运行没有瓶颈的渲染系统。

除了此图,这里的“这是什么”部分几乎全部来自我们的工作维基:

目前的渲染(Unity 5.0)、着色器的运行环境和图形API的CPU代码“不是很有效”,它存在一些问题,我们想要尽可能多地处理这些问题:
GfxDevice(我们的API概述):概述主要围绕DX9概念设计,部分围绕 DX10。例如:恒定/统一的缓冲区现在已经不适用了。一个已经有组织地发展了许多年的“混乱”,有地方需要整理。
在现代API中允许并行命令缓冲区的创建(像控制台、DX12、Metal或者Vulkan)。
渲染循环:存在许多的小型低效部分和冗余决策,想要优化。并行运行一部分循环,并且/或者将一部分工作化,尽可能使用本地命令缓冲区创建API。让代码更简单更统一,提取出相同的功能,让代码可测试性更高。
着色器/材质运行环境:记忆中的数据布局“不是非常好”。代码复杂,想要整理一下,让代码可测试性更高。“固定功能着色器”概念在运行中不应存在,请看【在导入时生成固定功能着色器】。基于文本的着色器格式很傻,请看【二进制着色器序列化】。
约束条件
无论我们想做什么来优化/整理代码,我们应该尽可能让功能起作用。一些几乎不用的功能或者极端案例可能被改变或者被破坏,但那是迫不得已的情况。

同时,程序员需要考虑一些代码是否看起来很复杂,这可能归咎于一些原因,其中一个是“某人写了太复杂的代码”(太好了!简化它!);另一个可能是“过去这个代码复杂是有一些原因的,但现在没有了”(太好了!简化它!)。
但是也有可能代码在做复杂的事情,比如:它需要处理一些棘手案例。可能代码可以简化,但是也有可能它不能简化。开始“从头重写东西”很有吸引力,但是在一些情况下,你的新的、美观的代码可能在你开始做与旧代码相同事情时就会变得复杂。计划
假如有一块CPU代码,有几个提升CPU性能的关键:1)“运行更快”2)“并行性更好”。我想我首先会专注于“运行更快”。一部分是因为我也想要简化代码并且记住代码要做的不同事情。简化数据,让数据流动更清晰,让代码更简单通常也会使第二步(“并行性更好”)更容易实现。
首先,我会着眼于更高层次的渲染逻辑(“渲染循环”)和着色器/材质的运行环境,但是组里的其他人会着眼于简化和绿化渲染API的抽象,之后再按照“并行性更好”的方法进行试验。
为了测试渲染性能,我们需要一些实际内容来测试。我已经看了我们拥有的几个游戏和例子工程,也已经让它们CPU受限(通过降低GPU负载——以低分辨率渲染;减少多边形参数;降低阴影贴图分辨率;减少或移除后处理步骤;降低纹理分辨率)。为了给CPU加更高负载,我重复了场景部分,这样会比最初有更多的渲染对象。
测试像“嘿,我有这100000个立方体”的例子的渲染性能很容易,但是那不是一个非常现实的用例。“大量对象的使用需要相同材质而没什么其他要求”和几千个参数不同的材质、几百个不同的着色器、几十个渲染目标变化、阴影贴图&常规渲染、Alpha混合对象、动态生成的几何图形等的渲染场景是不同的。
另一方面,测试一个“完整游戏”也很难处理,如果该游戏需要交互、加载完成很慢,或者开始时不是CPU受限的,会特别难处理。当测试CPU性能时,在不止一台设备上测试很有帮助。我通常在一台Windows系统(目前是Core i7 5820K)的开发PC上、一台Mac系统(2013 rMBP)的笔记本上和我手边任意一台iOS设备(现在是iPhone6)上测试。对于这个工作来说,在控制台上测试就太好了;我一直听说他们有极好的性能评测工具,或多或少的固定时钟和相对低配的CPU——但是我手边已经没有开发包了,也许我应该买一个。
笔记
接下来,我运行基准测试项目并观察分析器数据(Unity分析器和像Sleepy/Instruments这样的第三方分析器),同时我也观察代码看看它在做什么。这时,每当我看到一些奇怪的事时,我都会把它记录下来,作为之后研究的依据:
上面的一些奇怪内容可能有合法原因,在这些情况下我会增加代码注释,一些内容可能曾经有原因,但是现在不再合法。在两种情况下,源代码控制台日志/注释的功能会很有帮助,而且应该问问那些本来写这些代码的人为什么代码是那样的。上面列表的一般可能都是因为我在许多年前就是那么写的,这意味着我必须记住那些原因,即使它们“在当时看起来是个好主意”。

好了,这就是简介。下次从上面的“WAT?”列表中提取一些东西并且尝试对它们做点什么!
相关阅读:快速入门 | Unity StrangeIoc 框架介绍Unity性能优化专题---腾讯牛人分享经验
腾讯游戏开发者平台长按,识别二维码,加关注
经验分享丨项目实践项目孵化丨渠道发行做有梦想的游戏人-GAME AND DREAM-

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存