DirectX 11: sombreador de cómputo: escritura en un recurso de salida

Acabo de comenzar a usar la etapa Compute shader en DirectX 11 y encontré un comportamiento no deseado al escribir en un recurso de salida en el sombreador Compute. Parece que solo obtengo ceros como salida, lo que, a mi entender, significa que las lecturas fuera de límite se han realizado en el sombreador Compute. (Escrituras fuera de límite resultados en no-ops)

Crear los componentes del sombreador Compute

Recursos de entrada

Primero creo unID3D11Buffer* para datos de entrada. Esto se pasa como un recurso cuando se crea el SRV utilizado para la entrada a la etapa Compute shader. Si los datos de entrada nunca cambian, entonces podríamos liberar elID3D11Buffer* objeto después de crear el SRV ya que el SRV va a actuar como un manejador del recurso.

Sin embargo, quiero actualizar los datos de entrada de cada cuadro, así que solo voy a mantener el búfer a mi disposición para el mapeo.

// 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 );

Crear el SRV utilizando el búfer recién creado como recurso

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 );

Recursos de salida

Ahora necesito crear un recurso para que escriba el sombreador Compute. También crearé una versión de memoria del sistema del búfer para leer. Usaré elID3D11DeviceContext :: método CopyResource para copiar datos del búfer de salida del sombreador Compute, conectado al UAV, a la versión de memoria del sistema para realizar la asignación y guardar su contenido nuevamente en la memoria del sistema.

Cree un búfer de lectura y escritura en el que el sombreador de cálculo pueda escribir

(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 ) );

Cree una versión de memoria del sistema del búfer para volver a leer los resultados

outputDesc.Usage            = D3D11_USAGE_STAGING;
outputDesc.BindFlags        = 0;
outputDesc.CPUAccessFlags   = D3D11_CPU_ACCESS_READ;

hr = ( device->CreateBuffer( &outputDesc, 0, &mOutputResultBuffer ) );

Crear UAV para calcular sombreador para escribir resultados

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 );
Ejecutar sombreador de cómputo (cada cuadro)

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;
}

Lamento la cantidad de contenido en esta pregunta. Lo he estructurado con cuidado para que le resulte más fácil obtener una visión general.

El número de elementos pasados al sombreador es 32.

¿Alguna sugerencia sobre mi problema? ¡Gracias!