You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求简洁DirectX 12基础Compute Shader示例,实现后输出全零

简洁DirectX 12基础计算示例(加法操作)

核心步骤与代码

1. 初始化设备与命令队列

#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <vector>
#include <iostream>

#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")

ID3D12Device* g_pDevice = nullptr;
ID3D12CommandQueue* g_pCmdQueue = nullptr;
ID3D12CommandAllocator* g_pCmdAllocator = nullptr;
ID3D12GraphicsCommandList* g_pCmdList = nullptr;

void InitDevice() {
    IDXGIFactory6* pFactory = nullptr;
    CreateDXGIFactory1(IID_PPV_ARGS(&pFactory));

    IDXGIAdapter* pAdapter = nullptr;
    for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i) {
        DXGI_ADAPTER_DESC desc;
        pAdapter->GetDesc(&desc);
        if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
            if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_pDevice)))) {
                break;
            }
        }
        pAdapter->Release();
    }

    D3D12_COMMAND_QUEUE_DESC queueDesc = {};
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    g_pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&g_pCmdQueue));

    g_pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COMPUTE, IID_PPV_ARGS(&g_pCmdAllocator));
    g_pDevice->CreateGraphicsCommandList(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, g_pCmdAllocator, nullptr, IID_PPV_ARGS(&g_pCmdList));
    g_pCmdList->Close();

    pAdapter->Release();
    pFactory->Release();
}

2. 创建输入输出缓冲区

ID3D12Resource* g_pInputBuffer = nullptr;
ID3D12Resource* g_pOutputBuffer = nullptr;
ID3D12Resource* g_pOutputBufferUpload = nullptr;

const UINT kDataCount = 4;
struct Data { float value; };

void CreateBuffers() {
    UINT bufferSize = sizeof(Data) * kDataCount;

    D3D12_HEAP_PROPERTIES heapProps = {};
    heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;

    D3D12_RESOURCE_DESC resDesc = {};
    resDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resDesc.Width = bufferSize;
    resDesc.Height = 1;
    resDesc.DepthOrArraySize = 1;
    resDesc.MipLevels = 1;
    resDesc.Format = DXGI_FORMAT_UNKNOWN;
    resDesc.SampleDesc.Count = 1;
    resDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

    g_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &resDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&g_pInputBuffer));
    g_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &resDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&g_pOutputBuffer));

    heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
    ID3D12Resource* pInputUpload = nullptr;
    g_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &resDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pInputUpload));

    Data inputData[kDataCount] = {{1.0f}, {2.0f}, {3.0f}, {4.0f}};
    UINT8* pData;
    CD3DX12_RANGE readRange(0, 0);
    pInputUpload->Map(0, &readRange, reinterpret_cast<void**>(&pData));
    memcpy(pData, inputData, bufferSize);
    pInputUpload->Unmap(0, nullptr);

    g_pCmdAllocator->Reset();
    g_pCmdList->Reset(g_pCmdAllocator, nullptr);
    g_pCmdList->CopyResource(g_pInputBuffer, pInputUpload);
    CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(g_pInputBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
    g_pCmdList->ResourceBarrier(1, &barrier);
    g_pCmdList->Close();

    ID3D12CommandList* ppCmdLists[] = {g_pCmdList};
    g_pCmdQueue->ExecuteCommandLists(_countof(ppCmdLists), ppCmdLists);

    ID3D12Fence* pFence = nullptr;
    UINT64 fenceValue = 1;
    g_pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pFence));
    HANDLE hFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    g_pCmdQueue->Signal(pFence, fenceValue);
    if (pFence->GetCompletedValue() < fenceValue) {
        pFence->SetEventOnCompletion(fenceValue, hFenceEvent);
        WaitForSingleObject(hFenceEvent, INFINITE);
    }
    CloseHandle(hFenceEvent);
    pFence->Release();
    pInputUpload->Release();

    heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
    g_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &resDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_pOutputBufferUpload));
}

