• gkENGINE渲染优化


    gkENGINE上次总结了一篇开发总结(上)。然后二进制DEMO发到了OPENGPU上,算是引起了挺多的关注。

    友情链接一下,openGPU帖子:

    http://www.opengpu.org/forum.php?mod=viewthread&tid=15246

    效率这个问题被很多大牛提及,因此,正在撰写的开发总结(下)被我停了下来。认真的分析渲染流程,找到性能瓶颈,尝试修改,突破。再分析新的性能瓶颈,尝试修改,突破...

    整个优化完成下来,渲染器结构也重构了不少,接下来继续总结渲染流程应该更加得心应手。

    题外话:gkENGINE之前的二进制DEMO已经上传至项目主页

    https://gkengine.codeplex.com/

    gkENGINE 项目已经决定开源,代码正在整理中,准备工作完毕后就上传至codeplex托管,欢迎到时各位朋友捧场,相互学习!

    终于,经过两周的业余时间,把DEMO的开场镜头的效率提升到了240%。期间的一些有意思的分析和优化方法,值得记录一下。

    MISSON START                                                                                    

    当时是一个周末,先在程序中将DEMO中截图的那个镜头锁定了下来。然后开始用Intel GPA这个图形分析工具开始分析。

    插一句,GPA是Intel出品的一款图形分析工具。类同DirectX SDK自带的PIX和 Nv著名分析工具PerfHUD。GPA的最大优势是使用便捷,稳定性高。个人认为这是PIX和perfHUD的弊端。因此一般的简单分析任务我都选用GPA来完成。

    工具的下载页面在这里:http://software.intel.com/en-us/vcsource/tools/intel-gpa

    不过要说的一点是,虽然GPA已经更新到了2013 R2版本。但是个人觉得最好用的还是历史的4.3版。对nvidia, ati的显卡支持得不错。后继版本的支持就显得不那么友好了。并且坑爹的是,好像Intel官方不再提供以前的版本,需要在其他网站搜索下载...

    接下来,便得到了精确的GPU时间数据和各阶段的详细资源:

      渲染效率:GTX560  104frame/s   9.6ms/frame

    这是对0425DEMO版本,第一个镜头的GPA时间分析。简要分析,将一些不太合理的瓶颈标红,如下:

    SSAO:全屏的AO计算,占用了整体场景着色时间的一半。

    SHADOWMASKGEN:阴影mask的生成,总体消耗的时间和SSAO等同。

    POSTPROCESS:FOG, HDR, DOF等后处理效果,消耗时间也很高。

    REFLECTMAP:  反射图的生成,在此场景中没有水面,REFLECT MAP的生成应该被裁剪掉。

    第一轮,与分辨率的战斗

    以上的瓶颈,都是后处理算法,都是像素计算密集型的渲染算法,因此,如果能够在基本保证渲染质量的情况下,显著的降低像素计算复杂度,那么性能将会得到成倍的提升。

    1. SSAO优化

    SSAO的shader,汇编指令169, 11个采样指令。1280x720分辨率,每帧执行92W次。根据场景复杂度,优化了一下旋转纹理的采样方式,使用半分辨率的SSAO处理(降采样)。可以将计算量降低为1/4,而质量下降很低。

    2. ShadowMask优化

    采用相同策略,使用半尺寸渲染,将计算量降为1/4。不过这时在后面的着色阶段,会产生渲染错误。

    如图,由于MASK采用半尺寸,因此在全尺寸的着色阶段,会由于线性采样,在阴影和受光的交界像素处采样到非阴影值(树干的边缘,后面的茅屋阴影有非阴影白边)。

    解决的办法是,在着色阶段,在采样点都右下方像素多采样一个阴影值,两者取最小值作为当前像素的阴影值,过滤掉这个渲染错误。这个解决方案也有必然的弊端:可能会在本身没有阴影的地方产生一定程度的阴影黑边。但是相对白边,黑边造成的瑕疵完全可以接受。

    3. POST PROCESS优化

    之前的POSTPROCESS有很多RT来回倒腾的操作(紫色的矩形块)。经过RT的合理分配和顺序调整,可以去掉一些RT STRETCH的操作,提高POST-PROCESS的效率。

    4. 终极优化 - 可变渲染分辨率

    之前都是针对各种特性渲染的降采样,来降低像素计算压力。但是在移动平台上,提升依然不是太明显,因此,需要一个终极的解决方案。经过PHOTOSHOP里测试,使用3/4的尺寸渲染,然后“放大”到全尺寸。最终的画面质量下降不是太大。但是像素计算量能直接下降为接近1/2。带来的性能提升是十分显著的。因此在之前的texture管理器中添加了一个scale属性。对除BACKBUFFER之外的所有纹理进行降采样处理。渲染完成后,STRETCH到BACKBUFFER上。

    第二轮,解决新问题

    第一轮优化暂告一段落,和分辨率的斗争结束了。渲染效率直接提升了接近100%。当时就在OPENGPU上补充了一个跟帖,效率的确是提上去了,不过也有坛友提出质量下降的问题。

    1. 为低分辨率渲染添加锐化pass

    如上图所示,的确,使用3/4的渲染分辨率,少了近一半的像素,质量下降难免,如坛友所说,结果像是图片经过了质量压缩。但是这个下降是否可以再补偿回来一些呢?

    经过一些调研,轻微的尺寸缩放可以通过锐化来补偿。于是开始尝试之前做过的锐化算法,将图像进行微弱的高斯模糊,再利用模糊结果和原图进行线性外插,强化像素的“反差”,达到锐化目的。

    color = lerp( blur, curr, sharpvalue ); // sharpvalue取值大于1

    2. 使用手动MIPMAP解决地形颗粒感过重的问题

    对于有坛友提出的颗粒感过重的问题,因为terrian的多层混合是直接在shader中计算的,由于纹理的重复采样是直接在shader总通过frac得出,因此打开mipmap会有采样错误(由于frac计算出的texcoord不连续),之前图省事直接关闭了mipmaping。因此这里用了一个简单的方法,解决这个问题:利用像素的线性深度,手动计算应该采样的mipmap层数(避免使用自动的ddx计算,造成地块间的不连续值),然后使用texlod来取得对应Mipmap的值。

    这时再观察GPA的数据。

    这时之前的几个瓶颈已经被压到合理的消耗范围。渲染时间大部分集中在了shadowmap生成,zpass, general pass上。这算是一个合理的渲染管线消耗分配了。

    但是也可以注意到,图标中标黄的两个消耗块,占据了相当大的时间,已经超过了SSAO和SHADOWMASK的消耗。

    这两个消耗,就是地形系统中占屏幕像素最大的那个block。分析他们的shader assembly,发现每个像素采样的次数达到了惊人的26次!(zpass 7次, general pass 19次)

    3. tex2dlod和tex2dgrad的选择

    而仔细看却发现tex_ld, tex_ldl的次数却没有那么多,通过搜素发现,原来tex2dlod这个函数可以显式的指定lod层数,消耗比tex2d, tex2dgrad都要大。在GPA中会显示两次采样指令。

    因此,将tex2dlod的方式改为tex2dgrad, 手动计算ddx送入插值而非直接指定mip层数。像素采样次数直接砍半。

    4. 高光纹理合并到diffuse的alpha通道

    同时,发现地形纹理使用高光贴图稍微有些浪费,索性直接将高光值做成单色存入diffuse的alpha,砍掉采样高光纹理的消耗。

    至此,地形block的采样次数从26次降低到了9次,地形block的渲染消耗直接降到了50%。

     

    第三轮,引入混合渲染管线

    经过前两轮优化,基本上性能已经被榨干至极限了。不开启锐化的情况下,在720p分辨率在GTX560上已经能跑到260FPS以上,接下来想要在保证画面质量的情况下,进一步尝试一下优化,突破点大概只有几点了:

    • 1. 降低DP
    • 2. 降低shader复杂度

    对于第二点,要保证渲染效果不变,这将是个漫长的优化-测试的循环过程。

    对于第一点,目前渲染管线是Deferred Lighting(延迟光照),CryEngine3使用的是这个方式。

    优点:在拥有延迟渲染解耦光照运算的优势下,能保证主光源丰富的材质效果,同时获得一个低带宽开销的渲染流程。

    缺点:所有不透明物体需要渲染两次:Zpass一次,输出法线和线性深度,GeneralPass一次,利用生成好的光照数据,和主光源,进行传统的材质运算。

    而Crytek在GDC2013的演讲中提出了Hybird Deferred Shading的概念,将之前实现的延迟光照和传统的延迟渲染进行混合,对于普遍材质使用延迟渲染方式,只需要一次渲染调用。而对于复杂材质,和以前一样走延迟光照的渲染流程。

    因此,我决定先引入传统的DeferredShading实现,先观察效率,并做成可以实时灵活切换渲染管线的架构,再考虑进一步的混合渲染方式。

    而要引入混合可切换的延迟渲染管线,就需要对之前的渲染流程进行改造。之前的流程是

    shadowmapgen -> zpass -> ssao -> deferred lighting -> shadowmask -> general pass -> fog/hdr/dof -> msaa -> output

    对于传统延迟渲染,大体流程都是需要的,但是在zpass, deferred lighting, general pass阶段是需要有不同的算法流程

    于是我将各个阶段抽象成IBasePipe, 通过策略模式,将算法包装进Pipe的派生类,然后主渲染流程调用各个pipe执行,选择对应的pipe实现进行渲染算法的组织。

    因此,渲染流程变为了

    pipe[shadowmapgen] -> pipe[zpass] -> pipe[ssao] -> pipe[shadowmask] -> pipe[deferred lighting] -> pipe[general pass] -> pipe[postprocess] -> output

    同时,延迟渲染还需要新的zpass和最后的合成pass的shader,同时,因为不能使用独立的generalpass了,因此之前的一些特殊材质效果一定需要损失。

    对于G-BUFFER,之前的配置是DEPTH|R32F + NORMAL+GLOSS|RGBA8。而延迟渲染需要记录物体的材质信息,所以至少需要多添加一层ALBETO颜色信息,所以GBUFFER在原有的基础上扩展了一个ALBETO DIF+SPEC|RGBA8的MRT。用于输出ALBETO颜色和单色的高光。

    对于物理属性,诸如:FRESNEL等,暂时没有写入。后期考虑压缩NORMAL,在NORMAL中多开辟一个通道来存储。

    渲染流程改为deferred shading后,DP少了一半,G-BUFFER的带宽压力增加了50%。但总体下来效率还是提高了5%左右。

    可惜的是,deferred shading要求更加统一的材质属性设置。所以,之前为延迟光照设置的材质属性,在延迟渲染下表现有了差异。考虑到提升并不明显,因此还是默认使用deferred lighing渲染管线。

     

    MISSION ACCOMPLISHED                                                                  

    任务完成,总结一下最终的效率。

    渲染配置:

    1280 x 720分辨率, 35.9W三角面, 362 DrawCall

    特效全开

    0.75的渲染尺寸,ssao, shadowmask一倍降采样

    测试结果:

    测试平台 帧率 帧时间
    Intel i5 2500K & Nvidia GTX560 241FPS 4.14ms
    Intel i7 3720QM & Nvidia GT650M 140FPS 7.14ms
    Intel i5 2500K & Intel HD Graphics 3000 30FPS 33.33ms

    效果对比:

    最下面的两张渲染结果分别是全渲染尺寸 和 shadow, ssao全尺寸的渲染结果。基本代表了优化开始时的渲染质量。

  • 相关阅读:
    CRM后期修改实体,新增货币类型字段 需要注意的问题
    CRM setValue方法日期类型字段赋值
    win10 ie11 以管理员身份运行才正常
    博客随缘更新,更多内容访问语雀知识库!
    解决Vulnhub靶机分配不到IP问题
    栈迁移原理图示
    【python】青果教务系统模拟登陆
    【二进制】CTF-Wiki PWN里面的一些练习题(Basic-ROP篇)
    2019"深思杯"山东省大学生网络安全技能大赛部分wp
    【web】docker复现环境踩坑
  • 原文地址:https://www.cnblogs.com/gameknife/p/gkengine_optmize.html
Copyright © 2020-2023  润新知