Vertices in compute shader

Started by
6 comments, last by Jason Z 12 years ago
Hello,

I'm trying to code a particle system which uses compute shader. My goal is to load a fixed number of particles in the gpu and let them permanently there while the compute shader does the gravity simulation.
I have created a class that should handle everything related to the compute shader. It hast two buffers (source and destination) and for each buffer a UAV and for the destination buffer also a SRV. The compute shader just copies the particles from the source Buffer to the destination buffer in the compute shader and then sets the destination buffer's SRV as a shader resource to the vertex buffer. But somehow no operations on my particles in the compute shader seem to work.

EDIT:
I cut it down to the main problem:
I created to buffers (source and destination), with unordered acces view and as shader resources. Both have an UAV with the "D3D11_BUFFER_UAV_FLAG_APPEND" flag bound to them. The destination buffer also gets a SRV, so that i can bind it to the vertex shaders as a shader resource. The source buffer gets all the particles which are just XMFLOAT3 variable during its initialisation.
So I've got two Buffers and their UAV's and the destination's SRV.
ID3D11Buffer* _srcParticleBuffer;
ID3D11UnorderedAccessView* _srcParticleBufferViewUAV;
ID3D11Buffer* _dstParticleBuffer;
ID3D11ShaderResourceView* _dstParticleBufferView;
ID3D11UnorderedAccessView* _dstParticleBufferViewUAV;


Everytime I render the scene I call the following functions:
devContext->CSSetShader(_computeShader, 0, 0);
devContext->CSSetUnorderedAccessViews(0, 1, &_srcParticleBufferViewUAV, 0);
devContext->CSSetUnorderedAccessViews(1, 1, &_dstParticleBufferViewUAV, 0);
devContext->Dispatch(100, 1, 1);
devContext->CopyResource(_srcParticleBuffer, _dstParticleBuffer);
devContext->VSSetShaderResources(0, 1, &_dstParticleBufferView);


My compute shader looks like this:
struct Particle{
float3 pos;
};
ConsumeStructuredBuffer<Particle> srcParticleBuffer : register(u0);
AppendStructuredBuffer<Particle> dstParticleBuffer : register(u1);
[numthreads(100, 1, 1)]
void CS_Main( uint3 dispatchThreadID : SV_DispatchThreadID )
{
Particle p = srcParticleBuffer.Consume();
p.pos.z = 5.0f;
dstParticleBuffer.Append(p);
}


And my vertex shader:
struct VS_INPUT{
uint vertexID : SV_VertexID;
};
struct Particle{
float3 pos : POSITION;
};
StructuredBuffer<Particle> particleBuffer : register(u0);
GS_INPUT VS_Main( VS_INPUT input){
GS_INPUT output = (GS_INPUT)0;
output.pos.xyz = particleBuffer[input.vertexID].pos;
output.pos.w = 1.0f;
output.tex0 = float2(0,0);
return output;
}


But somehow nothing happens. All my particles are created at the origin although they are supposed to be created at (0,0,5). How can I use the output buffer (destination buffer) as a valid shader resource which has all my particle positions to create the vertices in the vertex shader?
Advertisement
Hi!

In my experience, fetching particles in the vertex shader from a structured buffer is slower, than using a vertex buffer to read in the particle data. Though this means, you can’t use structured buffers anymore, since they can’t have the D3D11_BIND_VERTEX_BUFFER bind flag. Instead you’d have to use a byte address buffer.

Besides, it would also be easier if you use CopyResource to copy the initial data from the source to the destination buffer, which means you don’t need a UAV for the initial data.

But I see, eventually you want to modify the particles in the compute shader, which is why you need to access them at some point. Hm, you used consume and append buffers. This looks rather complicated and I believe that it requires internally some kind of synchronization since every thread wants to pick the last element. It would certainly be better to use the thread id to load from the buffers. If you have a byte address buffer use:
RWByteAddressBuffer srcParticleBuffer : register(u0); // SRV would do here too.
RWByteAddressBuffer dstParticleBuffer : register(u1);

float3 pos = asfloat(srcParticleBuffer.Load3(dispatchThreadID.x * 12) );
pos.x += 5;
dstParticleBuffer.Store3(dispatchThreadID.x * 12, asuint(pos) );


If you want to stick with structured buffers than better access them this way:
RWStructuredBuffer<Particle> srcParticleBuffer : register(u0); // SRV would do here too.
RWStructuredBuffer<Particle> dstParticleBuffer : register(u1);

Particle p = srcParticleBuffer [dispatchThreadID.x];
p.pos.x += 5;
dstParticleBuffer[dispatchThreadID.x] = p;


Could you show us, how you initialized the buffers and views? Perhaps you forgot to initialize one of the description’s fields. The StructureByteSize for instance is one that doesn’t cause failure during resource creation, but if forgotten it may appear that writes don’t occur (in your case the StructureByteSize would be 12).

Cheers!
Here is my code for the buffer initialization:
D3D11_BUFFER_DESC buffDesc;
ZeroMemory(&buffDesc, sizeof(buffDesc));
buffDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE ;
buffDesc.ByteWidth = sizeof(Particle)*maxParticles;
buffDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
buffDesc.StructureByteStride= sizeof(Particle);
buffDesc.Usage = D3D11_USAGE_DEFAULT;

D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(data));
data.pSysMem = particles;
device->CreateBuffer(&buffDesc, &data, &_srcParticleBuffer)

