请选择 进入手机版 | 继续访问电脑版

开源计算机图形学社区(Open Source Computer Graphics Community) |OpenGPU Forum (2007-2013)| OpenGPU Project

 找回密码
 注册
搜索
查看: 3064|回复: 5

KlayGE 4.10中渲染的改进(三):优化 [复制链接]

Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28

注册时间
2009-3-31
积分
14455
发表于 2016-12-29 14:13:19 |显示全部楼层
转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=3510

上一篇讲了tone mapping的改进。作为引擎的一个长期议题,优化是不可缺少的。本篇就讲讲在4.10中引入的新优化。

CPU端
在profiler里看到的占据CPU耗用第一名的一直是驱动。原先一直没在意这个,前一阵自己看了一下,发现前几位的好几个都是在SceneManager里,而且都和渲染队列相关。具体情况是,在每一帧确定渲染队列的时候,会执行一遍这样的步骤:
  • 扫描一遍场景里的所有SceneObject,根据它的Renderable的类型建立一个从Renderable到SceneObject列表的unordered_map,每个物体作为那个Renderable的instance
  • 把unordered_map中的Renderable建立一个队列
  • 渲染这个队列
  • 销毁unordered_map

所以其实这里的unordered_map只是个临时的对象,会频繁的建立和销毁。与此相关的大量内存申请、释放、排序,其实都是临时使用一下而已。而存成unordered_map的原因,也只是为了快速找到Renderable。实际上在每一个Renderable里,已经有一个vector,用来保存instance信息。所以在新版本里,我就改成了把SceneObject直接放到那个列表里。这样一来不需要建立临时的结构,而来可以直接从Renderable找到SceneObject,不需要搜索map。修改之后的步骤就变成:
  • 扫描一遍场景里的所有SceneObject,把它放入对应的Renderable的instance中,同时记录一个Renderable的队列
  • 渲染这个队列

没了这个临时结构,CPU部分每帧的开销降低了10%左右。

CPU和GPU之间的通信
由于每帧有大量的buffer用来从CPU到GPU传递数据,而这些buffer之前都是通过Map/Unmap来存入数据的。在D3D11中,Map/Unmap是同步的,这会造成一定的等待时间。而UpdateSubresource是异步的,等待时间大大少于Map/Unmap。改成尽可能用UpdateSubresource之后,整体性能提升了1-5%。虽然不多,但也比没有好。

以前要用Map/Unmap的原因是,不管AMD还是NV,在GDC上都推荐用Map/Unmap,说那个更快。实际测试的结果是,对于buffer来说,UpdateSubresource总是比Map/Unmap快。对于texture来说,在2015年之后的驱动上,UpdateSubresource绝大部分时候比Map/Unmap快。所以换成UpdateSubresource是有好处的。

GPU端
自从KlayGE 4.5增加了基于CS的TBDR之后,整个TBDR就是写在一个shader里。整个shader分为两个阶段。第一个是tiling,把每个tile的frustum和光源求交。如果存在相交,就把光源放到一个列表里。第二个阶段是shading,每个tile里的每个像素跟列表里的光源做shading计算。这个光源列表放在groupmemory,在同一个group里的thread可以共享。

实际上这两个阶段对于内存和thread的使用差别很大。Tiling阶段是per-tile的,shading阶段是per-pixel的。Tiling阶段主要读写groupmemory,shading阶段主要读写texture。放在一起并不是特别有利于GPU。但因为在实现TBDR的时候,其实就已经打算在某个时候切换到cluster shading(现在确定了4.11就要完成这个切换),一直没仔细优化这个地方。现在突然发现在GTX960上,CS的TBDR速度还不如PS的LIDR,而且切换到cluster shading也需要把这两个过程分开,所以在这个版本里就干脆优化一下。

结果就是把这两个阶段放到两个shader,之间通过texture做数据交换。经过一定的thread数调整之后,终于,让性能有了10%-50%的提升。还是蛮明显的,尤其是光源数量多的情况下。

D3D12
虽然去年就写好了D3D12的插件,但一直以来都只是能用,并非好用。速度只有D3D11的1%。这个版本里,profile的结果表明一大问题出在buffer的建立。之前因为偷懒,对于WRITEDISCARD类型的buffer,实现方法是每次都建立一个新的,删掉旧的。几乎所有时间都消耗在这里了。现在改成每个buffer保存一个D3D12Resource的列表,需要的时候找个空闲的使用,用完了标记成空闲,放回这个列表。这么一来,建立和删除buffer的次数远远下降了。D3D12插件的速度提升了10倍。

