首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

純 C 語言驅動 Direct3D 11 Compute Shader 實現 GPGPU 通用運算

     編輯:關於C

自 Direct3D 11 引入 Compute Shader 起,GPU 通用運算 (GPGPU) 不再仰賴 CUDA/OpenCL 即可在 Windows 平台實現。本文提供完整、可編譯的純 C 語言範例,展示如何建立裝置、配置緩衝區、編譯著色器、派遣運算並讀回結果。

開發環境與專案設定

  • Visual Studio 2015+ (支援 C99 特性:stdbool.h、stdint.h、指定初始化器)
  • Windows SDK 10 (含 d3d11.h, d3dcompiler.h)
  • 專案屬性:
    ▸ C/C++ → General → Additional Include: $(WindowsSDK_IncludePath)
    ▸ Linker → General → Additional Library: $(WindowsSDK_LibraryPath_x64)
    ▸ Linker → Input → Additional Dependencies: d3d11.lib; d3dcompiler.lib; dxguid.lib
    ▸ Platform: x64

核心流程總覽

  1. D3D11CreateDevice 建立裝置與即時內容
  2. D3DCompileFromFile 編譯 HLSL Compute Shader
  3. 建立 Structured Buffer (SRV/UAV) 與 Constant Buffer
  4. CSSetShaderResources/CSSetUnorderedAccessViews 綁定資源
  5. Dispatch 派遣執行緒群組
  6. 建立 STAGING 緩衝區 CopyResource → Map 讀回 CPU

關鍵資源建立函式

Structured Buffer (可同時綁定 SRV 與 UAV)

bool CreateStructuredBuffer(ID3D11Device *dev, uint32_t stride, uint32_t count,
                            void *init, ID3D11Buffer **out) {
    D3D11_BUFFER_DESC desc = {0};
    desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
    desc.ByteWidth = stride * count;
    desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
    desc.StructureByteStride = stride;
    D3D11_SUBRESOURCE_DATA data = {init, 0, 0};
    return SUCCEEDED(dev->lpVtbl->CreateBuffer(dev, &desc, init ? &data : NULL, out));
}

Constant Buffer (動態更新)

bool CreateConstantBuffer(ID3D11Device *dev, uint32_t bytes, void *init,
                          ID3D11Buffer **out) {
    D3D11_BUFFER_DESC desc = {0};
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.ByteWidth = bytes;
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    D3D11_SUBRESOURCE_DATA data = {init, 0, 0};
    return SUCCEEDED(dev->lpVtbl->CreateBuffer(dev, &desc, &data, out));
}

SRV / UAV 視圖建立

bool CreateBufferSRV(ID3D11Device *dev, ID3D11Buffer *buf, ID3D11ShaderResourceView **srv) {
    D3D11_BUFFER_DESC db; buf->lpVtbl->GetDesc(buf, &db);
    D3D11_SHADER_RESOURCE_VIEW_DESC desc = {0};
    desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
    desc.Format = DXGI_FORMAT_UNKNOWN;
    desc.BufferEx.NumElements = db.ByteWidth / db.StructureByteStride;
    return SUCCEEDED(dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)buf, &desc, srv));
}

bool CreateBufferUAV(ID3D11Device *dev, ID3D11Buffer *buf, ID3D11UnorderedAccessView **uav) {
    D3D11_BUFFER_DESC db; buf->lpVtbl->GetDesc(buf, &db);
    D3D11_UNORDERED_ACCESS_VIEW_DESC desc = {0};
    desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
    desc.Format = DXGI_FORMAT_UNKNOWN;
    desc.Buffer.NumElements = db.ByteWidth / db.StructureByteStride;
    return SUCCEEDED(dev->lpVtbl->CreateUnorderedAccessView(dev, (ID3D11Resource*)buf, &desc, uav));
}

Compute Shader 派遣與同步

void RunComputeShader(ID3D11DeviceContext *ctx, ID3D11ComputeShader *cs,
                      uint32_t nSRV, uint32_t nUAV,
                      ID3D11ShaderResourceView *srvs[],
                      ID3D11UnorderedAccessView *uavs[],
                      uint32_t X, uint32_t Y, uint32_t Z) {
    ctx->lpVtbl->CSSetShader(ctx, cs, NULL, 0);
    ctx->lpVtbl->CSSetShaderResources(ctx, 0, nSRV, srvs);
    ctx->lpVtbl->CSSetUnorderedAccessViews(ctx, 0, nUAV, uavs, NULL);
    ctx->lpVtbl->Dispatch(ctx, X, Y, Z);  // 例:2048/1024=2 群組

    // 清理綁定避免殘留
    ID3D11UnorderedAccessView *nullUAV[2] = {NULL, NULL};
    ID3D11ShaderResourceView *nullSRV[2] = {NULL, NULL};
    ID3D11Buffer *nullCB[1] = {NULL};
    ctx->lpVtbl->CSSetShader(ctx, NULL, NULL, 0);
    ctx->lpVtbl->CSSetUnorderedAccessViews(ctx, 0, 2, nullUAV, NULL);
    ctx->lpVtbl->CSSetShaderResources(ctx, 0, 2, nullSRV);
    ctx->lpVtbl->CSSetConstantBuffers(ctx, 0, 1, nullCB);
}

