Como posso alimentar os resultados do shader de computação no shader de vértice sem usar um buffer de vértice?
Antes de entrar em detalhes, quero descrever o problema:
Eu uso RWStructuredBuffers para armazenar a saída dos meus computados shaders (CS). Como os sombreadores de vértice e pixel não conseguem ler de RWStructuredBuffers, mapeio um StructuredBuffer no mesmo slot (u0 / t0) e (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
Eu uso um ShaderRecourceView para conceder acesso de pixel e / ou shader de vértice para os buffers. Este conceito funciona bem para o meu pixel shader, o vertex shader, no entanto, parece ler apenas 0 valores (eu uso SV_VertexID como índice para os buffers):
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;
}
Nenhuma mensagem de erro ou aviso do compilador hlsl, o renderloop é executado com 60 fps (usando vsync), mas a tela permanece preta. Como anulo a tela com Color.White antes do Draw (..) ser chamado, o pipeline de renderização parece estar ativo.
Quando eu leio o conteúdo dos dados do triângulo por meio de um UAView da GPU em “vertArray” e o coloco de volta em um buffer de vértice, tudo funciona no entanto:
Programa:
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;
}
Aqui a definição do 2D - Vertex / Pixelshaders. Por favor note que PS_2D acessa o buffer "output2" no slot t0 - e é exatamente esse o "truque" que eu quero replicar para o 3D shader de vértice "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);
}
Por três dias eu procurei e experimentei sem sucesso. Todas as informações que reuni parecem confirmar que minha abordagem usando SV_VertexID deve funcionar.
Alguém pode dar conselhos? Obrigado por ler meu post!
================================================== ===================
DETALHES:
Eu gosto muito do conceito de shaders de computação DirectX 11 e quero empregá-lo para computação algébrica. Como um caso de teste, eu faço fractais (conjuntos Mandelbrot) em 3D. Tudo funciona como esperado - exceto um último tijolo na parede está faltando.
O cálculo segue os seguintes passos:
Usando um CS para calcular uma textura 2D (a saída é "counterTable" e "colorOutbutTable" (funciona)
Opcionalmente, renderize essa textura para a tela (funciona)
Usando outro CS para gerar uma malha (lista de triângulos). Este CS obtém os valores x, y e color do passo 1, calcula a coordenada ze finalmente cria um quad para cada pixel. O resultado é armazenado em “vertexTable”. (trabalho)
Alimentando a lista de triângulos para o shader de vértice (problema !!!)
Renderizar para tela (funciona - usando um buffer de vértice).
Para programação eu uso F # 3.0 e SharpDX como wrapper .NET. O ShaderRessourceView para ambos os shaders (pixel e vértice) é configurado com os mesmos parâmetros (exceto os parâmetros de tamanho):
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
Nada de especial aqui. Criação de buffer 2D (liga-se ao buffer "output2" no slot t0):
outputBuffer2D <- new Buffer(device, descr)
outputView2D <- new UnorderedAccessView (device, outputBuffer2D)
shaderResourceView2D <- new ShaderResourceView (device, outputBuffer2D)
Criação de buffer 3D (liga-se a "vertexTable2" no slot t4):
vertexBuffer3D <- new Buffer(device, descr)
shaderResourceView3D <- new ShaderResourceView (device, vertexBuffer3D)
// UAView not required here
Definindo recursos para 2D:
context.InputAssembler.PrimitiveTopology <- PrimitiveTopology.TriangleStrip
context.OutputMerger.SetRenderTargets(renderTargetView2D)
context.OutputMerger.SetDepthStencilState(depthStencilState2D)
context.VertexShader.Set (vertexShader2D)
context.PixelShader.Set (pixelShader2D)
renderizar 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)
Definindo recursos para 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)
render 3D (não funciona - tela preta como resultado de saída)
context.ClearDepthStencilView(depthView3D, DepthStencilClearFlags.Depth, 1.0f, 0uy)
context.Draw(dataXsize * dataYsize * 6, 0)
swapChain.Present(1, PresentFlags.None)
Finalmente os números dos slots:
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