You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 22:24:59