【问题标题】:Is discard bad for program performance in OpenGL?丢弃对OpenGL中的程序性能有害吗?
【发布时间】:2012-01-20 11:56:08
【问题描述】:

我正在阅读this的文章,作者写道:

以下是如何通过两个简单的步骤在每个平台上编写高性能应用程序:
[...]
遵循最佳实践。对于 Android 和 OpenGL,这包括诸如“批量绘制调用”、“不要在片段着色器中使用丢弃”等内容。

我以前从未听说过丢弃会对性能等产生不良影响,并且一直使用它来避免在不需要详细 alpha 时进行混合。

有人可以解释一下为什么以及何时使用丢弃可能被认为是一种不好的做法,以及丢弃 + depthtest 与 alpha + blend 相比如何?

编辑:在收到关于这个问题的答案后,我做了一些测试,方法是在上面渲染带有纹理四边形的背景渐变。

  • 使用 GL_DEPTH_TEST 和以“if( gl_FragColor.a < 0.5 ){ discard; }”行结尾的片段着色器大约提供 32 fps
  • 从片段着色器中删除 if/discard 语句增加 渲染速度约为 44 fps
  • 将 GL_BLEND 与混合函数“(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)" 而不是 GL_DEPTH_TEST 也导致大约 44 fps

【问题讨论】:

  • 很有趣的问题,希望有人能解答。

标签: android opengl-es opengl-es-2.0 shader


【解决方案1】:

它取决于硬件。对于 PowerVR 硬件和其他使用基于 tile 的渲染的 GPU,使用discard 意味着 TBR 不能再假设绘制的每个片段都会变成一个像素。这个假设很重要,因为它允许 TBR首先评估所有深度,然后只评估最顶层片段的片段着色器。一种延迟渲染方法,硬件除外。

请注意,开启 alpha 测试也会遇到同样的问题。

【讨论】:

  • 啊,我明白了。那么如果使用了discard,它会影响每一个tile,还是只影响一个fragment被丢弃的那个?如果它只影响一个图块,它应该仍然比使用 alpha/blending 更有效,对吗?
  • “阿尔法/混合”是什么意思?在任何情况下,它都会影响执行使用discard 关键字的片段着色器的每个图块,无论它是否实际调用它。
  • 使用 alpha/混合我只是指使用带有 alphamap 的纹理来确定当前片段的可见性。如果我没记错的话,除非您启用了 GL_BLEND 而不是 GL_DEPTH_TEST(例如,如果您在另一个对象前面渲染一个对象),否则它不能正常工作?
  • 我会接受你的回答,因为它解释了我的问题的要点,但我仍然不清楚深度测试和丢弃如何(在性能上)与使用 alphamap 和混合进行比较。
  • 在渲染宽度GL_DEPTH_TEST disabled 时会影响性能吗?我想在天空盒着色器中使用丢弃(生成天空盒)。
【解决方案2】:

“丢弃”对所有主流图形加速技术都不利——IMR、TBR、TBDR。这是因为片段的可见性(以及深度)只能在片段处理之后确定,而不是在 Early-Z 或 PowerVR 的 HSR(隐藏表面移除)等期间确定。在移除之前,图形管道越往下越倾向于表明它对表现;在这种情况下,更多的碎片处理+其他多边形深度处理的中断=效果不好

如果您必须使用丢弃,请确保只有需要它的 tris 使用包含它的着色器进行渲染,并且为了最大限度地减少其对整体渲染性能的影响,请按以下顺序渲染您的对象:不透明、丢弃、混合。

顺便说一下,只有 PowerVR 硬件决定了延迟步骤中的可见性(因此它是唯一被称为“TBDR”的 GPU)。其他解决方案可能是基于图块的 (TBR),但仍像 IMR 那样使用依赖于提交顺序的 Early Z 技术。 TBR 和 TBDR 会在芯片上进行混合(比进入主存储器更快、更省电),因此应该优先考虑混合以提高透明度。正确渲染混合多边形的通常过程是禁用深度写入(但不是测试)并以从后到前的深度顺序渲染 tris(除非混合操作与顺序无关)。通常近似排序就足够了。几何形状应避免大面积完全透明的碎片。通过这种方式,每个像素仍会处理多个片段,但不会像丢弃片段那样中断硬件深度优化。

