【问题标题】:Pixel Shader can access Structured Buffer while Vertex Shader can't - is that a DirectX spec?像素着色器可以访问结构化缓冲区,而顶点着色器不能——这是 DirectX 规范吗?
【发布时间】:2013-11-14 15:44:10
【问题描述】:

==================== 编辑:解决方案=====================

我终于找到了问题所在,因为答案对于学习 DirectX 的初学者可能很重要,所以我将其发布在这里。 (我使用 F# 和 SharpDX 作为 DirectX 的 .NET 包装器)

在我的程序中,资源(缓冲区、视图等)仅在调整交换链大小时发生变化。所以我将所有资源分配(IA、OM、VS、PS)放入一个函数switchTo2DLayout。如果交换链没有调整大小,switchTo2DLayout 立即返回(不做任何事情)。这是由一个标志控制的。

后来我发现这个标志从未被重置,因此资源分配是在每次绘制调用之前完成的。我纠正了这个错误,但现在图像仅在第一次调用renderPixels 时呈现。原来我必须在draw call之前设置ShaderresourceView每次

let renderPixels () =
    switchTo2DLayout()

    // this line was missing:
    context.PixelShader.SetShaderResource(COUNT_COLOR_SLOT, countColorSRV_2D)

    context.ClearRenderTargetView (renderTargetView2D, Color.White.ToColor4())
    context.Draw(4,0)                                                
    swapChain.Present(1, PresentFlags.None)  

这完全出乎我的意料。我使用的有关 DirectX 的书籍从未明确说明哪些资源可以分配一次(只要设置不变),哪些资源必须在每次绘制调用上分配.

对于网格渲染,我使用了类似的设置(这里没有我提到的错误),但同样缺少等效的行:

let draw3D() = 
    switchTo3DLayout()

     // this line was missing:
     context.VertexShader.SetShaderResource(TRIANGLE_SLOT, triangleSRV  ) 

     context.ClearDepthStencilView(depthView3D, DepthStencilClearFlags.Depth, 1.0f, 0uy
     context.ClearRenderTargetView(renderTargetView2D, Color4.Black )                                                            
     context.Draw(triangleCount, 0)
     swapChain.Present(1, PresentFlags.None)    

这解释了为什么 2D 渲染工作因为这个错误(像素着色器从缓冲区读取),而 3D 没有(顶点着色器从缓冲区读取)。

======================== 我的原帖:=================

几天前我发布了一个问题[链接:]How can I feed compute shader results into vertex shader w/o using a vertex buffer?,这可能太复杂而无法回答。同时,我将设置简化为一个更简单的情况:

struct PS_IN
{
float4 pos : SV_POSITION;
float4 col : COLOR;
};

RWStructuredBuffer<float4> colorOutputTable :  register (u5);
StructuredBuffer<float4> output2 :             register (t5);  

案例 A:像素着色器设置颜色(有效)

// vertex shader A

PS_IN VS_A  ( uint vid : SV_VertexID )  
{
    PS_IN output = (PS_IN)0;   
    if (vid == 0) output.pos = float4(-1, -1, 0, 1);
    if (vid == 1) output.pos = float4( 1, -1, 0, 1);
    if (vid == 2) output.pos = float4(-1,  1, 0, 1);    
    if (vid == 3) output.pos = float4( 1,  1, 0, 1);    

    return output;  
}

// pixel shader A

float4  PS_A ( float4 input : SV_Position) : SV_Target
{        
  uint2 pixel =  uint2(input.x, input.y);       
  return output2[ pixel.y * width + pixel.x];   // PS accesses buffer (works)

}

案例 B:顶点着色器设置颜色(不起作用)

// vertex shader B

PS_IN VS_B ( uint vid : SV_VertexID )   
{
    PS_IN output = (PS_IN)0;   
    if (vid == 0) output.pos = float4(-1, -1, 0, 1);
    if (vid == 1) output.pos = float4( 1, -1, 0, 1);
    if (vid == 2) output.pos = float4(-1,  1, 0, 1);    
    if (vid == 3) output.pos = float4( 1,  1, 0, 1);    
    output.col = output2[vid];  // VS accesses buffer (does not work)
    return output;  
}

// pixel shader B

float4 PS_B  (PS_IN input ) : SV_Target
{
    return input.col;
}

显然像素着色器可以访问“output2”缓冲区,而顶点着色器不能(读取始终为零)。

在互联网上搜索我找不到任何关于这种行为的解释。在我的“真实”应用程序中,计算着色器计算一个三角形列表并将其存储在 RWStructuredBuffer 中,因此我需要从顶点着色器(通过映射槽)访问该表。

我想很多使用计算着色器的人可能会遇到这个问题。知道如何解决这个问题吗? (目前无法使用Level 11.1 或11.2,只好找基于11.0的解决方案)

【问题讨论】:

    标签: directx hlsl vertex-shader compute-shader


    【解决方案1】:

    刚刚在这里尝试过,您的着色器似乎可以工作(从未遇到过在任何阶段都无法访问 StructuredBuffer 的问题,功能级别 11)。

    示例来自 SharpDX minicube(只是替换了里面的着色器代码,并添加了一个缓冲区)。

    我唯一需要做的就是为您的三角带反向缠绕(我可以修改光栅化器)。

    由于我知道您在调试管道方面存在问题,因此查看是否有问题的另一种有用方法是使用查询(在您的情况下主要是 PipelineStatistics 和 Occlusion)。

    一些错误的状态可能很容易产生问题,因此您可以通过查看写入/渲染图元的像素数立即查看是否有问题。

    这里的着色器代码:

    struct PS_IN
    {
        float4 pos : SV_POSITION;
        float4 col : COLOR;
    };
    
    RWStructuredBuffer<float4> colorOutputTable :  register (u5);
    StructuredBuffer<float4> output2 :             register (t5);
    
    PS_IN VS_B(uint vid : SV_VertexID)
    {
        PS_IN output = (PS_IN)0;
        if (vid == 1) output.pos = float4(-1, -1, 0, 1);
        if (vid == 0) output.pos = float4(1, -1, 0, 1);
        if (vid == 3) output.pos = float4(-1, 1, 0, 1);
        if (vid == 2) output.pos = float4(1, 1, 0, 1);
        output.col = output2[vid];  // VS accesses buffer (does not work)
        return output;
    }
    
    // pixel shader B
    
    float4 PS_B(PS_IN input) : SV_Target
    {
        return input.col;
    }
    

    代码是here(它很笨重,所以我把它放在pastebin上)

    【讨论】:

    • 您能解释一下为什么在这种情况下反转绕组会有所不同吗?我的原始数据是一个 TriangleList,而这里的四个顶点形成一个 TriangleStrip - 我也必须反转我的三角形吗?
    • 由于我们没有指定光栅化状态,所以默认启用背面剔除(参见:msdn.microsoft.com/en-us/library/windows/desktop/…)。您将其缠绕反转,因此您的三角形面将被剔除,而不会进入像素着色器。
    • 当我将output.col = output2[vid]; 替换为output.col = float4( 0.5, 0.6, 0.8, 1); 时,我的绕组确实显示了一个蓝色矩形旁边 - 我总是使用顺时针绕组,只要我使用顶点缓冲区,它就可以与我的三角形列表一起使用(复制从 CPU 到 GPU)...
    • 取决于您如何设置光栅器,您是否尝试过 pastebin 中的代码?
    • 我可能会,但在开始之前我有一个问题:你没有复制RWStructuredBuffer&lt;float4&gt; colorOutputTable : register (u5); StructuredBuffer&lt;float4&gt; output2 : register (t5); 同时我已经尝试了你和我的绕组,当我设置颜色常量时,两者都产生了蓝色矩形VS,当我试图从缓冲区中获取颜色时,两者都保持黑色。也许插槽 5 中两个缓冲区的“映射”都不起作用,但是什么是可行的方法?计算着色器无法写入结构化缓冲区,VS 无法从 RW-Buffer 读取(如果我尝试,我的 HLSL 编译器会哭。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多