3. 编译计算Shader

创建ComputeAdd.hlsl文件:

struct Data { float value; };

RWStructuredBuffer<Data> OutputBuffer;
StructuredBuffer<Data> InputBuffer;

[numthreads(1,1,1)]
void CS(uint3 dispatchThreadID : SV_DispatchThreadID)
{
    OutputBuffer[dispatchThreadID.x].value = InputBuffer[dispatchThreadID.x].value + 10.0f;
}

编译代码:

ID3DBlob* g_pCSBlob = nullptr;

void CompileComputeShader() {
    HRESULT hr = D3DCompileFromFile(L"ComputeAdd.hlsl", nullptr, nullptr, "CS", "cs_5_0", D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &g_pCSBlob, nullptr);
    if (FAILED(hr)) {
        std::cerr << "计算Shader编译失败" << std::endl;
        exit(-1);
    }
}

4. 创建根签名与PSO

ID3D12RootSignature* g_pRootSignature = nullptr;
ID3D12PipelineState* g_pPSO = nullptr;

void CreateRootSignatureAndPSO() {
    CD3DX12_ROOT_PARAMETER rootParams[2];
    rootParams[0].InitAsShaderResourceView(0, 0, D3D12_SHADER_VISIBILITY_ALL);
    rootParams[1].InitAsUnorderedAccessView(0, 0, D3D12_SHADER_VISIBILITY_ALL);

    CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(_countof(rootParams), rootParams, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE);

    ID3DBlob* pRootSigBlob = nullptr;
    ID3DBlob* pErrorBlob = nullptr;
    D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &pRootSigBlob, &pErrorBlob);
    g_pDevice->CreateRootSignature(0, pRootSigBlob->GetBufferPointer(), pRootSigBlob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature));
    pRootSigBlob->Release();
    if (pErrorBlob) pErrorBlob->Release();

    D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
    psoDesc.pRootSignature = g_pRootSignature;
    psoDesc.CS.pShaderBytecode = g_pCSBlob->GetBufferPointer();
    psoDesc.CS.BytecodeLength = g_pCSBlob->GetBufferSize();
    g_pDevice->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&g_pPSO));
}

5. 创建SRV和UAV视图

ID3D12DescriptorHeap* g_pSrvUavHeap = nullptr;

void CreateViews() {
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heapDesc.NumDescriptors = 2;
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    g_pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&g_pSrvUavHeap));

    UINT descriptorSize = g_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srvDesc.Format = DXGI_FORMAT_UNKNOWN;
    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
    srvDesc.Buffer.FirstElement = 0;
    srvDesc.Buffer.NumElements = kDataCount;
    srvDesc.Buffer.StructureByteStride = sizeof(Data);
    srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
    CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(g_pSrvUavHeap->GetCPUDescriptorHandleForHeapStart());
    g_pDevice->CreateShaderResourceView(g_pInputBuffer, &srvDesc, srvHandle);

    D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
    uavDesc.Format = DXGI_FORMAT_UNKNOWN;
    uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uavDesc.Buffer.FirstElement = 0;
    uavDesc.Buffer.NumElements = kDataCount;
    uavDesc.Buffer.StructureByteStride = sizeof(Data);
    uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    CD3DX12_CPU_DESCRIPTOR_HANDLE uavHandle(g_pSrvUavHeap->GetCPUDescriptorHandleForHeapStart(), 1, descriptorSize);
    g_pDevice->CreateUnorderedAccessView(g_pOutputBuffer, nullptr, &uavDesc, uavHandle);
}

6. 录制并执行计算命令

