DirectX 11,结合像素着色器以防止出现瓶颈

时间:2013-09-23 07:24:39

标签: image-processing directx shader pixel-shader

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

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

让我简要介绍一下算法:

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

      

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

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

      

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

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

      

    我还没有与我的开发人员联系,所以不知道在这里等待我的是什么样的瓶颈。

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

      

    ......与第3步相同。

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

      

    Problem diagram

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

    问题1:难道不可能以某种方式让GPU知道如何在不涉及CPU并导致瓶颈的情况下执行每个分支表单到底部?即一次性编程图形流水线序列然后让GPU完成它的工作。

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

    任何帮助将不胜感激!

    提前谢谢。

    祝你好运, 卢卡斯

1 个答案:

答案 0 :(得分:1)

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

<强> 1。着色器重复

首先,目前还不清楚,你在这里称之为“瓶颈”。是的,理论上,在for循环中绘制调用是一种性能损失。但它是瓶颈吗?您的应用程序是否真的失去了性能?多少?只有剖析器(CPU和GPU)才能回答。但要运行它,必须先完成算法(第3和第4阶段)。所以,我最好坚持使用当前的解决方案,并开始实施整个算法,然后进行配置文件,然后解决性能问题。

但是,如果你准备好调整......常见的“重复”技术正在实现。您可以再创建一个顶点缓冲区(称为实例缓冲区),它将包含不是针对每个顶点的参数,而是针对一个绘制实例。然后,您通过一次DrawInstanced()电话完成所有操作。

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

与往常一样,您在这里有一个交易:代码简单到(可能)性能。

<强> 2。多次渲染

  

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

通常,你会像这样链接,所以不涉及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个标记。您可以拥有多个输入纹理和多个渲染目标。只需确保每个下一个传递都知道先前的传递输出。 如果上一次传递使用的资源多于当前,请不要忘记取消绑定,以防止难以找到错误:

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()方法调用和Pixel / Vertex着色器切换?

我们永远不会知道。司机选择合适的资源位置。但是如果你有使用DEFAULT用法和0 CPU访问标志创建的资源,你几乎可以肯定它将永远存在于视频内存中。

希望它有所帮助。快乐的编码!