不过现在对于空闲buffer的检查还是每帧一次,不是每个drawcall一次。以至于buffer的数量还是不少。以后需要更精细地通过fence控制,降低内存消耗。

总结
KlayGE 4.10里还有一些改进,比如volumetric post process、blitter、asyn swap chain等,时间有限就不在这里写出来了。很快4.10就会发布,并迅速进入4.11的开发中。届时会有更多的改进会加入。

最专业的游戏引擎、图形编程、业界资讯,请见http://www.klayge.org

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

注册时间
2009-3-27
积分
4250
发表于 2017-1-10 05:35:07 |显示全部楼层
本帖最后由 Phantom 于 2017-1-10 05:40 编辑

龚大,我一直对D3D12的同步问题比较迷茫。
初始化/更新资源数据一定要排空管线吗?我看Klay里面好像是这么做的。如果我能人为保证一个资源(比如 ConstantBuffer)当前没有绑定到管线上,没有任何一处地方使用到它,这时候对它进行数据更新也需要排空管线吗?

还有我跟了一下Klay的代码,RenderEngine在提交CommandList的时候一般都是在不得已必须提交的时候才去做。这个一般不得已的时候大多数都是更新资源(尤其是ConstantBuffer)。
我有一个想法,我这样做能不能行得通:
假设当前一帧只有一个RenderTarget作业,也没有PostProcess,场景中有10个物体需要渲染。
在这个前提下我把这10个物体设计成在渲染的时候对需要复用的资源进行实例拆分,举例:
每个物体都至少需要一个位置信息,表现在WVP矩阵,让每个物体都有一个自己的WVP的ConstantBuffer,而不是只建立一个CB每画完一个把下一个物体的WVP更新进去,这样我可以在再开画之前先把10个CB的数据填满,然后等一次同步,画的时候一气把10个物体全部画完中间不再再因为更新数据而被打断。这样是否可行?
当然WVP矩阵只是最典型的例子,如果10个物体只要在渲染上有任何复用资源需要更新,我就把复用资源拆分成每个物体拥有一个资源实例。
这样是否有提速的意义呢?


----------------------------


另外,还有哥问题。我是个.net得小白,请教一下Klay的编辑器的项目C#和C++工程之间有有一个Wapper工程,那个工程到底是C++/CLI项目还是C++/CX项目啊?怎么区分呢?

使用道具 举报

Rank: 16Rank: 16Rank: 16Rank: 16

注册时间
2010-7-23
积分
2950
发表于 2017-1-10 10:34:24 |显示全部楼层
恭喜龚大贺喜龚大

使用道具 举报

Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28

注册时间
2009-3-31
积分
14455
发表于 2017-1-11 03:49:13 |显示全部楼层
Phantom 发表于 2017-1-10 05:35
龚大,我一直对D3D12的同步问题比较迷茫。
初始化/更新资源数据一定要排空管线吗?我看Klay里面好像是这么 ...

是的,就应该是那样更新CB。

wrapper是C++/CLI。因为不是WinRT的。

使用道具 举报

Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17

注册时间
2009-3-27
积分
4250
发表于 2017-1-12 05:02:12 |显示全部楼层
gongminmin 发表于 2017-1-11 03:49
是的,就应该是那样更新CB。

wrapper是C++/CLI。因为不是WinRT的。

是指只要更新资源的数据,无论什么情况都需要排空管线吗?

使用道具 举报

Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28Rank: 28

注册时间
2009-3-31
积分
14455
发表于 2017-1-12 14:39:20 |显示全部楼层
Phantom 发表于 2017-1-12 05:02
是指只要更新资源的数据,无论什么情况都需要排空管线吗?

我的意思是,更新就应该像你说的那样,都有自己的cb或者同一个cb的不同区域,填满后画。KlayGE现在那么写是因为偷懒了。

使用道具 举报

最近看过此主题的会员

您需要登录后才可以回帖 登录 | 注册

‹‹
我的工具栏

关于我们|手机版|Archiver|开源计算机图形学社区(Open Source Computer Graphics Community) | OpenGPU Project | OpenGPU Forum (2007-2013)

GMT+8, 2017-11-20 01:38 , Processed in 0.068941 second(s), 13 queries .

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部