本系列为Epic官方视频[UOD2020]虚幻引擎4在移动平台上的更新|EpicGames Jack Porter&Dmitriy Dyomin(官方字幕)笔记。

Topics

本节一共分为三个Topics,最新情况、未来计划、深入了解新的移动端延迟渲染。

移动平台趋势

Mac开始使用Apple Silicon来转为同样的架构类型,电脑端拥有的图形能力也将在移动端实现。

Android的Vulkan的性能也大幅度提升,重心将放在Vulkan。OpenGL ES已经被作为legacy platform,会有一些feature进行fallback或者tradeoff,但还会继续支持,因为几年前的设备还不支持Vulkan。

许多Android新款手机有了更高的刷新率,所以用户也在期待更高的帧数。

Android64和ASTC已经成为主流(2年前的Fornite当时已经支持这些)

UE4.26

Android方面:

  • 支持新的Google Play App Bundles和Asset Delivery。
  • 高刷新率(GLES和Vulkan都支持)
  • NDK21
  • 正在开发:AAR(Android Archive),这样就可以在一个标准的Android App中,当你需要的时候调出UE。
  • 正在开发:收集Android数据(温度、内存、GPU性能等),并与其他系统连接(比如Unreal Insights)

iOS方面:

  • 支持iOS14、Xcode12、iOS14的新控制器框架(新的输入设备及对应的UI)。
  • 支持iPadPro的120Hz刷新率
  • Metal的Windows工具,这样可以在Windows上编译Metal Shader

Patching方面:

  • 新的Patching插件:ChunkDownLoader。用于patching和DLC

UE mobile

UE mobile team 2020年在提升移动设备上图像的功能和质量,以及为了未来的移动设备完善UE。

帮助我们实现这一点的其中一个工具是Compute Shader。在UE4.25中,已经将ES的最低限制从2.0提升到了3.1。这样各个模块都可以使用Compute Shader。

目前Compute Shader被用于Niagara GPU particle simulation、runtime virtual texture的纹理压缩、GPU Scene、Postprocessing的部分模块。之后还会支持distance field、GPU driven rendering(culling、instancing、更高级的geometry)、生成geometry(by compute shader)。

但是当我们增加特性的时候,Android的Gaphics driver出现了一些问题。2017年前的设备的Vulkan驱动无法运行引擎,只能使用ES。但是ES(大多数Mali GPU)中texture buffer size有一些限制,使我们无法使用需要的功能,特别是GPUScene。虽然还有其他buffer类型,但是这些buffer VS无法访问。未来的Android手机会有可升级的独立GPU驱动,但旧的设备无法使用。

关于调试工具,之前我们只能使用平台相关的:Arm Mobile Studio(Mali graphics debugger)、Snapdragon Adreno Profiler。现在已经内嵌了RenderDoc。google也在开发自己的工具Android GPU Inspector,可以提供GPU Tracing(包括对Adreno和Mali设备进行performance counters)。目前该测试版工具已经支持了使用Android11的Pixel 4。

UE4 Mobile vs Desktop

PC端可以使用forward或者deferred renderer,可以使用多光源阴影,每个物件都可以接受多个reflection capture(逐像素的进行blend),ssr,多种shading models(default、clear-coat、skin、hair)

Mobile只能使用forward renderer, 一个方向光,方向光的动态阴影,支持点光源和聚光灯(不支持阴影),每个物件只有一个reflection capture,planer reflection(无ssr),只有default shading model

所以改善移动画质的第一种做法是直接使用pc的渲染方法,这种方式只适合于超高端设备。

UE4 Mobile renderer

4.26中,我们将desktop forward和desktop deferred搬到了mobile。desktop forward也被用于Switch上。Android只支持Vulkan。renderer支持所有的光照类型,多光源阴影,per pixel reflection capture,full physically based rendering model。

可以在启动的时候选择使用PC或者mobile renderer。所以,如果运行在高端设备上,可以选择PC或者mobile renderer。

UE4 Mobile renderer improvement

Mobile Deferred Renderer

可移动聚光灯针对static、moveable物件产生的Dynamic shadow。

多种Shading model(PC端支持:clear coat、subsurface scattering、thin translucent、hair等,Mobile端4.26开始支持clear coat,并简化了增加shading models的方式)

