【问题标题】:How can I feed compute shader results into vertex shader w/o using a vertex buffer?如何使用顶点缓冲区将计算着色器结果输入顶点着色器?
【发布时间】:2013-11-23 07:07:04
【问题描述】:

在我详细介绍之前,我想概述一下问题:

我使用 RWStructuredBuffers 来存储我的计算着色器 (CS) 的输出。由于顶点和像素着色器无法从 RWStructuredBuffers 中读取,我将一个 StructuredBuffer 映射到同一插槽 (u0/t0) 和 (u4/t4):

cbuffer cbWorld : register (b1) 
{
    float4x4 worldViewProj;
    int dummy;
}   

struct VS_IN
{
    float4 pos : POSITION;
    float4 col : COLOR;
};

struct PS_IN
{

    float4 pos : SV_POSITION;
    float4 col : COLOR;
};

RWStructuredBuffer<float4> colorOutputTable : register (u0);    // 2D color data
StructuredBuffer<float4> output2 :            register (t0);    // same as u0
RWStructuredBuffer<int> counterTable :        register (u1);    // depth data for z values
RWStructuredBuffer<VS_IN>vertexTable :        register (u4);    // triangle list
StructuredBuffer<VS_IN>vertexTable2 :         register (t4);    // same as u4

我使用 ShaderRecourceView 授予像素和/或顶点着色器访问缓冲区的权限。这个概念适用于我的像素着色器,但顶点着色器似乎只读取 0 值(我使用 SV_VertexID 作为缓冲区的索引):

PS_IN VS_3DA ( uint vid : SV_VertexID ) 
{           
    PS_IN output = (PS_IN)0; 
    PS_IN input = vertexTable2[vid];
    output.pos = mul(input.pos, worldViewProj); 
    output.col = input.col; 
    return output;
}

没有来自 hlsl 编译器的错误消息或警告,渲染循环以 60 fps(使用 vsync)运行,但屏幕仍然是黑色的。由于我在调用 Draw(..) 之前使用 Color.White 将屏幕空白,因此渲染管道似乎处于活动状态。

当我通过 UAView 从 GPU 读取三角形数据内容到“vertArray”并将其反馈到顶点缓冲区时,一切正常:

程序:

    let vertices = Buffer.Create(device, BindFlags.VertexBuffer, vertArray)
    context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf<Vector4>() * 2, 0))

HLSL:

PS_IN VS_3D (VS_IN input )
{
    PS_IN output = (PS_IN)0;    
    output.pos = mul(input.pos, worldViewProj);
    output.col = input.col; 
    return output;
}

这里定义了 2D - Vertex / Pixelshaders。请注意,PS_2D 访问插槽 t0 中的缓冲区“output2”——这正是我想要为 3D 顶点着色器“VS_3DA”复制的“技巧”:

float4 PS_2D ( float4 input : SV_Position) : SV_Target
{        
    uint2 pixel =  uint2(input.x, input.y);         
    return output2[ pixel.y * width + pixel.x]; 
}

float4 VS_2D ( uint vid : SV_VertexID ) : SV_POSITION
{
if (vid == 0)
    return float4(-1, -1, 0, 1);
if (vid == 1)
    return float4( 1, -1, 0, 1);
if (vid == 2)
    return float4(-1,  1, 0, 1);    

return float4( 1,  1, 0, 1);    
}

三天来,我一直在寻找和尝试,但无济于事。我收集的所有信息似乎都证实了我使用 SV_VertexID 的方法应该有效。

谁能给点建议?感谢您阅读我的帖子!

================================================ =======================

详情:

我非常喜欢 DirectX 11 计算着色器的概念,我想将它用于代数计算。作为一个测试用例,我在 3D 中渲染分形(Mandelbrot 集)。一切正常——除了墙上的最后一块砖不见了。

