DirectX 11 - 计算着色器:写入到输出资源

8
我刚开始使用DirectX 11中的计算着色器阶段,但在写入计算着色器的输出资源时遇到了一些不想要的行为。据我理解,我只得到了零作为输出,这意味着在计算着色器中执行了越界读取。(越界写入会导致无操作)。

创建计算着色器组件

输入资源

首先,我创建一个用于输入数据的ID3D11Buffer*。在创建用于计算着色器阶段输入的SRV时,将其作为资源传递。如果输入数据永远不会更改,则可以在创建SRV后释放ID3D11Buffer*对象,因为SRV将充当资源的句柄。

然而,我希望每帧更新输入数据,因此我将保留该缓冲区以进行映射。

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

使用新创建的缓冲区作为资源创建SRV
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 );

输出资源

现在我需要为计算着色器创建一个资源进行写入。同时,我将创建一个系统内存版本的缓冲区以供读取。我将使用ID3D11DeviceContext::CopyResource方法从与UAV连接的计算着色器输出缓冲区复制数据到系统内存版本,以便进行映射并将其内容保存回系统内存。

创建一个可读写的缓冲区,计算着色器可以写入

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

创建一个系统内存版本的缓冲区,以便从中读取结果。
outputDesc.Usage            = D3D11_USAGE_STAGING;
outputDesc.BindFlags        = 0;
outputDesc.CPUAccessFlags   = D3D11_CPU_ACCESS_READ;

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

创建无人机用于计算着色器以写入结果。
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 );

每帧执行计算着色器

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

非常抱歉这个问题中的内容较多。我已经仔细组织了它,以便您更容易地获取概述。

传递到着色器的元素数量为32。

对我的问题有任何建议吗?谢谢!


你尝试过启用调试层来检查是否有任何错误吗?这只需要在创建设备时将“Flags”设置为D3D11_CREATE_DEVICE_DEBUG即可。请参阅https://msdn.microsoft.com/zh-cn/library/windows/desktop/jj200584(v=vs.85).aspx。 - Adam Miles
@AdamMiles 我已经启用了调试层,没有错误或警告。谢谢您的建议!我使用VS图形诊断工具来调试计算着色器,RWStructuredBuffer已经正确填充。因此,在那之后一定会发生一些事情。也许是在将输出资源复制到系统内存缓冲区后的映射阶段。我会去研究这个问题! - SvinSimpe
1个回答

4
您在调用CopyResource时,源和目标的顺序是错误的。请注意修改。

就是这样了!干得好,先生 :) - SvinSimpe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接