ao(支持ground truth ao,可以改善间接光和shadow。Galaxy S7上需要3-4ms)

ssr(per pixel projected reflection,比传统ssr更适合mobile。但仅用于planer。当开启planer reflection的时候,可以设置这个选项。速度更快,但也有缺点。)

reflection capture compression用于节省内存。

Runtime Virtual Texture compression ETC2(通常用于性能消耗大的地形材质,转换成VT,改善运行时性能)

未来工作

更多的shading model

CSM caching for resuing(在各帧之间重用cascade shadow map的数据)

使用Render Dependency Graph来实现mobile renderer

Distance Field feature(shadow、ao)

GPU driven rendering(culling、instancing、高级geometry)

Mobile deferred renderer细节

Mobile默认还是会使用forward shading。也可以手动设置,使得mobile使用pc renderer(“平台”->"项目设置"。默认为deferred,也可以使用forward)。但总的来说在mobile使用pc renderer不合适。

所以,引入了新的针对mobile GPU优化过的deferred shading。启用它的方法是在DefaultEngine.ini中添加r.Mobile.ShadingPath=0/1。

Deferred vs forward

下面来看mobile deferred shading的优势,以及它在mobile GPU如何高效实现的。

Forward renderer的优点是:

  • 单pass渲染完场景,不需要大量内存带宽。

缺点是:

  • 材质Shader必须包含光照代码。且针对不同光照情况,需要大量Shader变体。
  • Overdraw导致被遮挡像素的光照运算也被冗余计算。
  • 多个局部光源也会消耗大量的ALU,因为每个像素都必须遍历周边所有的光源,即使光源并不会切实影响这个像素。
  • 不能很好的支持光照贴花(需要DBuffer和深度读取)。

Deferred rendeer的优点是:

  • 材质和光照计算完全独立,也就不需要很多Shader变体。
  • Overdraw的性能消耗大幅减少,因为光照只应用于可见的像素。

缺点是:

  • 需要4-6个MRT
  • MSAA的话会导致内存问题更严重
  • 不支持alpha blend

传统意义的延迟渲染如下:

UE4

One Pass Single Deferred的目标是:中间内存GBuffer不存入system memory

UE4

如何在不同的API实现Mobile Deferred Shading

目前Metal、Vulkan、ES都可以做到这一点,但是做法都不一样

Vulkan的做法为:将Render Pass分为多个subpass,每个subpass有自己要执行的任务,将它们放在一个pass可以方便我们表达GBuffer pass和lighting pass之间的依赖关系。这个依赖关系会被GPU driver使用,将多个subpass合并成One single pass。这样我们就不需要把GBuffer store回system memory了。每个subpass都需要声明自己读写的attachment。用于获取input attachment的语法为SubpassLoad,通过它我们就可以在lighting subpass中获取当前像素GBuffer的数据。

UE4

Mobile deferred shaderer中最后会得到3个subpass:1.Gbuffer,2.Decal(写入GBuffer),3.Lighting+Transparent(写入SceneColor)。结束后,只将SceneColor写入system memory即可。

UE4

第二个subpass decal虽然经常为空,因为场景中不会总有贴花。但是这个pass还是需要存在,这是为了避免出现两种不同的render pass configuration。因为render pass configuration会被bake进PSO。如果在运行时切换render pass configuration,就需要为每种render pass configuration创建新的PSO。举个例子:堡垒之夜游6000个PSO,在移动设备上需要花费几分钟来编译得到这些PSO。所以必须时刻考虑如何减少PSO。

该方案,共有5个color attachment(SceneColor、GBufferA、B、C、SceneDepthAux(required for iOS))和1个depthstencil attachment。导致G71和更老的GPU不支持(它们最多只支持4个color attachment MRT)

Metal的原理差不多,但是处理起来要简单的多,因为在iOS中的Metal,不需要在render pass中分清具体的subpass以及它们之间的依赖性。在Metal的shading language中可以声明某些attachment为某个ps的输入,然后Shader就可以通过这种方式访问GBuffer了。使用这种方式还可以实现programable blending。Metal中无法读取DepthStencil attachment(Patrick:针对这一点我是存疑的),所以增加了一个额外的color attachment(精度为32bit float,因为16bit的精度不够,可能是因为fastmath的优化)用于保存深度(Android中不需要这个color attachment,因为Android可以直接fetch depthstencil attachment),在lightingpass中可被读取。具体代码如下:

