【厚积薄发】技术分享连载(八十二)| 实现动态合批的条件| 渲染模块需关注的参数指标| 移动端上的阴影实现方式...

来源:互联网 发布:猪八戒考试软件 编辑:程序博客网 时间:2024/06/05 02:05

转载自:http://mp.weixin.qq.com/s/feljdhK-VOhXDYCpFFnPiA

性能:实现动态合批的条件

  1. 只有一个cube,不接受、投射阴影,dc为1,如下所示:
    这里写图片描述
  2. 复制上面创建的cube,dc仍为1(动态合批),如下所示:
    这里写图片描述
    这里写图片描述
  3. 两个cube,一个quad,除了位置不一样,其他都一样:相同的缩放、相同的材质,且均未使用lightmap,但是只有两个cube合批了,quad未合批成功,如下所示:
    这里写图片描述
    这里写图片描述

  4. 测试过如果场景中有Plane,Quad和Plane会被合批,如下所示:
    这里写图片描述

  5. Frame Debugger也没有合批失败的相关提示,我的疑点是:
    1)是否跟透明有关?
    2)2个模型顶点属性个数不一致。
    但是我不知道如何获取这些信息,请问这是什么原因导致的呢?

  6. 动态合批的条件需要满足:

    • 相同材质,即指向同一份材质对象,参数一致;
    • 顶点属性不能超过900,目前看来Cube和Quad的顶点都很少,不太会超过900的限制。所以可能是材质的不一致,检查下两者的材质是否指向同一个,材质参数是否一样。
  7. 我用2010.1.0f3做了测试,确定是因为Cube的Mesh有uv2,而quad没有,导致无法合批,如图所示:
    这里写图片描述
    (1)在场景中场景建一个Quad, Batch数为1:
    这里写图片描述

(2)在摄像机上挂脚本,生成一个cube,当注释掉uv2设置为null的代码时,batch数为2:
这里写图片描述

(3)当打开注释,batch数为1:
这里写图片描述

同时,从Frame Debugger的结果也可以看到,此时Cube和Quad已经合并了。因此,我们认为Standard Shader对有UV2和没有UV2的Mesh进行了分别渲染,从而导致无法进行合批。

动画:在unity3d的官方文档中,animationclip.framerate的疑问

  1. “Frame rate at which keyframes are sampled. (Read Only) “
    对此,有一些疑问:
    (1)这个是否为动画系统每一秒钟更新动画的次数?如果是,当这个值高于application.targetFrameRate时,一秒钟更新动画的次数依据哪个为准
    (2)这个是否只是调节动画播放速度的一个参数,而每秒钟动画状态更新的次数不受影响?

  2. A:动画采样的时候是根据当前播放的时间去找对应的前后两个关键帧做插值。所以动画本身得告诉Unity两个关键帧(没做keyframe reduction时)的间隔时间是多少,或者一秒有几帧,这个值就是framerate。所以这个是只读的,导出时就应该是确定的。因此这个值也不会影响运行时动画的更新次数,默认情况下动画就是每帧更新一次的。

工具:unity中的Frame Debugger中的渲染顺序以及数量和profiler中看到的DrawCall数量,以及在高通的调试工具里看到的DrawCall数,他们之间有什么关系呢?哪些数值是影响渲染的重要指标?

FrameDebugger中的截图:
这里写图片描述
Profiler中的截图:
这里写图片描述
高通中截图:
这里写图片描述
说明:FrameDebug和profiler的截图是同一个工程,同一个视角在editor下的截图,高通工具的截图是Android机器上同意给工程,同一个视角的截图。
A:简单来说,unity引擎中Total Batches是我们建议最需要关注的指标。
1个setPass call或Batch,相当于是一次Render State的切换,而1个Drawcall则是cpu让gpu去渲染一个object的一次操作。在当前的移动设备中,1次Render State的切换比一个Draw Call本身要耗时,所以,TotalBatches是我们较为建议的关注指标,也是uwa性能报告中所提供的DC查看指标。而Frame Debugger中,其数量是与Total Batches相一致的,即查看的是每一个Batch的渲染物体。 更为详细一些的说明,可以查看https://answer.uwa4d.com/question/58d29b8b5a5050b366a6b6ae

而Unity Profiler中的Draw Call,其理论上对应的则是glDrawElements的调用次数,其与高通或其他第三方工具所返回的Gl Trace信息操作数量不太一致,但应该与其中的glDrawElements API的调用次数基本一致,题主可以自行检测看看。

该问题来自UWA问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59ddc3fa43cf099e2d2295be

性能:阴影

Q:在尝试优化DrawCall时,发现即使关掉了需要深度绘制的后期处理,深度绘制的pass也依然存在。按照传统对Shadowmap实现的理解,应该是只需要额外的Shadowmap的Pass即可。 查阅资料了解到Unity 5.0之后,Unity用了一种利用Depth Texture的方式来计算阴影:

“In Forward rendering, directional light shadows are computed from camera’s depth texture instead of a separate “shadow collector” rendering pass. Saves a bunch of draw calls, especially when depth texture is needed for image effects anyway.”

