overdraw问题:
overdraw顾名思义就是过度绘制,就是在渲染过程中**绘制一帧FBO(或者RenderTarget)**超过一次相同像素的现象!这个是CG的问题!特别在是用来大量的透明混合的情况下会产生的,当然客户端andriod、IOS 前端都会有类似的问题,这是一个普遍的问题,毕竟是图形底层的问题。也是性能通常绕不开的问题。
overdraw产生的主要场景:
- 大量的透明度和半透明效果:当有多个半透明物体重叠时,每个物体都需要被渲染,即使它们中的一些可能被其他物体遮挡;除此之外透明的渲染为了正确是用来OIT将会产生大量的overdraw,比如Depth
Peeling就至少需要N/2+1 个 pass层 Order Independent Transparency with Dual Depth Peeling DualDepthPeeling.pdf (utah.edu)! - 复杂的场景:在一个复杂的场景中,可能有很多层的几何体互相遮挡,但如果没有使用有效的遮挡剔除技术,所有这些层都会被渲染。
- Ui的数量、层级过多等问题,还有各种UI的透明叠加也会产成
HSR技术:
HSR(Hidden Surface Removal),也称为遮挡剔除或隐藏表面消除,是图形渲染中提高图形渲染效率的技术,完美解决不透明的ovardarw问题,通过剔除那些在最终渲染的视图中不可见的几何面来降低处理和渲染的开销。
本质上算是一种剔除算法在游戏引擎或者3D引擎中,在一个3D场景中由于物体相互遮挡的关系,不是所有的物体或物体的部分都是用户可见的。例如,在一个场景里,一个远处的物体可能完全被前面的一个物体遮挡。如果渲染系统仍然处理这些最终不会被看到(对屏幕的没有贡献),会导致不必要的计算负担,浪费资源和时间。大致思路:沿一条射线从第一个不透明片元向后剔除被遮挡的片元。HSR影响的是整个pass的多个模型,若HSR失效,则推荐模型从前往后渲染更高效(遮挡,还有深度测试);
HSR技术可以在渲染流程的不同阶段被应用:
- Object级别:在空间变换和光栅化之前,可以决定整个物体是否可见。比如,使用视锥体剔除(Frustum Culling)或者 游戏中的剔除技术(一)遮挡剔除/Occlusion Culling可以移除完全在相机视野之外的物体。
- 背面剔除(Back-Face Culling):是根据mesh的法向量和观察方向判断该多边形是否面向观察者。如果一块面背对着观察者(即法线与观察方向相反),那么它很可能不可见,可以被剔除。
- 像素级别:使用深度测试(Z-buffering)或者early-z等技术在像素级别决定哪些像素应该在屏幕上绘制。每个像素点都有一个深度值,渲染时通过比较深度值来确定是否将当前片元的颜色写入到帧缓冲区。
HSR减少了图形管线下游阶段(如光栅化、纹理映射、着色等)的工作量,因此大大提高了渲染效率,这对于实时图形应用,如视频游戏和增强现实/虚拟现实(AR/VR),尤其重要。除了提升性能外,HSR还有助于降低能耗,对于运行在移动设备和低功耗平台上的应用来说尤为关键,所以本文重点介绍在移动端的HSR技术。
Early-Z:
先简单聊一下Early-Z:它是现代图形gpu硬件优化技术,用于减少不必要的像素着色计算,可部分解决overdraw的问题!它在传统渲染管线流程中加入了一个阶段,在执行昂贵的片元着色操作之前再进行深度测试。如果深度测试失败,意味着该像素会被场景中其他对象遮挡,因此可以提前丢弃这个像素,避免执行后续的着色计算。但是一旦开启Early-Z就有的渲染技术无法使用!
Early-Z 不兼容的操作与渲染技术:
- discard:在像素着色器中改变了深度值修改gl_FragDepth或者discard,在像素着色器中改变深度值时,GPU就不能再提前使用深度测试来剔除片段,因为实际的深度值只有在像素着色器运行后才能知道。
- 半透明和透明渲染:使用半透明材质时,需要OIT正确排序并混合片元颜色,这通常要求片元被处理而不是在深度测试阶段被剔除。因此,包含半透明物体的场景通常不适合进行 Early-Z 优化。
- Alpha Testing: alpha testing(通过比较alpha值来丢弃片元), Early-Z 优化的影响会影响 alpha testing。
- 本质是深度测试,不开启深度写入Z-Wirte就无法正确有效的使用Early-Z。
使用深度测试/early-z的要解决透明问题需要预渲染z-buffer 哪就是Z-Prepass技术,实现有两种方式
方法1:双Pass法
- 深度预处理Pass(Z-Prepass):在这个阶段,不对物体进行完全渲染,只简单地渲染出各个物体在屏幕上的深度信息,产生z-buffer。这个阶段不需要使用复杂的着色器或进行纹理映射。
- 主渲染Pass:有了预先计算好的深度信息,关闭深度写入,并且将深度比较函数设置为相等。(但是这个方法会带来动态批处理的问题,多Pass的Shader不能进行动态批处理,不能进行动态批处理就会带来DC问题,一个Pass通道一个DrawCall)
方法2:提前分离的Pass
- 将原先第一个Pass(即Z-Prepass)单独分离出来为单独一个Shader,并先使用这个Shader将整个场景的Opaque的物体渲染一遍。
- 原先材质只剩下原先的第二个Pass,仍然关闭深度写入,并且将深度比较函数设置为相等。
early-z技术与HSR 区别:
两者都是像素级别,都是为了解决overdraw的问题, 区别如上提到的early-z只能限制较多因为依赖深度图 所以不写深度就无法做early-z,hsr没有这么多的限制,提高性能~都是需要硬件支持,earlyz主要是pc端,对于移动端的是hsr,同时pc端是IMR移动端是tbdr。
early-z主要是能解决不透明物体的overdraw的问题,对于透明问题就支持并不太好!而且新人的结果可能是错误的比如你有两个三角形A、B交叉在一起,互有遮挡,那你不论怎么排序都会产生先完全绘制一个三角形,再绘制另外一个都是不对的。虽然early-z的改进可以解决这个问题,这个就是Z-PrePass!但是HSR不需要 early-z+Z-PrePass才能彻底的解决场景的既有透明又有不透明等的问题,关键Z-PrePass引入了一个新的pass会有较高的overdraw。
移动端GPU on-chip内存
这里特别强调on-chip是因为它在移动端GPU中重要的作用,看过TBR或者TBDR的同学应该是对on-chip不陌生~虽然on-chip不是直接HSR技术但是确实用到了,tiles或者各种深度texture是临时存储在这里的而且性能还非常高!所以我才在这里重点介绍,
首先现代GPU的内存分布大致是如下图(没找到移动端的先放PC端的顶一下后面有时间再画图或者写GPU内存通信的文章再画)
on-chip内存是一小块位于GPU核心内部的高速内存,通常也被称作缓存(cache)或本地存储(local storage)。这种内存提供了几个关键的功能,它对移动设备的图形性能和功耗优化至关重要:
- 高速数据访问:On-chip内存比外部操作系统的RAM(如DDR内存)具有更低的延迟和更高的带宽,对于tbdr或者TBDR的tiles图形数据,它们相同的数据或者相邻的数据经常被多次读取on-chip可以显著提高这些操作的效率。
- 降低功耗:距离处理器更近访问速度更快。这在移动设备中功耗、发热、电池寿命是设计的一个关键部分。
- 减少GPU和CPU之间的数据移动:数据频繁在GPU和CPU之间传递,会产生大量的总线流量、消耗大量功耗和处理时间。将这些数据保留在GPU的on-chip内存中可以减少这种开销。
移动端的GPU HSR 技术
移动端gpu型号很多Adreno、Mali和PowerVR,其中对于HSR技术来说有FPK(Forward Pixel Killing)、TBDR(Tile-Based Deferred Rendering)和LRZ(Local Render Z-buffer)技术。
Adreno它广泛应用于高通的Snapdragon
SoC中。Adreno系列GPU以其高性能和良好的功耗效率而著称使用的是LRZ技术;ARM公司开发了Mali系列GPU,它们被许多不同制造商采用在各种SoC设计中,如三星的Exynos系列和华为的Kirin系列Arm
其中Mail使用的是FPK;
PowerVR是TBDR技术iphne的A11以前都是直接使用的PowerVR,最后自研使用 Tailor Your Apps for Apple GPUs and Tile-Based Deferred Rendering | Apple Developer Documentation。
对于TBDR以前的文章记忆有说过:
TBDR与TBR很大的不同就是TBDR使用了HSR技术来几乎完美的解决overdraw。对于TBDR,无论物体的提交顺序如何(不排序),HSR将完全解决OverDraw问题。它最显著的不同点在于通过了Early-Z测试的像素不会立即执行像素着色器,而是先标记该像素属于哪个图元。当Tile处理完所有图元(场景中的所有物体),再绘制Tile上所有做了标记的像素。TBDR做到了硬件层级的遮挡像素剔除,减少OverDraw,减少带宽和内存访问。
HSR能够做到不依赖顺序绘制的像素级剔除,最主要的做法是图元光栅化后,会先标记该像素属于哪个图元(三角形),并存储于Tag Buffer,待场景所有的图元处理完毕,才进入片元着色器。HSR阶段处于光栅化之后像素着色之前:
特别强调一下如果 遇到开启AlphaTest和AlphaBlend的HSR是怎么处理。当前tile上如果前面的物体会挡住后面的物体就能正常发挥,如果HSR的射线检测layer0 ,1 ,2、3都是透明的开启了混合会打断 Defer流程,就正常draw(因此HSR对于AlphaTest和AlphaBlend物体都是没有作用的)导致渲染性能相对于不透明的HSR降低了点但是愣然是高于旧的IMR 或TBR渲染框架的!到layer4是不透明的遮挡了后续的layer5、6合HSR射线检测到后会设置5、6是失效的
。
LRZ(Local Render Z-buffer)技术:
LZR是Adreno 5及以上的芯片在TBR执行Early-Z剔除时的优化技术。在正常渲染管线前,先多执行一次渲染,生成低精度的DepthTexture,以提前剔除不可见的triangels。说白了,就是用硬件做occlusion culling,功能类似软光栅遮挡剔除,只是在硬件驱动重做速度更快(感觉跟上面结合Z-Prepass差不多只不过是硬件上做的)。 优点是减少内存访问和带宽,减少渲染图元,不需要应用程序从前到后绘制,提高帧率。缺点是:在像素着色器中写入深度值会失效。
FPK(Forward Pixel Killing技术:
TBR框架下Early-Z结合使用,管线位置位于Early-Z之后,是一个先进先出FIFO的队列。队列中有4个Quad(可以理解为2×2像素的平面),每个Quad有屏幕上位置的数据和Z数据Z越大代表离摄像机越远。流程大致如下:
- 预光栅化:在正式渲染之前,系统会执行一个轻量级的光栅化过程,以确定哪些图元可能影响当前Tile。
- 早期Z测试:对预光栅化结果进行深度测试,以确定这些图元是否有可能被其他图元遮挡。
- killed:这是关键部分,根据屏幕上相同位置(pos)的不同z,对不透明的像素进行替换(有近的就不渲染远的,渲染的线程直接停掉),这个过程叫作killed,因为这些像素将不会进入后续的着色阶段。
- 正式光栅化与着色:一旦完成了早期像素剔除,剩下的是那些确实需要被渲染的像素。这些像素将通过正规的光栅化和着色管线进行处理。
FPK的特点:
- FPK剔除粒度是Quad(2x2像素块)。
- FPK只对不透明物体有效透明物体无效。
- FPK必须开启深度测试才能起作用。
参考资料:
EarlyZ和TBDR中的HSR有什么本质区别么?
Tailor Your Apps for Apple GPUs and Tile-Based Deferred Rendering | Apple Developer Documentation
Optimizing the draw call render order
《Arm Mali GPU Best Practices》
隐藏面消除法—Forward Pixel Kill
移动设备GPU架构知识汇总
【技术美术百人计划】图形 2.7.2 GPU硬件架构概述