【问题标题】:DirectX 11, Combining pixel shaders to prevent bottlenecksDirectX 11,结合像素着色器以防止瓶颈
【发布时间】:2013-09-23 07:24:39
【问题描述】:

我正在尝试使用 GPU 实现一种复杂的算法。唯一的问题是硬件限制,最大可用功能级别为 9_3。

算法基本上是两个图像的“立体匹配”算法。由于上述限制,所有计算都必须仅在顶点/像素着色器中执行(没有可用的计算 API)。顶点着色器在这里相当无用,所以我将它们视为直通顶点着色器。

让我简要描述一下算法:

  1. 取两张图像并计算成本体积图(基本上将 RGB 转换为灰度 -> 将右图像转换为 D 并从左图像中减去它)。对于生成 Texture3D 的不同 D,此步骤重复大约 20 次。

    这里的问题:我不能简单地创建一个像素着色器来计算 由于 Pixel 的大小限制,一口气重复了 20 次 着色器(最多 512 个算术),所以我不得不在循环中调用 Draw() 在 C++ 中,在所有操作都完成时不必要地涉及 CPU 同样的两张图片 - 在我看来,我有一个瓶颈。我知道有多个渲染目标,但是:有最大值。 8 个目标(我需要 20+),如果我想在一个像素着色器中生成 8 个结果,我会超出它的大小限制(我的硬件为 512 算术)。

  2. 然后我需要计算每个计算的纹理框过滤器,其中 r > 9。

    这里的另一个问题: 因为窗口太大,我需要将框过滤分成两个像素着色器(分别垂直和水平方向),因为循环展开阶段会导致代码很长。这些循环的手动实现无济于事,因为它仍然会创建大像素着色器。所以另一个这里的瓶颈 - CPU 需要参与将结果从临时纹理(V 通道的结果)传递到第二通道(H 通道)。

  3. 然后在下一步中,对来自第一步和第二步的每对结果应用一些算术运算。

    我的发展还没有到这里,所以不知道什么样的瓶颈在等着我。

  4. 然后根据步骤 3 中的像素值为每个像素获取最小 D(第 1 步中的参数值)。

    ...与第 3 步相同。

这里基本上是一个非常简单的图表,显示了我当前的实现(不包括步骤 3 和 4)。

红点/圆圈/任何东西都是临时缓冲区(纹理),其中存储部分结果,并且每个红点 CPU 都会参与其中。

问题1: 是不是可以让GPU知道如何在不涉及CPU并导致瓶颈的情况下将每个分支形式执行到底? IE。一次性对图形管道序列进行编程,然后让 GPU 完成它的工作。

另外一个问题关于渲染到纹理的事情:即使在 Draw() 方法调用和像素/顶点着色器切换之间,所有纹理是否一直驻留在 GPU 内存中?或者发生从 GPU 到 CPU 的任何传输......因为这可能是另一个导致瓶颈的问题。

任何帮助将不胜感激!

提前谢谢你。

最好的问候, 卢卡斯

【问题讨论】:

    标签: image-processing directx shader pixel-shader


    【解决方案1】:

    在像素着色器中编写计算算法可能非常困难。为9_3 目标编写这样的算法是不可能的。限制太多。但是,好吧,我想我知道如何解决您的问题。

    1.着色器重复

    首先,不清楚,这里的“瓶颈”是什么。是的,理论上,for 循环中的绘制调用是性能损失。但它有瓶颈吗?您的应用程序真的会在这里失去性能吗?多少?只有分析器(CPU 和 GPU)才能回答。但要运行它,您必须首先完成您的算法(阶段 3 和 4)。所以,我最好坚持当前的解决方案,并开始实现整个算法,然后分析并修复性能问题。

    但是,如果您准备好进行调整……常见的“重复”技术就是实例化。您可以再创建一个顶点缓冲区(称为实例缓冲区),其中包含的参数不是针对每个顶点,而是针对一个绘制实例。然后,您只需拨打一个DrawInstanced() 即可完成所有工作。

    对于您的第一阶段,实例缓冲区可以包含您的D 值和目标Texture3D 层的索引。您可以从顶点着色器中传递它们。

    与往常一样,您需要权衡:代码的简单性与(可能)性能之间的关系。

    2。多遍渲染

    需要 CPU 参与来传递临时纹理的结果(结果 V pass) 到第二个 pass (H pass)

    通常,您会像这样进行链接,因此不涉及 CPU:

    // Pass 1: from pTexture0 to pTexture1
    // ...set up pipeline state for Pass1 here...
    pContext->PSSetShaderResources(slot, 1, pTexture0); // source
    pContext->OMSetRenderTargets(1, pTexture1, 0);      // target
    pContext->Draw(...);
    
    // Pass 2: from pTexture1 to pTexture2
    // ...set up pipeline state for Pass1 here...
    pContext->PSSetShaderResources(slot, 1, pTexture1); // previous target is now source
    pContext->OMSetRenderTargets(1, pTexture2, 0);
    pContext->Draw(...);
    // Pass 3: ...
    

    请注意,pTexture1 必须同时具有 D3D11_BIND_SHADER_RESOURCED3D11_BIND_RENDER_TARGET 标志。您可以有多个输入纹理和多个渲染目标。只要确保,每一次下一次传递都知道前一次传递的输出。 如果之前的 pass 使用的资源比当前的多,不要忘记取消绑定 unneeded,以防止难以发现的错误:

    pContext->PSSetShaderResources(2, 1, 0);
    pContext->PSSetShaderResources(3, 1, 0);
    pContext->PSSetShaderResources(4, 1, 0);
    // Only 0 and 1 texture slots will be used
    

    3。资源数据位置

    所有纹理是否一直驻留在 GPU 内存中,甚至在 Draw() 方法调用和像素/顶点着色器切换?

    我们永远无法知道。驱动程序为资源选择合适的位置。但是,如果您有使用DEFAULT 使用率和0 CPU 访问标志创建的资源,您几乎可以肯定它总是在显存中。

    希望对您有所帮助。编码愉快!

    【讨论】:

    • 你打消了我的疑虑。我的开发问题是我为移动设备开发,我找不到合适的 GPU 分析器。也许我会尝试在一些性能“可比”的旧 PC 上运行我的代码,看看它的表现如何。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多