您的位置:首页 > 移动开发 > Unity3D

Unity5内部渲染的优化1:介绍

2015-09-02 12:17 435 查看
译自aras的博客,总共3篇文章,讲述unity5优化自己渲染器的过程

吸取大神调试与优化经验

在工作中我们形成一个小组“strike team”来优化unity渲染的cpu部分。

基本信息/父本的警告

在很多情况下,我要严厉的说“这段代码很烂!“。当要努力改善代码的时候,你当然想提高不好的地方,这是重点。

一般来说并不意味着代码库是坏的,或者它不能用于做出好东西。就在今年3月,我们有Pillars of Eternity, Ori and the Blind Forest and Cities: Skylines among top rated PC games; 这些游戏都使用unity做的。“手机引擎只适合原型设计”这点还不太糟。

事实是,任何代码库,在很久的时间开发使用,并只有很少的人使用,在某种意义上真是糟糕。他们是代码奇怪的地方。没有人记得它们是怎样工作的,因为是在很多年前做的,没有意义了,也没有人来修复。在一个足够大的代码库,没有一个人可以知道所有的关于它如何工作的详细信息,所以在其他一些微妙的方式有一些决策冲突。借用某人的一句话“there are codebases that suck, and there are codebases that aren’t used” :)

努力改善代码库是很重要的!我们一直在坚持在改善它。我们已经在所有的方面都做了大量的改进,但坦率地说,渲染代码在近几年改进的非常多,没有人把单独维护和提高带代码成一个全职的工作,但是我们做了!

好几次我指着一些代码,说“哈哈!那太愚蠢了!”这是我写的代码。或者是有各种因素的代码(缺乏时间等)。也许我当时是愚蠢的,也许我在五年后会说一样的话。

愿望清单

系统的高吞吐量,没有瓶颈的工作。

(Unity5.0)现在的渲染,,着色器运行&图形API的CPU代码并不是非常有效率。它有一些问题,需要我们尽可能多的解决:

1. 图形加速器(Gfx)设备(我们的抽象渲染API)

a.抽象主要是围绕DX9 / 部分的DX10的概念设计。例如常数/统一(constant/uniform)缓冲现在并不适合。

b.过了几年越来越混乱,有些地方需要清理

c. 允许并行命令缓冲区创建现代API(如consoles, DX12, Metal 或 Vulkan)。

2.渲染循环

a.许多无效的东西和多余的决策需要优化

b.并行运行的循环和/或它们的jobify部分 。如果可能的话,使用原生的命令缓冲创建API。

c.让代码更简单更统一。分析出常用的功能。更多的可测试性。

3.着色器/材质的运行

a.数据在内存中的布局并不是特别好

b.想要清理复杂的代码。增加可测试性。

c. “固定功能着色器(Fixed function shaders)”的概念在运行时不应该存在。见【Generating Fixed Function Shaders at Import Time】。

d.基于文本的着色器格式是愚蠢的.见【Binary Shader Serialization】

限制

无论我们优化/清理代码,我们都应该尽可能保持他们的功能可以工作。一些很少使用的功能或特殊的情况可能被改变或破坏,但这只是作为最后的手段。

还有一点需要考虑,如果一些代码看起来很复杂,它也许由于以下几个原因产生的。其中之一就是“有人写的代码太复杂”(太棒了!我要简化它)。另一个可能是“有一些代码因为一些原因在过去是复杂的,但现在不是了”(太棒了! 我要简化它)。

但是也有可能代码做了复杂的事情,例如它需要处理一些棘手的情况。开始从头“重写”代码很容易,但在某些情况下一旦你开始让你的又新又好的代码做旧代码所做的一切时,它可能变得复杂。

计划

增加一段CPU的代码,通过几方面改善它的性能:1)“只是使它更快”和2)使它更并行。

我想首先关注“只是使它更快”部分。因为我也想简化代码,又想到要做很多棘手的事情。简化数据,使数据流更加清晰,使代码更简单往往还是做第二步(“更并行”)更容易。

