EarlyZ、Alpha Test和Alpha Blend

1 一个案例和一篇文档引发的思考

在广州的某家游戏开发公司T公司,一开始使用了alpha blend去实现某手游的草丛的效果,据说性能很差,后来经过侑虎科技采取的优化方法,即将alpha blend改为使用alpha test的方式去实现,据闻性能得到了很大的提升。所以一传十十传百,“alpha test的性能优于alpha blend”的结论在广州游戏开发圈中传开了。我一开始也信以为真,直到后来看到了Unity3D的某一页帮助文档,在这网页的文章中,对移动平台的shader的优化指南中有这样的一句话:

Avoid alpha-testing shaders; instead use alpha-blended versions.

这一句话,让我对中国游戏圈内耳口相传的一些“秘诀心得”真实性的怀疑度,又增加了一分。晚上总是睡不着。凡事须得研究,才会明白。于是我翻开各路大神牛人的博客一查,很多博客没有年代,歪歪斜斜的每页上都写着“牛逼,大神”几个字。我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着两个字是“扯淡”!

是时候自己做一些深入考据,不人云亦云了!

2 early fragment test

2.1 什么是early fragment test

在谈论alpha test和alpha blend孰优孰劣之前,首先要了解early fragment test(通常也被称为Early depth test)。OpenGL的wiki对它的解释在这里,引用它的原文在此:

early fragment test (sometimes called Early Depth Test) is a feature supported by many GPUs that allow the certain Per-Sample Processing tests that discard fragments to proceed before fragment processing in the rendering pipeline. This feature can be active in two ways: as an optimization and by explicit user control (with OpenGL 4.2 or ARB_shader_image_load_store).

译文如下:

early fragment test(有时称为“early depth test”)是许多GPU都支持的功能,在渲染流水线中,这种功能可以对逐个“样本”(译注:也即片元),在片元着色器中处理它之前,对其做一些测试工作,看它是否满足丢弃的条件,如果满足条件,则丢弃之,不在片元着色器中对其进行处理。可以通过两种方式激活此功能:作为一种优化方式去启用,或者通过显式的用户控制去使用(使用OpenGL 4.2或ARB_shader_image_load_store)。

这是什么意思呢,首先看下面的这张图:

上面的图中,最左边的就是没有启用early fragment test机制的渲染流水线示意图。可见,在默认的流水线中。在每一次draw call中计算的片元,无论它是不是最终不可见的,也即是不是被color buffer中的当前片元所遮挡住的,都要做执行在片元着色器中执行一趟计算。

显然,缺省默认的流水线是很低效的,因为在对某片元(后称“待处理片元”)执行的片元着色器的计算之前,待处理片元的深度值,其实是已经在流水线的其他前置阶段中完成了的。再对待处理片元执行一次片元着色器的计算,纯属多此一举。因此如上图中间的流程所示,把深度测试,甚至包括模板测试。提前至片元着色器阶段之前进行。这样子,如果当前color buffer中同一位置的片元,它的深度值比待处理片元的深度值要(即它比待处理片元更靠近摄像机),且它是不透明的话,那么待处理片元就对color buffer中该位置不贡献颜色,属于可以提前被丢弃掉的。

2.1 early fragment test的性能限制

还是直接引用OpenGL的wiki上的阐述:

  1. Note: Do recall that if a fragment shader writes to gl_FragDepth, even conditionally, it must write to it at least once on all codepaths.There can be other hardware-based limitations as well. For example, some hardware will not execute an early depth test if the (deprecated) alpha test is active, as these use the same hardware on that platform. Because this is a hardware-based optimization, OpenGL has no direct controls that will tell you if early depth testing will happen.Similarly, if the fragment shader discards the fragment with the discard keyword, this will almost always turn off early depth tests on some hardware. Note that even conditional use of discard will mean that the FS will turn off early depth tests.
  2. Note: All of the above limitations apply only to early testing as an optimization. They do not apply to anything below.

译文如下:

  1. 注意:切记,如果片元着色器写入gl_FragDepth(译注:gl_FragDepth是由GLSL提供的着色器内置变量,可以用它在着色器中读写之片元的深度值,参见这里),就算是有条件地写入。它也必须在所有代码路径上至少写入一次。 也可能存在其他基于硬件的(对使用early fragment test的)限制。例如如果alpha test功能被激活使用,则某些硬件将不会无法执行early fragment test。因为启用alpha test时,在该平台上,可能要使用和启用early fragment test相同的硬件。因为early fragment test基于硬件的优化,所以OpenGL无法提供API去决定是否会进行早期深度测试。类似地,如果片元着色器使用discard关键字丢弃片元,则几乎总是会关闭在某些硬件上能够执行的early fragment test。即使有条件地使用discard语句,也将意味着将关闭early fragment test。

注意:以上所有限制仅适用于作为优化的早期测试。它们不适用于以下任何内容。

参考网页

Unity3D帮助文档/Platform-specific/Mobile Developer Checklist

再议移动平台的AlphaTest效率问题

Unity植被性能优化

虚幻4渲染编程(Shader篇)【第十四卷:PreZ And EarlyZ In UE4】

图形学,什么情况下适合用Alpha Test来代替Alpha blend减少overdraw进行优化?

TBDR的HSR流程细节和使用AlphaBlend的效率提升程度?

Why should I prefer alpha blending to alpha testing?

AlphaTest可以在手机上使用吗?性能是否很差?

一口气解决RenderQueue、Ztest、Zwrite、AlphaTest、AlphaBlend和Stencil

Tile-Based Deferred Rendering

who am i