HLSL Compute Shader (compute.hlsl)

struct BufType { int i; float f; };

cbuffer cbNeverChanges : register(b0) {
    int cValue0; int cValue1;
};

StructuredBuffer buffer0 : register(t0);
StructuredBuffer buffer1 : register(t1);
RWStructuredBuffer bufferOut : register(u0);
RWStructuredBuffer   srcdstBuffer : register(u1);

[numthreads(1024, 1, 1)]
void CSMain(uint3 tid : SV_DispatchThreadID) {
    uint idx = tid.x;
    int cVal = cValue1 / cValue0;  // 20/10 = 2
    int res = (buffer0[idx].i + buffer1[idx].i) * cVal - srcdstBuffer[idx];
    bufferOut[idx].i = res;
    bufferOut[idx].f = (buffer0[idx].f + buffer1[idx].f) * (float)cVal;
    srcdstBuffer[idx] = res;  // 回寫供下一幀使用
}

主程式完整流程

int main(void) {
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    ID3D11Device *dev; ID3D11DeviceContext *ctx;
    D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
        D3D11_CREATE_DEVICE_DEBUG, NULL, 0, D3D11_SDK_VERSION, &dev, NULL, &ctx);

    ID3D11ComputeShader *cs;
    D3DCompileFromFile(L"compute.hlsl", NULL, NULL, "CSMain", "cs_5_0",
        D3DCOMPILE_DEBUG | D3DCOMPILE_ENABLE_STRICTNESS, 0, &blob, NULL);
    dev->lpVtbl->CreateComputeShader(dev, blob->lpVtbl->GetBufferPointer(blob),
        blob->lpVtbl->GetBufferSize(blob), NULL, &cs);

    // 準備資料
    struct BufType h0[2048], h1[2048];
    int local[2048];
    for (int i=0; i<2048; i++) {
        h0[i] = h1[i] = (struct BufType){i, (float)i};
        local[i] = i + 1;
    }
    int cval[4] = {10, 20};  // constant buffer 需 16 bytes 對齊

    // 建立 GPU 資源
    CreateStructuredBuffer(dev, sizeof(struct BufType), 2048, h0, &buf0);
    CreateStructuredBuffer(dev, sizeof(struct BufType), 2048, h1, &buf1);
    CreateStructuredBuffer(dev, sizeof(struct BufType), 2048, NULL, &outBuf);
    CreateStructuredBuffer(dev, sizeof(int), 2048, local, &srcdstBuf);
    CreateConstantBuffer(dev, sizeof(cval), cval, &cb);
    ctx->lpVtbl->CSSetConstantBuffers(ctx, 0, 1, &cb);

    // 建立視圖
    CreateBufferSRV(dev, buf0, &srv0);
    CreateBufferSRV(dev, buf1, &srv1);
    CreateBufferUAV(dev, outBuf, &uavOut);
    CreateBufferUAV(dev, srcdstBuf, &uavSrcDst);

    // 派遣
    ID3D11ShaderResourceView *srvs[] = {srv0, srv1};
    ID3D11UnorderedAccessView *uavs[] = {uavOut, uavSrcDst};
    RunComputeShader(ctx, cs, 2, 2, srvs, uavs, 2, 1, 1);  // 2 群組 × 1024 threads

    // 讀回結果
    ID3D11Buffer *stage = CreateStagingCopy(dev, ctx, outBuf);
    D3D11_MAPPED_SUBRESOURCE map;
    ctx->lpVtbl->Map(ctx, (ID3D11Resource*)stage, 0, D3D11_MAP_READ, 0, &map);
    struct BufType *gpu = (struct BufType*)map.pData;
    for (int i=0; i<10; i++) printf("[%d] i=%d f=%.1f\n", i, gpu[i].i, gpu[i].f);
    ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)stage, 0);
    stage->lpVtbl->Release(stage);

    // 釋放所有資源...
    return 0;
}

效能與除錯提示

  • 執行緒群組大小:numthreads(1024,1,1) 達硬體上限,Dispatch(2,1,1) 覆蓋 2048 元素
  • 除錯層:D3D11_CREATE_DEVICE_DEBUG + D3DCOMPILE_DEBUG 可在 VS Graphics Diagnostics 捕捉
  • 資源轉換:Compute Shader 無需手動 Resource Barrier (D3D12 才需)
  • 效能分析:使用 ID3D11Query (TIMESTAMP/DISJOINT) 測量 GPU 耗時

為何選擇 D3D11 Compute Shader?

Windows Store / UWP 應用僅允許 Microsoft 官方 API,D3D11 Compute Shader 是原生 GPGPU 路徑;對比 C++ AMP(已棄維)、OpenCL(Windows 驅動支援不一),D3D11 相容性最佳、部署零相依。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved