其他
经验分享 | 优化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——但是我手边已经没有开发包了,也许我应该买一个。
好了,这就是简介。下次从上面的“WAT?”列表中提取一些东西并且尝试对它们做点什么!
经验分享丨项目实践项目孵化丨渠道发行做有梦想的游戏人-GAME AND DREAM-