D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(uavDesc));
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_APPEND;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.Buffer.NumElements = maxParticles;
device->CreateUnorderedAccessView(_srcParticleBuffer, &uavDesc, &_srcParticleBufferViewUAV);


D3D11_BUFFER_DESC buffDesc;
ZeroMemory(&buffDesc, sizeof(buffDesc));
buffDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
buffDesc.ByteWidth = sizeof(Particle)*maxParticles;
buffDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
buffDesc.StructureByteStride= sizeof(Particle);
buffDesc.Usage = D3D11_USAGE_DEFAULT;
device->CreateBuffer(&buffDesc, 0, &_dstParticleBuffer);

ZeroMemory(&buffDesc, sizeof(buffDesc));
_dstParticleBuffer->GetDesc(&buffDesc);

D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
srvDesc.Buffer.NumElements = maxParticles;

ZeroMemory(&buffDesc, sizeof(buffDesc));
_dstParticleBuffer->GetDesc(&buffDesc);

D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(uavDesc));
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_APPEND;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Format = DXGI_FORMAT_UNKNOWN; //Must be UNKOWN when creating structured Buffer
uavDesc.Buffer.NumElements = maxParticles;



I will try this out with RWbuffers right now and tell you if it works.

EDIT:
I took away the "D3D_BUFFER_UAV_FLAG_APPEND" flag from both UAV's and tried to use the RWStructuredBuffers in the compute shader:
struct Particle{
float3 pos;
};
RWStructuredBuffer<Particle> srcParticleBuffer : register(u0);
RWStructuredBuffer<Particle> dstParticleBuffer : register(u1);
[numthreads(10, 1, 1)]
void CS_Main( uint3 dispatchThreadID : SV_DispatchThreadID )
{
Particle p = srcParticleBuffer[dispatchThreadID.x];
p.pos.z = 5.0f;
dstParticleBuffer[dispatchThreadID.x] = p;
}


But still nothing happens. I also tried to use a SRV for the srcParticleBuffer but it's the same. :/


EDIT 2:
Ok, I finally got it. The problem was that everytime I dispatched the compute shader I had to set the UAV's bound to the pipeline to NULL in order to work with the output data. Now I use only one RWStructuredBuffer in my compute Shader as input and output and a SRV and UAV of this buffer.
The code looks like that at the moment:

When rendering the scene I call:
devContext->CSSetShader(_computeShader, 0, 0);
devContext->CSSetUnorderedAccessViews(0, 1, &_srcParticleBufferViewUAV, NULL);
devContext->Dispatch(100, 1, 1);
ID3D11UnorderedAccessView* pNullUAV = NULL;
devContext->CSSetUnorderedAccessViews( 0, 1, &pNullUAV, NULL );
devContext->VSSetShaderResources(0, 1, &_srcParticleBufferView);


And my compute shader looks like this:
struct Particle{
float3 pos;
};
RWStructuredBuffer<Particle> srcParticleBuffer : register(u0);
[numthreads(10, 1, 1)]
void CS_Main( uint3 dispatchThreadID : SV_DispatchThreadID )
{
Particle p = srcParticleBuffer[dispatchThreadID.x];
p.pos.z = 5.0;
srcParticleBuffer[dispatchThreadID.x] = p;
}


And now everything works fine. :)
I never read something about binding NULL UAVs in order to use the buffer as a shader resource. Sometimes I think DirectX would be much easier at the beginning if a great overview of alle the buffers and views and usage would be provided but instead one has to pick every little detail he finds on the internet or the msdn and put it to a big picture to understand someday how everything is working.

Anyway, can someone tell me if there is a better method of handling the buffers in order to get more speed? Would it be faster using two buffers and then copying the resource when handling millions of particles?

And now everything works fine. smile.png
I never read something about binding NULL UAVs in order to use the buffer as a shader resource. Sometimes I think DirectX would be much easier at the beginning if a great overview of alle the buffers and views and usage would be provided but instead one has to pick every little detail he finds on the internet or the msdn and put it to a big picture to understand someday how everything is working.

That is exactly why we put the resources chapter right before the pipeline chapter in our book :) The interplay between the resources and the pipeline are the trickiest part of D3D11 programming - at least in my opinion!
Bought your book on VitalSource because I couldn't wait the weekend for shipping. Just recently I purchased a hard copy. I reviewed it on Amazon, too. Very good book.

P.S. If you wrote one about a good way to write an abstraction layer for a general graphics engine I would definitely buy it.ohmy.png

Bought your book on VitalSource because I couldn't wait the weekend for shipping. Just recently I purchased a hard copy. I reviewed it on Amazon, too. Very good book.

P.S. If you wrote one about a good way to write an abstraction layer for a general graphics engine I would definitely buy it.ohmy.png


Thanks for the feedback! Maybe sometime down the road another book may be in the cards, but right now I'm focusing on updating Hieroglyph 3. Thanks for the suggestion though - I'll be sure to take that into consideration when planning the next project.
I already took in consideration to buy this book. Maybe I should do it now and stop wasting so much time on little things. xD
The Particle Storm example of the hyroglyph engine helped me a lot in underrstaning the concept of this system although i couldnt see any D3D functions at first sight. :)
Yes, there is a little bit of hiding from the application layer, but the renderer and its friends are pretty thin wrappers around the API. Even so, you can always ask questions here for some quick answers :)

This topic is closed to new replies.

Advertisement