现在问题是:是不是在Unity 5.0之后,只要开启阴影,整个场景的一次深度Pass绘制就无法避免?如果没有其他效果需要用到深度Pass,单纯为了阴影进行一次深度Pass绘制,会额外增加深度Drawcall,可不可以理解为这种情况下,性能比之前的阴影实现方式(指的是5.0之前,只使用ShadowMap进行阴影计算的实现方式)要差?

A:1)如果是使用Unity引擎本身的实时阴影,那么无论是5.x还是4.x,其开启后,都会生成一张Depth Texture。但这个渲染物体并不是整个场景,而是需要投射阴影的物体需要多渲染一遍,而这个投射阴影的物体是可以进行手动设置的;

2)实时阴影Draw Call的增加主要取决于投射阴影物体的数量,一般来说,数量越多,则Draw Call增加越大,但并不是简单的线性关系。题主说的这个是PC平台的,在移动平台上并非如此,而是像我所回答的那样。在移动端,只会有一个Shadowmap,而没有updatedepthtexture的绘制。

该问题来自UWA问答社区,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59dc2ff0776b39742eece21d

性能:shader variant相关

Q:今天看了唐建伟的《合并Shader系列 | 如何合并渲染状态》,获益良多,但是也产生了一些的问题:
1)这样通过多参数来设置渲染状态不会造成Shader Variant吗?
2、什么情况下在才会造成Shader Variant呢?

A:1)不会。在具体回答之前,我先理清一下“Shader Variant问题”是什么问题,我的理解,题主是指Shader会编译出更多的Shader变体(Shader Program)。 在这篇文章中,我们只合并了渲染状态,渲染状态的合并不会导致Unity编译出更多的Shader Variant。

我们就先拿一个示例Shader来做测试,我选用了文章中的“ShaderCombine/01.ShaderCombineSimpleZTest”来做测试,Unity版本为 5.5.4p3,使用Unity的Shader Variant Collection来算取Variant数量,不管是否加入合并的代码,Variant的数量都是259。

即便是使用“ShaderCombine/02.ShaderCombineCommonState”来测试,Variant的数量也是259。

从上面的图片可以看出,不管是否有渲染状态的参数在里面,Variant的数量都不会改变。至于原因我写在下面这个问题里。

2)宏定义(Keyword),“#pragma multi_compile XXX YYY ZZZ”,“#pragma multi_compile_xxx”,SubShader,Pass,Fallback及一些特殊不常用命令等的增减会造成variant的数量变化。

在说之前,先说说什么是Variant吧。其实一个Variant可以理解为一个具体的在GPU上执行的小程序,而一个Shader通常会编译出非常多的variant来应对不同的情况,比如单说雾效就有如下:没有雾效、有线性雾、有指数雾1、有指数雾2这样的4个Variant(PS:这里只考虑雾效,其他条件一致)。至于原因嘛,有很多,粗略归纳一下是因为GPU需要更多的并行处理、逻辑单元少,因此Shader里面要尽可能规避各种判断、循环语句等等,最后本来可以通过逻辑判断来处理的雾效就需要编译成不同的执行程序,来对应不同的情况(PS:这是一种高级优化,背后的原理很多,建议自行查询相关资料,查明前因后果)。

至于一个Shader到底会生成多少Variant呢?精确的计算方法,Unity并没有给出,但是我的归纳总结一下就是几组不同的编译宏的组合了,比如雾效、光照图、光源、阴影等等。另外还可以通过Unity的工具Shader Variant Collection来查看一个Shader到底有多少个Variant,也可以在里面来自己组合和预编译自己想要的Variant。

最后回答一下第一问的原因。刚刚我们说了造成Variant数量增加主要是需要生成不同的Variant来应对不同的情况,那么不同的渲染状态是否情况不同呢?其实不是。生成不同的Variant主要是为了消灭Shader内部的逻辑判断(PS:Shader的真正逻辑是CGPROGRAM…ENDCG中间的东西)。继续用雾效举例,先只考虑有雾效和没雾效,按一般的游戏逻辑写法,我们通常会在逻辑里用一个if判断来搞定,但是由于GPU的特殊性,这样的做法非常低效、不可取,那么就会使用Keyword这样的编译宏在编译的时候就分别编译为有雾效和没雾效的2个执行程序,也就是2个Variant。现在说会渲染状态,看任何的Shader,我们都不会在CGPROGRAM…ENDCG里面有关于渲染状态的处理代码,当然不需要为不同的渲染状态编译不同的variant,也就不会造成variant的增加。(这部分可以参看Unity的渲染流水线,渲染一个物体需要非常多的步骤,我们写的Shader编译成的variant只在流水线中的两个可编程部分执行,而渲染状态是设置其他步骤的,与variant是完全隔离开、互不干扰。也可以说CGPROGRAM…ENDCG内的逻辑决定了variant的数量,CGPROGRAM…ENDCG外的是给Unity配置状态用的,不会引起Shader的逻辑变化,因此没变化)。

另附上一张简化版渲染流程图:
这里写图片描述

该问题来自UWA问答社区,感谢唐建伟提供了回答,如您对该问题仍有疑问,可以转至社区进行进一步交流。
https://answer.uwa4d.com/question/59daf3e0b7f9f3186a45e467
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站(answer.uwa4d.com)上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

阅读全文
0 0
原创粉丝点击