使用CopyResource()+Map()/Unmap()导致性能损耗的解决方案咨询
D3D11 UAV数据读取性能损耗解决方案
问题背景
无额外数据读取逻辑时3D场景FPS约5000,使用CopyResource+Map读取UAV数据后FPS降至2500,性能减半。已确认瓶颈为CPU等待GPU完成4字节数据拷贝,使用D3D11_MAP_FLAG_DO_NOT_WAIT时Map()持续返回DXGI_ERROR_WAS_STILL_DRAWING。
核心原因
GPU与CPU异步执行,调用CopyResource后GPU尚未完成数据拷贝,CPU立刻调用Map会进入阻塞等待状态,直接拉长单帧耗时。
解决方案
1. 双缓冲延迟读取(推荐)
通过双缓冲机制让CPU读取上一帧的GPU结果,当前帧的拷贝操作留到下一帧处理,彻底避免CPU等待GPU。
初始化代码修改
// 定义双缓冲用的staging buffer数组 ID3D11Buffer* outputResultBuffer[2] = { nullptr, nullptr }; D3D11_BUFFER_DESC outputDesc; // --- 原outputBuffer初始化逻辑不变 --- outputDesc.Usage = D3D11_USAGE_DEFAULT; outputDesc.ByteWidth = sizeof(float); outputDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS; outputDesc.CPUAccessFlags = 0; outputDesc.StructureByteStride = sizeof(float); outputDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; FOG_TRACE(mDevice->CreateBuffer(&outputDesc, nullptr, &outputBuffer)); // 创建两个staging buffer用于双缓冲 outputDesc.Usage = D3D11_USAGE_STAGING; outputDesc.BindFlags = 0; outputDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; FOG_TRACE(mDevice->CreateBuffer(&outputDesc, nullptr, &outputResultBuffer[0])); FOG_TRACE(mDevice->CreateBuffer(&outputDesc, nullptr, &outputResultBuffer[1])); // --- 原UAV初始化逻辑不变 --- D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{}; uavDesc.Buffer.FirstElement = 0; uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_APPEND; uavDesc.Buffer.NumElements = 1; uavDesc.Format = DXGI_FORMAT_UNKNOWN; uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; FOG_TRACE(mDevice->CreateUnorderedAccessView(outputBuffer, &uavDesc, &unorderedAccessView)); // 双缓冲索引标记 int currentBufferIndex = 0;
更新阶段代码修改
const UINT offset = 0; mDeviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &mRenderTargetView, mDepthStencilView, 1, 1, &unorderedAccessView, &offset); mDeviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); ObjectManager::Draw(); // 切换双缓冲:读取上一帧的buffer,当前帧数据拷贝到新的buffer int readBufferIndex = currentBufferIndex; currentBufferIndex = (currentBufferIndex + 1) % 2; // 拷贝当前帧GPU数据到当前索引的staging buffer mDeviceContext->CopyResource(outputResultBuffer[currentBufferIndex], outputBuffer); // 读取上一帧已完成拷贝的buffer(无阻塞) D3D11_MAPPED_SUBRESOURCE mappedBuffer; HRESULT hr; FOG_TRACE(hr = mDeviceContext->Map(outputResultBuffer[readBufferIndex], 0, D3D11_MAP_READ, 0, &mappedBuffer)); if (SUCCEEDED(hr)) { float* copy = (float*)(mappedBuffer.pData); OutputDebugString(String::ToStr(*copy) + L"\n"); } mDeviceContext->Unmap(outputResultBuffer[readBufferIndex], 0); const UINT var[4]{}; mDeviceContext->ClearUnorderedAccessViewUint(unorderedAccessView, var);
2. GPU查询同步(需实时读取当前帧数据时使用)
使用D3D11_QUERY_EVENT查询GPU是否完成拷贝操作,避免CPU无意义空等,同时可在等待间隙执行其他轻量CPU任务。
初始化代码修改
ID3D11Query* copyCompleteQuery = nullptr; D3D11_QUERY_DESC queryDesc{}; queryDesc.Query = D3D11_QUERY_EVENT; FOG_TRACE(mDevice->CreateQuery(&queryDesc, ©CompleteQuery)); // --- 原其他初始化逻辑不变 ---
更新阶段代码修改
// --- 原渲染、Draw逻辑不变 --- ObjectManager::Draw(); mDeviceContext->CopyResource(outputResultBuffer, outputBuffer); // 提交查询,标记GPU操作完成节点 mDeviceContext->End(copyCompleteQuery); // 等待GPU完成拷贝,期间可插入其他CPU任务 while (true) { HRESULT hr = mDeviceContext->GetData(copyCompleteQuery, nullptr, 0, 0); if (hr == S_OK) break; // 示例:执行输入处理、UI逻辑更新等轻量任务 Input::Update(); } // 此时GPU已完成拷贝,Map无阻塞 D3D11_MAPPED_SUBRESOURCE mappedBuffer; HRESULT hr; FOG_TRACE(hr = mDeviceContext->Map(outputResultBuffer, 0, D3D11_MAP_READ, 0, &mappedBuffer)); if (SUCCEEDED(hr)) { float* copy = (float*)(mappedBuffer.pData); OutputDebugString(String::ToStr(*copy) + L"\n"); } mDeviceContext->Unmap(outputResultBuffer, 0); // --- 原Clear UAV逻辑不变 ---
3. 调整读取时机(并行CPU任务)
将Map/Unmap操作延迟到GPU执行渲染命令的间隙,让CPU在等待GPU的同时处理其他任务,减少空耗:
// 1. 提交所有GPU渲染命令 ObjectManager::Draw(); mDeviceContext->CopyResource(outputResultBuffer, outputBuffer); // 2. 执行其他CPU任务(如游戏逻辑、UI更新、输入处理) GameLogic::UpdateFrame(); UI::Render(); // 3. 此时GPU大概率已完成拷贝,执行Map读取 D3D11_MAPPED_SUBRESOURCE mappedBuffer; HRESULT hr; FOG_TRACE(hr = mDeviceContext->Map(outputResultBuffer, 0, D3D11_MAP_READ, 0, &mappedBuffer)); // --- 后续读取操作不变 ---
内容的提问来源于stack exchange,提问作者DownloaderGames
相关产品推荐
相关产品推荐