UE4

在Metal和Vulkan中都支持Memoryless模式,Metal中在创建texture storage mode的时候设置memoryless flag即可,这样就不会在system memory中创建该RT,该RT只会在render pass的时候存在于tile/chip memory中,可以节约大量内存。

Vulkan的话也可以用同样的方式,在创建的时候设置transient flag,以及使用lazily内存分配模式,具体代码如下:

UE4

总结一下Mobile Deferred rendering涉及到的MRT:SceneColor使用R11G11B10的格式,用于保存emissive和unlit。3个GBuffer为RGBA32格式,其中的一些属性目前还没被用于Mobile deferred shading,所以新的特性还在开发中,最终全部属性都会被用上。SceneDepthAux使用32bit float来保存场景深度。总的来说,MRT占用了160bit(20byte)

UE4

OpenGL ES的话则通过extension来实现Mobile Deferred Shading:pixel local storage(Mali和PowervR支持,Adreno不支持,且无法store回system memory),framebufferfetch(Adreno支持,Mali不完全支持,Mali只能读取color0 attachment)。所以UE需要在runtime的时候根据GPU型号改变Shader代码。UE4.27会完全支持PLS和FBFetch。

Mobile Deferred Shading支持的效果

支持所有贴花混合模式(包括lit decal(Patrick:forward应该也可以,把光照计算写在decal的shader中即可?)),如下图所示:

UE4

可以混合影响到每个像素的reflection capture和non-static skylight reflection(Patrick:forward应该也可以,把混合放在shader中即可?)

SSR(4.27实现)

Mobile Deferred Shading的具体实现

使用FullscreenQuad来实现lighting pass,这样会计算屏幕上每个像素的光照。但其实unlit像素的光照是不需要计算的(比如室外场景的天空,可能会占用一半左右的屏幕),所以在渲染GBuffer的时候,当渲染unlit物件的时候,可以在stencil中标明这是一个unlit像素,然后在lightingpass中可以通过stencil test,过滤掉所有unlit像素。

针对local light rendering。比如spot light,默认会着色所有在light volume内的像素,而其实我们只需要渲染那些与light volume相交的几何体上的像素即可。比如下图中的地板和地板上的盒子

UE4

有一个古老的技术,用两个pass,cull未被light影响的像素,具体实现方式如下:先绘制light volume的正面,不写入颜色,把它后面的物件标记stencil。然后再绘制light volume的反面,然后针对它前面且被标记stencil的物件,进行光照计算。这样会多了一次绘制,且在DC之间进行了状态切换,但测试结果证明,这个算法依然是正向优化。尤其在场景中没有很多local light的时候。这个优化在Mobile Deferred Shading默认开启。

UE4

我们会按照贡献度来依次绘制local light,但如果屏幕上有很多local light的时候,这样逐一渲染可能会影响性能(临界点大概在100个灯光=200个DC)。且如果一个像素有大量重叠光照,也会很影响性能(Patrick:ALU无法避免,省去的只 是DC数量和VS运算)。这种情况就可以使用Cluster/Tile Base Lighting,在帧开始的时候运行一个计算任务,生成影响each cluster的light list。这样,在渲染平行光lighting pass的时候,就可以查看light list,然后在一个DC中渲染所有影响当前像素的local light,这样就可以在一次DC中渲染所有光源。前面提到的local light culling技术和cluster base lighting无法共用。Cluster base lighting是默认关闭的。可以通过r.mobile.UseClusteredDeferredShading=0/1打开。

通过一些测试工具可以看到Mobile Deferred Shading在GPU和内存带宽上的开销要少很多。

TodoList

将160bit的MRT优化到128bit

支持更多的shading model,比如sss和clearcoat

light function和IES light profiles

Shadow of local light(forward支持spot shadow,deferred目前不支持)

SSR

TAA(Patrick:MSAA?)

虽然并非全部原创,但还是希望转载请注明出处:电子设备中的画家|王烁 于 2020 年 12 月 22 日发表,原文链接(http://geekfaner.com/ue4/blog7_UOD2020_2.html)