D3D11触发Fence后Map调用仍缓慢的问题排查
计算着色器Map调用后台性能暴跌问题分析
现状概述
运行计算着色器,通过D3D11 Query统计得着色器本身耗时约0.22ms,但实际总耗时平均约0.6ms,Map调用占用了部分时间。着色器负责计算自上一帧以来所有变更像素的dirty RECT并返回CPU,功能运行正常。
核心问题:正常场景下总耗时约0.6ms,性能理想;但程序后台运行且存在GPU密集型应用(如Blender、3A游戏)时,总耗时从0.6ms飙升至9ms,Map调用速度异常缓慢,需要定位原因。
当前实现代码细节
结果缓冲区创建
buffer_desc.ByteWidth = sizeof(RECT); buffer_desc.StructureByteStride = sizeof(RECT); buffer_desc.Usage = D3D11_USAGE_DEFAULT; buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS; buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; buffer_desc.CPUAccessFlags = 0; hr = ID3D11Device5_CreateBuffer(ctx->device, &buffer_desc, NULL, &ctx->reduction_final);
UAV设置
uav_desc.Format = DXGI_FORMAT_UNKNOWN; uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; uav_desc.Buffer.FirstElement = 0; uav_desc.Buffer.NumElements = 1; uav_desc.Buffer.Flags = 0; hr = ID3D11Device5_CreateUnorderedAccessView(ctx->device, (ID3D11Resource*) ctx->reduction_final, &uav_desc, &ctx->reduction_final_UAV);
暂存缓冲区创建
buffer_desc.ByteWidth = sizeof(RECT); buffer_desc.StructureByteStride = sizeof(RECT); buffer_desc.Usage = D3D11_USAGE_STAGING; buffer_desc.BindFlags = 0; buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; hr = ID3D11Device5_CreateBuffer(ctx->device, &buffer_desc, NULL, &ctx->cpu_staging);
Map调用实现
hr = ID3D11DeviceContext4_Map(ctx->device_ctx, (ID3D11Resource*) ctx->cpu_staging, 0, D3D11_MAP_READ, 0, &mapped_res);
Fence与事件创建
hr = ID3D11Device5_CreateFence(ctx->device, 1, D3D11_FENCE_FLAG_NONE, &IID_ID3D11Fence, (void**) &ctx->fence); ctx->fence_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
Dispatch后的复制与Fence信号
ID3D11DeviceContext4_CopyResource(ctx->device_ctx, (ID3D11Resource*) ctx->cpu_staging, (ID3D11Resource*) ctx->reduction_final); ID3D11DeviceContext4_Signal(ctx->device_ctx, ctx->fence, ++ctx->fence_counter); hr = ID3D11Fence_SetEventOnCompletion(ctx->fence, ctx->fence_counter, ctx->fence_handle);
等待与Map读取逻辑
DWORD waitResult = WaitForSingleObject(ctx->fence_handle, 1); if (waitResult != WAIT_OBJECT_0) { LOG("Timeout Count: %u", ++timeout_count); return false; } else { LOG("Timeout Count: %u", timeout_count); ID3D11DeviceContext4_Map(ctx->device_ctx, (ID3D11Resource*) ctx->cpu_staging, 0, D3D11_MAP_READ, 0, &mapped_res); memcpy(rect, mapped_res.pData, sizeof(RECT)); ID3D11DeviceContext4_Unmap(ctx->device_ctx, (ID3D11Resource*) ctx->cpu_staging, 0); return true; }
核心疑问
- 对Map调用的工作原理存在哪些误解?
- 有没有办法缩短Map调用的耗时?
- Fence是否可能在复制操作完成前就触发信号?
已尝试的优化方案
- 设置
SetGPUThreadPriority为7 - 设置
AvSetMmThreadCharacteristicsW为"DisplayPostProcessing"、"Games"和"Capture" - 设置
AvSetMmThreadPriority为AVRT_PRIORITY_CRITICAL
内容的提问来源于stack exchange,提问作者vulkur
相关产品推荐
相关产品推荐