【讨论】:

  • “片段的可见性(以及深度)只能在片段处理后确定,而不是在 Early-Z 或 PowerVR 的 HSR(隐藏表面去除)等期间确定。”这并不完全正确。 Early-Z 或丢弃都可以防止绘制片段。所以很有可能先做一个然后再做另一个。 PowerVR 不能因为你和我所说的原因。但是传统的渲染器当然可以。如果他们不这样做,那只是因为丢弃逻辑与深度测试逻辑捆绑在一起。这是硬件设计问题,而不是算法必要性。
  • 我错过了丢弃片段呈现在现有几何图形“后面”的情况:S - 这就是您的意思吗?在这种情况下,早期 Z 和 HSR 都会拒绝没有片段处理的片段。即便如此,Early-Z 要求在遮蔽、丢弃片段之前渲染遮蔽片段,否则丢弃着色器仍需要为这些片段运行以确定深度。在这种情况下,HSR 不依赖于提交顺序 - 在帧结束时,如果丢弃片段位于不透明片段之后,则它们不会被 PowerVR 处理。
  • 片段渲染失败的原因有很多。深度测试是一个,丢弃是另一个。 Early-Z 只是先执行深度测试。丢弃会干扰这一点的唯一原因是丢弃逻辑是否与硬件中的深度测试逻辑相关联。仅仅因为某些东西通过了深度测试并不意味着它会通过一切。如果深度和丢弃是耦合的,那只是因为硬件是这样构建的,而不是因为算法必须这样做。您应该能够进行 Early-Z 测试,然后再丢弃。
  • 当然可以,但是这样会比较慢,因为深度信息要到管道后期才能确定。着色器中的“丢弃”(具有通常的渲染状态)会影响片段的深度 write 值,因此它会影响硬件中后续深度测试的性能。这就是它与混合的不同之处。
  • 深度书写与彩色书写同时发生。 discard,就像深度测试或模板测试一样,会影响深度和颜色书写。在片段着色器之前执行深度测试并不要求最终写入测试的深度。现在,某些 Z-剔除 技术(Hi-Z、Hierarchial-Z 等)确实需要这样做。所以你不能将它们与discard 一起使用。但这些与 Early-Z 不同,后者只是在片段着色器之前对每个片段进行深度测试。
【解决方案3】:

此外,仅在片段着色器中使用“if”语句可能会导致某些硬件的速度大幅下降。 (具体来说,大量流水线化的 GPU 或执行单指令/多数据的 GPU 会因分支语句而受到很大的性能损失。)因此,您的测试结果可能是“if”语句和其他人提到的效果的组合。

(值得一提的是,当我切换到对半透明对象进行深度排序并将它们重新渲染到前面而不是以随机顺序渲染并丢弃着色器中的片段时,在我的 Galaxy Nexus 上进行的测试显示出巨大的加速。)

【讨论】:

    【解决方案4】:

    对象 A 在对象 B 的前面。对象 A 有一个使用“丢弃”的着色器。因此,我无法正确执行“Early-Z”,因为我需要知道对象 B 的哪些部分将通过对象 A 可见。这意味着对象 A 必须一直通过处理管道,直到几乎最后一个在我确定对象 B 是否实际可见之前的时刻(直到执行片段处理)。

    这对 HSR 和“Early-Z”不利,因为可能被遮挡的对象必须等待深度信息更新,然后才能进行处理。如上所述,它对每个人都不利,或者用更友好的方式“朋友不要让朋友使用 Discard”。

    【讨论】:

      【解决方案5】:

      在您的测试中,您的 if 语句是每像素级别的性能

      if ( gl_FragColor.a < 0.5 ){ discard; }
      

      每个被渲染的像素都会被处理一次(很确定这是每个像素而不是每个纹素)

      如果您的 if 语句正在测试 Uniform 或 Constant,您很可能会得到不同的结果,因为 Constants 仅在编译时处理一次,或者 Uniform 在每次更新时处理一次。

      【讨论】:

        猜你喜欢
        • 2012-11-24
        • 2011-11-24
        • 2016-11-06
        • 2010-09-18
        • 2011-01-11
        • 2019-09-18
        • 2011-10-13
        • 2016-07-20
        • 1970-01-01
        相关资源
        最近更新 更多