求简洁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
相关产品推荐
相关产品推荐

