I've just started using the Compute shader stage in DirectX 11 and encountered some unwanted behaviour when writing to an output resource in the Compute shader. I seem to get only zeroes as output which, to my understanding, means that out-of-bound reads has been performed in the Compute shader. (Out-of-bound writes results in no-ops)
Creating the Compute shader components
Input resources
First I create an ID3D11Buffer*
for input data. This is passed as a resource when creating the SRV used for input to the Compute shader stage. If the input data never changes then we could release the ID3D11Buffer*
object after creating the SRV since the SRV is going to act as a handle to the resource.
However, I want to update the input data each frame so I'm just going to keep the buffer at my disposal for mapping.
// Create a buffer to be bound as Compute Shader input (D3D11_BIND_SHADER_RESOURCE).
D3D11_BUFFER_DESC constantDataDesc;
constantDataDesc.Usage = D3D11_USAGE_DYNAMIC;
constantDataDesc.ByteWidth = sizeof(ParticleConstantData) * NUM_PARTICLES;
constantDataDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
constantDataDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
constantDataDesc.StructureByteStride = sizeof(ParticleConstantData);
constantDataDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
hr = device->CreateBuffer ( &constantDataDesc, 0, &mInputBuffer );
Creating the SRV using the newly created buffer as resource
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
srvDesc.BufferEx.FirstElement = 0;
srvDesc.BufferEx.Flags = 0;
srvDesc.BufferEx.NumElements = NUM_PARTICLES;
hr = device->CreateShaderResourceView( mInputBuffer, &srvDesc, &mInputView );
Output resources
Now I need to create a resource for the Compute shader to write to. I will also create a system memory version of the buffer to read from. I will use the ID3D11DeviceContext::CopyResource method to copy data from the Compute shader output buffer, connected to the UAV, to the system memory version for performing mapping and save its content back into system memory.
Create a read-write buffer the compute shader can write to
(D3D11_BIND_UNORDERED_ACCESS).
D3D11_BUFFER_DESC outputDesc;
outputDesc.Usage = D3D11_USAGE_DEFAULT;
outputDesc.ByteWidth = sizeof(ParticleData) * NUM_PARTICLES;
outputDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
outputDesc.CPUAccessFlags = 0;
outputDesc.StructureByteStride = sizeof(ParticleData);
outputDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
hr = ( device->CreateBuffer( &outputDesc, 0, &mOutputBuffer ) );
Create a system memory version of the buffer to read the results back from
outputDesc.Usage = D3D11_USAGE_STAGING;
outputDesc.BindFlags = 0;
outputDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
hr = ( device->CreateBuffer( &outputDesc, 0, &mOutputResultBuffer ) );
Create UAV for compute shader to write results
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.Flags = 0;
uavDesc.Buffer.NumElements = NUM_PARTICLES;
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
hr = device->CreateUnorderedAccessView( mOutputBuffer, &uavDesc, &mOutputUAV );
Executing Compute shader (each frame)
C++
mParticleSystem.FillConstantDataBuffer( mDeviceContext, mInputBuffer );
// Enable Compute Shader
mDeviceContext->CSSetShader( mComputeShader, nullptr, 0 );
mDeviceContext->CSSetShaderResources( 0, 1, &mInputView );
mDeviceContext->CSSetUnorderedAccessViews( 0, 1, &mOutputUAV, 0 );
// Dispatch
mDeviceContext->Dispatch( 1, 1, 1 );
// Unbind the input textures from the CS for good housekeeping
ID3D11ShaderResourceView* nullSRV[] = { NULL };
mDeviceContext->CSSetShaderResources( 0, 1, nullSRV );
// Unbind output from compute shader
ID3D11UnorderedAccessView* nullUAV[] = { NULL };
mDeviceContext->CSSetUnorderedAccessViews( 0, 1, nullUAV, 0 );
// Disable Compute Shader
mDeviceContext->CSSetShader( nullptr, nullptr, 0 );
// Copy result
mDeviceContext->CopyResource( mOutputBuffer, mOutputResultBuffer );
// Update particle system data with output from Compute Shader
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = mDeviceContext->Map( mOutputResultBuffer, 0, D3D11_MAP_READ, 0, &mappedResource );
if( SUCCEEDED( hr ) )
{
ParticleData* dataView = reinterpret_cast<ParticleData*>(mappedResource.pData);
// Update particle positions and velocities
mParticleSystem.UpdatePositionAndVelocity( dataView );
mDeviceContext->Unmap( mOutputResultBuffer, 0 );
}
HLSL
struct ConstantParticleData
{
float3 position;
float3 velocity;
float3 initialVelocity;
};
struct ParticleData
{
float3 position;
float3 velocity;
};
StructuredBuffer<ConstantParticleData> inputConstantParticleData : register( t0 );
RWStructuredBuffer<ParticleData> outputParticleData : register( u0 );
[numthreads(32, 1, 1)]
void CS_main( int3 dispatchThreadID : SV_DispatchThreadID )
{
outputParticleData[dispatchThreadID.x].position = inputConstantParticleData[dispatchThreadID.x].position;
}
I'm sorry for the amount of content in this question. I've structured it with care so you may find it easier to get an overview.
Number of elements passed to shader is 32.
Any suggestions on my problem? Thank you!
RWStructuredBuffer
is filled correctly. So there must be something happening after that. Perhaps at the mapping phase after copying the output resource to the system memory buffer. I'll look into that! – SvinSimpe