十八、D3D12学习笔记——计算着色器通用计算

OK,我们现在回到计算着色器最具价值的一个部分,通用计算GPGPU。当然现在有很多方法实现通用计算,比如英伟达提出的CUDA解决方案,可能这在深度学习方面还是不错的,不过如果了解过CUDA的人,会对环境配置就感到困惑,比如我,所以我还是倾向于采用D3D提供的并行计算来进行通用计算的设计。

学习这个部分你需要对计算着色器有一定的了解,不了的话可以去看我第十四篇文章,至少了解到启动计算着色器:

mCommandList->Dispatch(x, y, z);

因为本篇我们就主要面对数据的输入输出(CUDA中是直接使用内存开辟函数来进行的)。

一、计算数据输入

数据与资源的准备

    std::vector<Data> dataA(NumDataElements);
    std::vector<Data> dataB(NumDataElements);//两组输入数据

    for(int i = 0; i < NumDataElements; ++i)
    {
        dataA[i].=为数据赋值;
        dataB[i].=为数据赋值;

    }

    UINT64 byteSize = dataA.size()*sizeof(Data);

    // 数据通过上传堆存储于默认堆
    mInputBufferA = d3dUtil::CreateDefaultBuffer(
        md3dDevice.Get(),
        mCommandList.Get(),
        dataA.data(),
        byteSize,
        mInputUploadBufferA);

    mInputBufferB = d3dUtil::CreateDefaultBuffer(
        md3dDevice.Get(),
        mCommandList.Get(),
        dataB.data(),
        byteSize,
        mInputUploadBufferB);

    // 使用UAV创建可以写数据的资源
    ThrowIfFailed(md3dDevice->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(byteSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS),
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS,//资源状态
        nullptr,
        IID_PPV_ARGS(&mOutputBuffer)));
    //为拷贝数据准备的回读堆
    ThrowIfFailed(md3dDevice->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
        D3D12_RESOURCE_STATE_COPY_DEST,//资源状态
        nullptr,
        IID_PPV_ARGS(&mReadBackBuffer)));

根签名

 CD3DX12_ROOT_PARAMETER slotRootParameter[3];

    // 以根描述符来设置资源与寄存器槽的关联
    slotRootParameter[0].InitAsShaderResourceView(0);
    slotRootParameter[1].InitAsShaderResourceView(1);
    slotRootParameter[2].InitAsUnorderedAccessView(0);

ComputPSO设置

与计算着色器部分一致

D3D12_COMPUTE_PIPELINE_STATE_DESC computePsoDesc = {};
    computePsoDesc.pRootSignature = mRootSignature.Get();
    computePsoDesc.CS =
    {
        reinterpret_cast<BYTE*>(mShaders["vecAddCS"]->GetBufferPointer()),
        mShaders["vecAddCS"]->GetBufferSize()
    };
    computePsoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
    ThrowIfFailed(md3dDevice->CreateComputePipelineState(&computePsoDesc, IID_PPV_ARGS(&mPSOs["vecAdd"])));

将资源绑定到流水线

设置寄存器槽与默认堆的对应关系,进行数据的填充

mCommandList->SetComputeRootShaderResourceView(0, mInputBufferA->GetGPUVirtualAddress());
    mCommandList->SetComputeRootShaderResourceView(1, mInputBufferB->GetGPUVirtualAddress());
    mCommandList->SetComputeRootUnorderedAccessView(2, mOutputBuffer->GetGPUVirtualAddress());

对应到shader中为:

StructuredBuffer<Data> gInputA : register(t0);
StructuredBuffer<Data> gInputB : register(t1);
RWStructuredBuffer<Data> gOutput : register(u0);

启动计算着色器

mCommandList->Dispatch(1, 1, 1);//根据需要设置

对应计算着色器

//描述的是一个组内的设计,即一个数据并行算法的设计

[numthreads(32, 1, 1)]//32组数据一起执行相同的计算过程
void CS(int3 dtid : SV_DispatchThreadID)
{
    gOutput[dtid.x = 计算逻辑;
}

计算完成后,数据全部进入gOutput,这是一个UAV。

二、数据的输出

mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mOutputBuffer.Get(),
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE));

    mCommandList->CopyResource(mReadBackBuffer.Get(), mOutputBuffer.Get());

    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mOutputBuffer.Get(),
        D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON));

1.转换资源状态为拷贝数据的资源;

2.使用CopyResource实现将资源从GPU拷贝到回读堆

3.将回读堆数据映射到一个数组mappedData ,则可从数组取数据

Data* mappedData = nullptr;
    ThrowIfFailed(mReadBackBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mappedData)));

    std::ofstream fout("results.txt");

    for(int i = 0; i < NumDataElements; ++i)
    {
        fout << "(" << mappedData[i].v1.x << ", " << mappedData[i].v1.y << ", " << mappedData[i].v1.z <<
            ", " << mappedData[i].v2.x << ", " << mappedData[i].v2.y << ")" << std::endl;
    }

    mReadBackBuffer->Unmap(0, nullptr);

综上实现了数据传入,计算,传出。

建立这么一个框架之后,实际需要我们进行编程的地方就是标红部分。是不是既简单又通用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值