首先我会看着更高水平的渲染逻辑(“渲染循环(rendering loops)”)和材质/材料运行,团队中的其他人将考虑简化和处理抽象渲染API,并尝试“更并行”的方法。

进行渲染性能测试,我们会需要一些实际的内容去测试。我看了现有的几个游戏和演示,让他们的CPU被限制 (通过减少GPU负载-低分辨率渲染;减少多边形数,减少阴影贴图;减少或消除后期处理;减少纹理分辨率)。让CPU有更高的负荷,我复制了部分场景,所以要比原来的渲染得更多。

渲染测试非常简单,比如“嘿,我有100000个cubes!”但这并不是一个现实的例子。“只是大量使用相同材质的大量的对象”是一个非常不同的渲染情况,从用不同参数的成千上万的材质,数以百计的不同的着色器,几十个渲染目标的变化,阴影贴图&定期渲染,对象的alpha混合,动态生成几何体等。


另一方面,测试一个“完整的游戏”也非常麻烦,特别是它有需要交互的地方,缓慢加载所有关卡,或不限制CPU。



当测试CPU性能时, 在多个设备上测试很有帮助。我通常在PC Windows 系统(i7 5820 k),在Mac笔记本(2013 rMBP), iOS设备怎样都好我现在在用 (iPhone 6)上进行测试。在控制台上测试将会很棒,我一直听说到他们有很棒的分析工具,或多或少固定的时钟和cpu——但我并没有devkits。也许这意味着我应该弄一个。

注释

接下来,我运行了标准的项目看了分析的数据(包括unity分析器和第三方评测器不活跃的困/工具) ,也看了代码看它做什么。此时,每当我看到了一些奇怪的事情于是我把它写下来供以后调查:




以下来自上图翻译

setPassWithShader 在某些点来避免PPtr dererf的最优化方式。现在他看起来总是做PPtr dererf,然后只要调用SetPass(又做了一次dererf)。

材质显示列表不断的重建>1的每像素光。不需要储存多于一个列表的材质

GetTextureDecodeValues被调用了很多次(建立像素光cookie的一些东西),结束无用的线性gamma转换

材质显示列表不断地重建由于未赋值的全局贴图属性而产生连锁反应(_Cube ,没有赋值),在我们的属性丢失了的时候同时需要找出为什么我们会失败并做记录

GpuProgramParameters::MakeReady是做什么的?为什么把它区分?

为什么STL maps被属性表使用?

1. 只使用健全的&简单的数据布局

2. 相关的怪事:

1. 为什么TexEnv 从属性表中分离?

2. SetRectTextureID-为什么,什么

3. 贴图 像素宽度/HDR 解码性能的一部分在设备状态中执行

4. NotifyMipBiasChanged 有很多复杂的事情,原不明

IsPassSuitable在渲染循环中一次又一次的被调用。也许在渲染循环中要建立direct pass 指针表?

一次性提供所有贴图取代在某一时间SetTexture

在内存中安排属性表数据来匹配真实不变的缓冲布局。可能需要几个不同关键的字布局

TextureID转换为long(mac/linux中是64位)被ps4特殊提交3cbd28d4d6cd

1. 看起来像只在ps4上的优化,直接存储一个指针到TextureID。如果可以的话无论任何地方都这么做,或者只在ps4把它做成long(intptr_t更好)

为什么ChannelAssigns 和VertexComponent在所有时间都能通过吗?似乎没有什么用处

渲染循环分类是非常昂贵的。基于哈希分配(渲染循环分配)开启并完成它。

上面的一些缺陷可能由某些原因产生,在这种情况下,我添加注释去解释它们。可能当时有一些原因,但现在没有了。在这两种情况下源控制日志/注释功能是非常有帮助的,并问写代码的人当初为什么会这样。上面的列表的一半也许因为我以这种方式写很多年了,这意味着我必须记住这些原因,即使他们“在当时似乎是一个好主意”。

这就是介绍。下一次,会对上面的清单做点什么!

下一篇待译

------译自 wolf96 http://blog.csdn.net/wolf96
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: