【问题标题】:How can I optimise an SM3 HLSL pixel shader by only executing complex code for some pixels?如何通过仅对某些像素执行复杂代码来优化 SM3 HLSL 像素着色器?
【发布时间】:2014-10-19 16:14:42
【问题描述】:

我有一个非常复杂的 HLSL 着色器,它使用 Direct3D9 中的着色器模型 3 进行大量纹理读取。复杂代码仅用于某些像素,因此我在该代码块周围放置了一个 if 语句。令我惊讶的是,这根本没有带来任何性能提升。如果我改用 clip(-1),我确实看到了巨大的性能提升,所以这个着色器确实是我程序的瓶颈。为什么在没有 clip(-1) 行的情况下分支不能提高我的性能?

我找到了这个话题:How much performance do conditionals and unused samplers/textures add to SM2/3 pixel shaders? 这个话题表明在着色器模型 3 中可以使用分支进行优化,但性能是每批像素中最差的。在某些情况下,慢速分支主要位于屏幕边缘,而快速分支主要位于屏幕中心。我认为这意味着批量像素通常会采用相同的分支,因此我希望通过这种方式获得性能提升。

在伪代码中,像素着色器如下所示:

float4 colour = tex2D(texture, uv);
if (colour.a < 0.5f)
{
    //I only get a performance boost if I replace this line with clip(-1);
    oColour = colour;
}
else
{
    complexSlowCodeWithTonsOfTextureReadsGoesHere;
    oColour = result;
}
oColour *= 2;

这给了我与删除分支并始终使用慢速 else 分支中的代码时完全相同的性能。如果我用 clip(-1) 替换第五行,我会看到巨大的性能提升(以及大部分黑屏),因此 if 语句实际上正在运行。

我在这里做错了什么还是不可能在着色器模型 3 中优化这样的着色器?

【问题讨论】:

  • 您的着色器是否只是在全屏过程中运行一次?或者它是用来绘制几个对象的。在第一种情况下,使用 stencil 可以为您带来相当不错的性能提升。
  • 是全屏后期效果。为此使用模板缓冲区将非常复杂,但可能。 Gnietschow 在下面的回复已经很好地回答了我的问题。 :) (我只是发现我可以点击 V 符号表示我的问题已得到回答......)

标签: shader hlsl direct3d9


【解决方案1】:

问题是您的 if 将被展平(均已执行,错误分支的结果被丢弃),因为您在其中一个分支中使用了 tex2D 之类的渐变函数 (doc)。如果从分支中删除这些函数或将它们替换为 tex2Dlod 或 tex2Dgrad 等非梯度函数,您应该会看到性能提升。如果在 if 之前添加 [branch],编译器将有助于找到有问题的行。这将提示编译器你想要一个真正的分支 if,如果你使用梯度函数,编译将失败。

就我的经验而言,gpu 使用 2x2 片段计算输出。这需要计算用于纹理查找的正确 miplevel,因此需要邻居的信息。这可以防止 tex2D 函数分支,因为它们是相邻操作所需要的。如果您通过传递 miplevel 为 gpu 提供所需的信息,则不再需要其他片段,因此可以实际跳过分支。

【讨论】:

  • 感谢您非常清楚的解释!我切换到使用 tex2Dlod,我现在确实看到了预期的性能提升! :) 顺便说一句,我在我的帧率中清楚地看到 clip(-1) 与 tex2D 一起工作,这让我感到惊讶,因为这也会干扰从邻居那里获取 miplevel 信息。
  • 这一点已经让我很困惑,不幸的是我对此没有任何解释。我唯一能想象的是,这样的剪辑片段会产生特殊的价值。我希望其他人可以启发我们:)
【解决方案2】:

使用 Z/Stencil 缓冲区来屏蔽您不希望着色器在其上运行的区域。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-27
    • 2020-07-26
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 2011-12-06
    相关资源
    最近更新 更多