计算采取以下步骤:

  1. 使用 CS 计算 2D 纹理(输出为“counterTable”和“colorOutbutTable”(有效)

  2. 可选择将此纹理渲染到屏幕上(有效)

  3. 使用另一个 CS 生成网格(三角形列表)。此 CS 从步骤 1 中获取 x、y 和颜色值,计算 z 坐标,最后为每个像素创建一个四边形。结果存储在“vertexTable”中。 (作品)

  4. 将三角形列表提供给顶点着色器(问题!!!)

  5. 渲染到屏幕(工作 - 使用顶点缓冲区)。

对于编程,我使用 F# 3.0 和 SharpDX 作为 .NET 包装器。 两个着色器(像素和顶点)的 ShaderRessourceView 设置了相同的参数(大小参数除外):

let mutable descr = new BufferDescription()     
descr.BindFlags <- BindFlags.UnorderedAccess ||| BindFlags.ShaderResource 
descr.Usage <- ResourceUsage.Default  
descr.CpuAccessFlags <- CpuAccessFlags.None
descr.StructureByteStride <- xxx    / / depends on shader
descr.SizeInBytes <-  yyy       / / depends on shader
descr.OptionFlags <- ResourceOptionFlags.BufferStructured

这里没什么特别的。 创建 2D 缓冲区(绑定到插槽 t0 中的缓冲区“output2”):

outputBuffer2D <- new Buffer(device, descr) 
outputView2D <- new UnorderedAccessView (device, outputBuffer2D)  
shaderResourceView2D <- new ShaderResourceView (device, outputBuffer2D)

创建 3D 缓冲区(绑定到插槽 t4 中的“vertexTable2”):

vertexBuffer3D <- new Buffer(device, descr) 
shaderResourceView3D <- new ShaderResourceView (device, vertexBuffer3D)
//  UAView not required here

为 2D 设置资源:

context.InputAssembler.PrimitiveTopology <- PrimitiveTopology.TriangleStrip
context.OutputMerger.SetRenderTargets(renderTargetView2D)
context.OutputMerger.SetDepthStencilState(depthStencilState2D)
context.VertexShader.Set (vertexShader2D)
context.PixelShader.Set (pixelShader2D) 

渲染 2D:

context.PixelShader.SetShaderResource(COLOR_OUT_SLOT, shaderResourceView2D)
context.PixelShader.SetConstantBuffer(CONSTANT_SLOT_GLOBAL, constantBuffer2D )
context.ClearRenderTargetView (renderTargetView2D, Color.White.ToColor4())         
context.Draw(4,0)                                                
swapChain.Present(1, PresentFlags.None)            

为 3D 设置资源:

context.InputAssembler.PrimitiveTopology <- PrimitiveTopology.TriangleList
context.OutputMerger.SetTargets(depthView3D, renderTargetView2D)
context.VertexShader.SetShaderResource(TRIANGLE_SLOT, shaderResourceView3D )
context.VertexShader.SetConstantBuffer(CONSTANT_SLOT_3D, constantBuffer3D)
context.VertexShader.Set(vertexShader3D)
context.PixelShader.Set(pixelShader3D)

渲染 3D(不起作用 - 黑屏作为输出结果)

context.ClearDepthStencilView(depthView3D, DepthStencilClearFlags.Depth, 1.0f, 0uy)
context.Draw(dataXsize * dataYsize * 6, 0)
swapChain.Present(1, PresentFlags.None)

最后是槽号:

static let CONSTANT_SLOT_GLOBAL = 0
static let CONSTANT_SLOT_3D = 1
static let COLOR_OUT_SLOT = 0
static let COUNTER_SLOT = 1
static let COLOR_SLOT = 2    
static let TRIANGLE_SLOT = 4

【问题讨论】:

  • 你解决过这个问题吗?我想做几乎完全相同的事情,但它不适合我。你是怎么解决这个问题的?

标签: hlsl directx-11 vertex-shader compute-shader directcompute


【解决方案1】:

好的,我建议的第一件事是打开调试层(在创建设备时使用调试标志),然后转到项目属性,调试选项卡,然后勾选“启用非托管代码调试”或“启用本机代码调试” ”。

当您开始调试程序时,如果管道状态出现问题,运行时将给您潜在的警告。

一个潜在问题(与您发布的内容最有可能出现问题): 确保在调度后清理您的计算着色器 UAV 插槽。如果您尝试将 vertexTable2 绑定到您的顶点着色器,但该资源仍被绑定为计算着色器输出,则运行时会自动将您的 ShaderView 设置为 null(当您尝试读取它时,它又会返回 0)。

要清理您的计算着色器,请在您完成调度的设备上下文中调用它:

ComputeShader.SetUnorderedAccessView(TRIANGLE_SLOT, null)

另请注意,PixelShader 可以访问 RWStructuredBuffer(从技术上讲,如果您的功能级别为 11.1,则可以将 RWStructuredBuffer 用于任何着色器类型,这意味着最近的 ATI 卡和 Windows 8+)。

【讨论】:

  • 我使用 4 个计算着色器,并且在每次调度调用后清理所有资源,因此您的建议已经在代码中。抛出)。
  • 我目前在 Web 2012 中使用 VS Express(因为包含 F# 3.0);您描述的调试选项在我的调试属性选项卡上不存在(仅“启用 VS 托管进程”)。我想我必须使用完整版本的 VS 才能进行 DirectX 调试(当我使用调试选项创建交换链时,会引发异常)。所以我目前没有机会进行低级调试。如果您知道解释如何在 Visual Studio 中启用 DirectX 调试的网络资源,我将很高兴收到它。
  • SharpDX 对每个 DirectX 级别使用不同的库,所以我坚持使用 11.0 以向后兼容一些朋友。
  • 通常,只要您安装了 DirectX SDK(2010 年 6 月),创建带有调试标志的设备就可以了。要捕获警告,您还可以使用 windbg,它应该很乐意捕获这些警告(当然前提是调试标志处于打开状态)。
【解决方案2】:

将三角形列表提供给顶点着色器(问题!!!)

我会考虑使用原始缓冲区,而不是使用结构化缓冲区(不允许您绑定为 vb)。它需要在着色器中进行投射,但允许您在 cs 和 vs. 中使用相同的缓冲区。

创建缓冲区时,请执行以下操作:

D3D11_BUFFER_DESC desc = {};
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_VERTEX_BUFFER;
desc.ByteWidth = byteSize;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;

然后您可以绑定为着色器资源:

D3D11_SHADER_RESOURCE_VIEW_DESC desc = {};
desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
desc.BufferEx.FirstElement = 0;
desc.Format = DXGI_FORMAT_R32_TYPELESS;
desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
desc.BufferEx.NumElements = descBuf.ByteWidth / 4;

或无序访问视图:

D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {};
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
desc.Buffer.FirstElement = 0;
desc.Format = DXGI_FORMAT_R32_TYPELESS; // Format must be DXGI_FORMAT_R32_TYPELESS, when creating Raw Unordered Access View
desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
desc.Buffer.NumElements = descBuf.ByteWidth / 4; 

在着色器中你会使用这样的东西:

ByteAddressBuffer Buffer0 : register(t0);
ByteAddressBuffer Buffer1 : register(t1);
RWByteAddressBuffer BufferOut : register(u0);

int i0 = asint( Buffer0.Load( DTid.x*8 ) );
float f0 = asfloat( Buffer0.Load( DTid.x*8+4 ) );
int i1 = asint( Buffer1.Load( DTid.x*8 ) );
float f1 = asfloat( Buffer1.Load( DTid.x*8+4 ) );

BufferOut.Store( DTid.x*8, asuint(i0 + i1) );
BufferOut.Store( DTid.x*8+4, asuint(f0 + f1) );

上面的示例代码取自 DirectX June 2010 SDK 的 BasicCompute11 示例。它演示了如何使用结构化缓冲区和原始缓冲区。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 2013-11-26
    • 1970-01-01
    相关资源
    最近更新 更多