void ExecuteCompute() {
    g_pCmdAllocator->Reset();
    g_pCmdList->Reset(g_pCmdAllocator, g_pPSO);

    g_pCmdList->SetComputeRootSignature(g_pRootSignature);
    ID3D12DescriptorHeap* ppHeaps[] = {g_pSrvUavHeap};
    g_pCmdList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);

    UINT descriptorSize = g_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    CD3DX12_GPU_DESCRIPTOR_HANDLE srvHandle(g_pSrvUavHeap->GetGPUDescriptorHandleForHeapStart());
    CD3DX12_GPU_DESCRIPTOR_HANDLE uavHandle(g_pSrvUavHeap->GetGPUDescriptorHandleForHeapStart(), 1, descriptorSize);

    g_pCmdList->SetComputeRootDescriptorTable(0, srvHandle);
    g_pCmdList->SetComputeRootDescriptorTable(1, uavHandle);

    g_pCmdList->Dispatch(kDataCount, 1, 1);

    CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(g_pOutputBuffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    g_pCmdList->ResourceBarrier(1, &barrier);

    g_pCmdList->CopyResource(g_pOutputBufferUpload, g_pOutputBuffer);

    g_pCmdList->Close();

    ID3D12CommandList* ppCmdLists[] = {g_pCmdList};
    g_pCmdQueue->ExecuteCommandLists(_countof(ppCmdLists), ppCmdLists);

    ID3D12Fence* pFence = nullptr;
    UINT64 fenceValue = 2;
    g_pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pFence));
    HANDLE hFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    g_pCmdQueue->Signal(pFence, fenceValue);
    if (pFence->GetCompletedValue() < fenceValue) {
        pFence->SetEventOnCompletion(fenceValue, hFenceEvent);
        WaitForSingleObject(hFenceEvent, INFINITE);
    }
    CloseHandle(hFenceEvent);
    pFence->Release();
}

7. 读取并输出结果

void ReadResults() {
    Data outputData[kDataCount];
    UINT8* pData;
    CD3DX12_RANGE readRange(0, sizeof(Data) * kDataCount);
    g_pOutputBufferUpload->Map(0, &readRange, reinterpret_cast<void**>(&pData));
    memcpy(outputData, pData, sizeof(Data) * kDataCount);
    g_pOutputBufferUpload->Unmap(0, nullptr);

    std::cout << "计算结果:" << std::endl;
    for (UINT i = 0; i < kDataCount; ++i) {
        std::cout << "输入 " << i+1 << ": " << i+1 << ".0f -> 输出: " << outputData[i].value << std::endl;
    }
}

8. 资源清理

void Cleanup() {
    if (g_pOutputBufferUpload) g_pOutputBufferUpload->Release();
    if (g_pOutputBuffer) g_pOutputBuffer->Release();
    if (g_pInputBuffer) g_pInputBuffer->Release();
    if (g_pSrvUavHeap) g_pSrvUavHeap->Release();
    if (g_pPSO) g_pPSO->Release();
    if (g_pRootSignature) g_pRootSignature->Release();
    if (g_pCSBlob) g_pCSBlob->Release();
    if (g_pCmdList) g_pCmdList->Release();
    if (g_pCmdAllocator) g_pCmdAllocator->Release();
    if (g_pCmdQueue) g_pCmdQueue->Release();
    if (g_pDevice) g_pDevice->Release();
}

主函数

int main() {
    InitDevice();
    CreateBuffers();
    CompileComputeShader();
    CreateRootSignatureAndPSO();
    CreateViews();
    ExecuteCompute();
    ReadResults();
    Cleanup();
    return 0;
}

关键注意事项

  • 资源状态转换:DirectX12必须手动管理资源状态,输入缓冲区需从COPY_DEST转为NON_PIXEL_SHADER_RESOURCE,输出缓冲区需从UNORDERED_ACCESS转为COPY_SOURCE才能拷贝到上传堆。
  • 根签名绑定:确保根参数索引与Shader中的绑定一致,描述符堆需设置为SHADER_VISIBLE。
  • 命令同步:每次执行命令后需等待Fence完成,确保GPU操作结束后再读取结果。

内容的提问来源于stack exchange,提问作者Zarsr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:15:05