如何修复CreateCommandAllocator()返回的DXGI_ERROR_DEVICE_REMOVED错误?
DX12创建CommandAllocator报错DXGI_ERROR_DEVICE_REMOVED问题排查
问题现象
尝试创建ID3D12CommandAllocator*时,调用CreateCommandAllocator返回DXGI_ERROR_DEVICE_REMOVED,调用GetDeviceRemovedReason()得到DXGI_ERROR_INVALID_CALL。移除渲染目标视图(RTV)创建代码后,程序可正常运行。
相关代码
创建CommandAllocator的代码
for(UINT i=0;i<NUM_BACK_BUFFERS;i++) ThrowFEX(DX12.d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&DX12.Buffers.pCommandAllocator[i])), ERROR_CREATECOMMANDALLOCATOR, DX12.d3dDevice);
渲染目标视图创建代码
D3D12_HEAP_PROPERTIES properties; properties.Type=D3D12_HEAP_TYPE_DEFAULT; properties.CPUPageProperty=D3D12_CPU_PAGE_PROPERTY_UNKNOWN; properties.MemoryPoolPreference=D3D12_MEMORY_POOL_UNKNOWN; properties.CreationNodeMask=0; properties.VisibleNodeMask=0; D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; // 资源类型为Buffer desc.Alignment = 0; // 对齐字节数,默认0 desc.Width = windowRect.right * windowRect.bottom * sizeof(DirectX::XMFLOAT4); // 资源字节大小 desc.Height = 1; // Buffer高度(固定为1) desc.DepthOrArraySize = 1; // 深度或数组元素数量(Buffer为1) desc.MipLevels = 1; // 多级纹理数量(Buffer为1) desc.Format = DXGI_FORMAT_UNKNOWN; // 资源数据格式(默认未知) desc.SampleDesc.Count = 1; // 采样数 desc.SampleDesc.Quality = 0; // 采样质量 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; // 资源数据布局 desc.Flags = D3D12_RESOURCE_FLAG_NONE; // 资源标志 // 获取帧缓冲区 for(UINT i=0;i<NUM_BACK_BUFFERS;i++){ ThrowF(DX12.ixgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&DX12.Buffers.renderTargets[i])), ERROR_GETFRAMEBUFFERS); ThrowF(DX12.d3dDevice->CreateCommittedResource(&properties, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_PRESENT, nullptr, IID_PPV_ARGS(&DX12.Buffers.renderTargets[i])), ERROR_GETFRAMEBUFFERS); } D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = DX12.rtvHeap->GetCPUDescriptorHandleForHeapStart(); // 获取帧视图缓冲区的CPU句柄 for(UINT i=0;i<NUM_BACK_BUFFERS;i++){// 移除下面这行后程序正常运行 DX12.d3dDevice->CreateRenderTargetView(DX12.Buffers.renderTargets[i], nullptr, rtvHandle); // 为帧缓冲区创建视图 rtvHandle.ptr += i * DX12.d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); // 偏移句柄 }
SwapChain创建代码
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width=0; swapChainDesc.Height=0; swapChainDesc.BufferCount=NUM_BACK_BUFFERS; swapChainDesc.AlphaMode=DXGI_ALPHA_MODE_UNSPECIFIED; swapChainDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.Flags=DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; swapChainDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.SampleDesc.Count=1; swapChainDesc.SampleDesc.Quality=0; swapChainDesc.Scaling=DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect=DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.Stereo=FALSE; ThrowF(DX12.dxgiFactory->CreateSwapChainForHwnd(DX12.d3dCommandQueue, hwnd, &swapChainDesc, NULL, NULL, &DX12.ixgiSwapChain), ERROR_CREATESWAPCHAIN)
设备创建代码
D3D12_COMMAND_QUEUE_DESC desc = {}; desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; desc.NodeMask = 0; D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; heapDesc.NumDescriptors = NUM_BACK_BUFFERS; // 描述符数量 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // 堆类型为RTV heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; // 标志 ThrowF(CreateDXGIFactory1( IID_PPV_ARGS(&DX12.dxgiFactory) ), ERROR_CREATEDEVICE) // 创建DXGI工厂 ThrowF(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&DX12.d3dDevice) ), ERROR_CREATEDEVICE) // 创建D3D12设备 ThrowF(DX12.d3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&DX12.d3dCommandQueue)), ERROR_CREATEDEVICE) // 创建命令队列 ThrowF(DX12.d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&DX12.rtvHeap)), ERROR_CREATEDEVICE) // 创建RTV描述符堆 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; // 堆类型为CBV/SRV/UAV heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; // 堆对着色器可见 heapDesc.NumDescriptors = 1; // 描述符数量 for(int i;i<NUM_BACK_BUFFERS;i++) ThrowF(DX12.d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&DX12.Buffers.cvbHeap[i])), ERROR_CREATEDEVICE) // 为常量缓冲区创建描述符堆
问题根源及修复方案
1. 错误覆盖交换链Buffer指针
在获取帧缓冲区的循环中,先调用GetBuffer获取了交换链原生的2D纹理渲染目标资源,但紧接着调用CreateCommittedResource创建了Buffer类型资源并覆盖指针:
- Buffer类型资源无法用于创建RTV,触发非法调用导致设备被移除;
- 同时造成
GetBuffer获取的资源内存泄漏。
修复:删除错误的CreateCommittedResource调用,直接使用交换链返回的资源:
for(UINT i=0;i<NUM_BACK_BUFFERS;i++){ ThrowF(DX12.ixgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&DX12.Buffers.renderTargets[i])), ERROR_GETFRAMEBUFFERS); // 移除此行错误代码 // ThrowF(DX12.d3dDevice->CreateCommittedResource(&properties, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_PRESENT, nullptr, IID_PPV_ARGS(&DX12.Buffers.renderTargets[i])), ERROR_GETFRAMEBUFFERS); }
2. RTV描述符句柄偏移错误
当前代码用i * 增量计算偏移,会导致句柄每次偏移量翻倍,最终超出堆范围。正确做法是每次循环增加单个描述符的固定增量:
修复:
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = DX12.rtvHeap->GetCPUDescriptorHandleForHeapStart(); UINT rtvIncrement = DX12.d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); for(UINT i=0;i<NUM_BACK_BUFFERS;i++){ DX12.d3dDevice->CreateRenderTargetView(DX12.Buffers.renderTargets[i], nullptr, rtvHandle); rtvHandle.ptr += rtvIncrement; // 每次偏移单个描述符大小 }
3. 常量缓冲区描述符堆循环变量未初始化
设备创建代码中,常量缓冲区堆的循环变量i未初始化,会导致未定义行为:
修复:
// 原代码 for(int i;i<NUM_BACK_BUFFERS;i++) // 修复后 for(int i=0;i<NUM_BACK_BUFFERS;i++)
4. 可选优化:设置资源状态
交换链返回的资源初始状态为D3D12_RESOURCE_STATE_PRESENT,若要用于渲染,需通过命令队列提交资源转换屏障,将其转换为D3D12_RESOURCE_STATE_RENDER_TARGET状态。
内容的提问来源于stack exchange,提问作者I101I
相关产品推荐
相关产品推荐

