自 Direct3D 11 引入 Compute Shader 起,GPU 通用運算 (GPGPU) 不再仰賴 CUDA/OpenCL 即可在 Windows 平台實現。本文提供完整、可編譯的純 C 語言範例,展示如何建立裝置、配置緩衝區、編譯著色器、派遣運算並讀回結果。
stdbool.h、stdint.h、指定初始化器)d3d11.h, d3dcompiler.h)$(WindowsSDK_IncludePath)$(WindowsSDK_LibraryPath_x64)d3d11.lib; d3dcompiler.lib; dxguid.libD3D11CreateDevice 建立裝置與即時內容D3DCompileFromFile 編譯 HLSL Compute ShaderCSSetShaderResources/CSSetUnorderedAccessViews 綁定資源Dispatch 派遣執行緒群組STAGING 緩衝區 CopyResource → Map 讀回 CPUbool 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));
}
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));
}
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));
}
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);
}
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 捕捉ID3D11Query (TIMESTAMP/DISJOINT) 測量 GPU 耗時Windows Store / UWP 應用僅允許 Microsoft 官方 API,D3D11 Compute Shader 是原生 GPGPU 路徑;對比 C++ AMP(已棄維)、OpenCL(Windows 驅動支援不一),D3D11 相容性最佳、部署零相依。