提要
随着移动端上引擎的渲染特性如HDR,PBR,SSR,完整的后处理管线等等不断的增加,直接照搬端游方案很容易翻车,很多时候需要针对移动端做适配,这时候只有对移动端硬件底层做充分的了解才能得出最优解。 本文从引擎开发者角度详细阐述一下对于移动端GPU架构的理解。
从IMR说起
IMR常用于PC,主机,笔记本等设备。最大的特点就是Primitive提交之后直接进行绘制, 如下如所示
这个和RTR 上的Pipeline是一致的。
硬件层面的渲染流程见下图,
IMR中的内存访问
首先确认的是,FrameBuffer是一直存储在SystemMemory上的,每个Primitive的渲染都会读取和写入DepthStencil,SceneColor,这些都是放在System Memory上的,读取System Memory(也叫Main Memory,也叫DDR,也叫DRAM)上的数据计算完之后又写入主存。
假设FrameBuffer是存储在主存上的一段连续内存,那么每个三角形在绘制的时候其实是一种随机的内存访问,这种随机即使在加入Cache机制之后还是会引起很大的性能消耗。
IMR的优势和劣势
优势 – 简单顺畅的流程保证了在单个Primitive渲染流程中没有额外的中间结果存储。
劣势 – 带宽消耗。
假设渲染一个1440p的画面,使用32bit的SceneColor,32bit的Depth/stencil,那整体需要的内存大小为30m,如果整个FrameBuffer都要同时随机访问的话,即使有Cache机制,也会触发大量的从System Memory到Cache的读取以及从Cache到System Memory的写入。
移动端方案 – TBDR
移动端硬件设计的目的 相对于桌面gpu单纯追求更高的帧率,移动端的方案则需要同时考虑更低的发热,更小的功耗,更高的性能,并在他们之中寻找平衡点。所以移动端硬件设计方案最重要就是尽可能的减少带宽消耗。所以就有了TBDR(Tiled Based Deferred Rendering)。
整个FrameBuffer被划分成了多个块,称之为Tile,分Tile渲染,每次渲染一个Tile的时候,将当前Tile所影响的Primitive都取出来,进行对应的三角形变换,着色,深度测试后之后,将结果再写回主存,
AMD在早年的分享中做了一个实验,同样渲染两个三角形所产生带宽消耗对比
硬件上和PC有两个区别:
1、显存和系统内存共用,GPU和内存直接打交到代价很大。
2、GPU上封装了一块很小的高速存储空间,称之为On-chip memory(也叫Tile Memory,也叫 on-chip framebuffer, Tile Buffer之类的),这块内存在渲染的第二阶段就会使用这部分的内存,大小因不同的GPU设计而不同,最小可能只覆盖 16 × 16 pixel。
当然有些厂商还做了多级缓存机制,比如最新的apple芯片,除了每个Shader Core都拥有自己的L1 Cache之外,还增加了GPU Last Level Cache,进一步减小了GPU与主存传递数据的代价。
详解TBDR渲染流程
完整的pipeline可以参考下图
伪代码如下
# Phase one
for draw in renderPass:
for primitive in draw:
for vertex in primitive:
execute_vertex_shader(vertex)
if primitive not culled:
append_tile_list(primitive)
# Phase two
for tile in renderPass:
for primitive in tile:
for fragment in primitive:
execute_fragment_shader(fragment)
上面的流程可以分为两个Phase – Tiling Phase和Render Phase。
Tiling Phase
在绘制Tile的时候需要只要当前Tile有哪些Primitive参与绘制,这个需要提前算好,这个计算的过程就叫Tilling(也叫Binning)。
Tilling Phase做三件事
1、将当前ViewPort划分为多个Tile,
2、执行VertexShader,三角形都变换到屏幕空间
3、计算Primitive会影响到哪些Tile,并将结果存储到主存上去。
Tilling计算的结果会存储到主存去,称之为Intermediate store(也有叫 polygon lists,也有叫Frame Data,也有叫 parameter buffer, 也叫Geometry Work Set),后面在分Tile渲染的时候会从主存load这个数据,但是这个代价就小很多了,不过也要注意顶点数量,不要让这个buffer过于膨胀。
下面有个简单的示意动画
Rendering Phase
Tiling之后GPU就对依次渲染这些Tiles,每个GPU Core每次渲染一个Tile,Core越多,同时能渲染的Tile就越多。
几个重要的点单独拎出来说一下
硬件层面的RenderPass
简单的说,就是一次渲染管线的执行。一次RenderPass会将渲染结果存储到FBO的Attachments里面,每个Attachment都需要在Tile上初始化(Load Action),并且有可能需要写回到SystemMemory(SaveAction)。
Tile Memory Load Store Action
对于OnChip和System Memory之间内存的拷贝,我们通常称之为Resolve,甚至还有Light weight resolve(从OnChip到System Memory)和Heavy weight resolve(从System Memory到OnChip 再写回到System Memory)的说法,另外MSAA里面也有关于resove的说法,所以…
Resolve 的具体含义得根据上下文来确定。
不过现在多了一种说法就是Load Action 和Store Action。 如果是TileMemory到SysemMemory的内存写入,指的就是Store Action为Store。
Load Action 有三种,DontCare,Load, Clear。 Store Action 有三种, Store, DontCare,storeAndMultisampleResolve, MultisampleResolve. 每种Action 的详细说明请参考苹果的文档Load and Store Actions。
如果想要优化流水线的性能,就一定要注意设置好每个RenderTarget的Load Action 和 Store Actions。
比如,深度和Stencil通常只有在Rasterize阶段才会使用,所以直接放到了Onchip上, Store Actions设置为DontCare这样就不用把结果写入主存,省下了大量的带宽。
Resource Storage Mode
通常用StorageMode来表示内存中的对象的被CPU和GPU的访问模式,通常有Shared,Private,Memoryless三种,如下图所示
Shared – CPU 和GPU都可以访问,这类资源通常由CPU创建并更新。
Private – 存在SystemMemory上,只有GPU可以访问,通常用于绘制 render targets,Compute Shader存储中间结果或者 texture streaming.
Memotyless – 存在TileMemory, 只有当前Tile可以访问,用完就会被刷新掉,比如Depth/Stencil Buffer 在 iOS 上对于所有的不需要 resolve 的 rt(或 store action 设置为 don’t care)都应该设置为 memoryless,比如上面说到的Depth和Stencil。
Tiled Base Deferred Render中的Defered
其实这里有两个Deferred,一个是指VS之后不立即进行PS着色,所有的厂商的移动端芯片都是如此。 另一个Deferred是由Power VR以及苹果的芯片独占,这个Deferd指的是shading操作是在所有像素都经过可见性检测之后才进行的,这个可见性判定的操作称之为HSR.下图是详细的HSR流程。
其实每个厂商都有一些相关的消除Overdraw的算法,比如Mali的Forward Pixel Kill (FPK),比如高通的A Low Resolution Z (LRZ) ,具体算法以及原理可以看最后的链接。
TBDR的优势和劣势
优
1、带宽上的节省
2、由于OnChip Memory的存在,一些不需要resolve出来的信息可以只存在OnChip中,可以节省System Memory。
3、分Tile 渲染对于Texture Cache 更加友好
劣
1、屏幕尺寸越大,划分的Tile数量越多,生成的Geometry WorkSet越大。
2、Tilling Phase是完全多出来的,打断了流水线的执行,引入了latency。
Performance Tuning
主要介绍一下Profile工具,之前有写过一篇高级图形调试优化技巧 – XCode篇,有兴趣可以点开看看,不过最新版本的xcode又出了很多新的功能,最好是去wwdc的视频学习一下。
在XCode截帧之后,每个RenderPass都单独标记出来,并且每个Attachment的Load Action和Save Action)以及Memory Type都有标注出来,还可以看到当前pass的带宽消耗以及当前帧的总带宽消耗。
Android下可以使用Renderdoc,不过API需要是Vulkan,截帧之后就可以看到每个pass以及对应的load store action.
当然还有其他厂商比如Arm和高通提供的工具,总体来说,还是Xcode好用一些。
小结
移动端硬件发展至今,大的渲染框架还是TB(D)R这一套,但是随着硬件制程以及技术迭代,移动端硬件能提供的性能空间也在飞速发展,各种移动端的渲染技术也是层出不穷,如何合理地使用与分配这些性能空间算是引擎开发者的必备技能了。
另一方面,移动端和桌面端的硬件区别也将会越来越小,不用说几年前的switch掌机,还是去年苹果发布的arm架构的M1芯片,一些桌面端的GPU甚至也利用TBR来做性能优化了, 称之为partially-tile-based rendering
移动端也有在某些情况下切换IMR渲染的技术,比如高通的FlexRender。
后面还会继续针对移动端高性能图形开发写几篇东西,大家敬请期待。
Reference
Tile-Based Rendering https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/tile-based-rendering Performance Tuning for Tile-Based Architectures – OpenGL Insights Chapter.23 ARM’s
Mali Midgard Architecture Explored – https://www.anandtech.com/show/8234/arms-mali-midgard-architecture-explored/7
Next-Gen Tile-Based GPUs – http://developer.amd.com/wordpress/media/2013/01/gdc2008_ribble_maurice_TileBasedGpus.pdf
GPU Framebuffer Memory: Understanding Tiling – https://developer.samsung.com/galaxy-gamedev/resources/articles/gpu-framebuffer.html
Best Practices For Mobile
Principles of High Performance – https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/principles-of-high-performance A New Software Based GPU Framework Evgeny Miretsky 2013
On NVIDIA’s Tile-Based Rendering – https://www.techpowerup.com/231129/on-nvidias-tile-based-rendering
Tile-based Rasterization in Nvidia GPUs – https://www.realworldtech.com/tile-based-rasterization-nvidia-gpus/
Understanding GPU Family 4 – https://developer.apple.com/documentation/metal/gpu_features/understanding_gpu_family_4
How GPU Works – https://cs184.eecs.berkeley.edu/sp19/lecture/23-51/how-gpus-work
PowerVR performance tips for Unreal Engine 4 – https://www.imaginationtech.com/blog/powervr-performance-tips-for-unreal-engine-4/
Load and Store Actions – https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/LoadandStoreActions.html
Killing Pixels – A New Optimization for Shading on ARM Mali GPUs – https://community.arm.com/developer/tools-software/graphics/b/blog/posts/killing-pixels—a-new-optimization-for-shading-on-arm-mali-gpus
Visibility processing – https://developer.qualcomm.com/docs/adreno-gpu/developer-guide/gpu/overview.html