Files
XCEngine/D3D12_RHI_Test_Issue.md
ssdfasd 008fb98dee refactor(editor): Complete architecture refactoring
- SceneManager: remove singleton, use dependency injection via EditorContext
- SelectionManager: already interface-based via ISelectionManager
- Panel: now receives IEditorContext for accessing managers
- HierarchyPanel: migrated to use IEditorContext instead of singletons
- Add ISceneManager interface and SceneManagerImpl
- EditorContextImpl: holds all editor subsystems

Architecture now follows dependency injection pattern:
Application -> EditorContext -> SceneManager/SelectionManager
EditorLayer -> Panels (receive context via SetContext)

All Manager singletons removed: EditorSceneManager::Get(), SelectionManager::Get()
2026-03-25 15:51:27 +08:00

6.5 KiB
Raw Blame History

D3D12 RHI 测试失败问题报告

问题描述

5个D3D12测试在调用 CreateCommandList 时失败,错误码 DXGI_ERROR_NOT_CURRENTLY_AVAILABLE (0x887A0005)

失败的测试

测试名称 错误位置
CommandList_ClearRenderTarget_WithRealView CreateCommandList 返回 nullptr
CommandList_ClearDepthStencil_WithRealView CreateTexture 返回 nullptr (D24_S8_UInt格式)
CommandList_SetRenderTargets_WithRealViews CreateCommandList 返回 nullptr
CommandList_BeginEndRenderPass_Basic CreateCommandList 返回 nullptr
CommandList_BeginEndRenderPass_WithClear CreateCommandList 返回 nullptr

错误码分析

DXGI_ERROR_NOT_CURRENTLY_AVAILABLE (0x887A0005)

根据 Microsoft 文档,此错误表示:

"The resource or operation is not available at the current time. This can be returned when a command is submitted to a queue that is not in a state to process that command."

测试代码(最小复现)

TEST_P(RHITestFixture, CommandList_ClearRenderTarget_WithRealView) {
    // 1. 创建纹理 - 成功
    TextureDesc texDesc = {};
    texDesc.width = 256;
    texDesc.height = 256;
    texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
    texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
    RHITexture* texture = GetDevice()->CreateTexture(texDesc);
    ASSERT_NE(texture, nullptr);  // ✅ 通过

    // 2. 创建RTV - 成功
    RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, {});
    ASSERT_NE(rtv, nullptr);  // ✅ 通过

    // 3. 创建命令列表 - 失败
    CommandListDesc cmdDesc = {};
    cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
    RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
    ASSERT_NE(cmdList, nullptr);  // ❌ 失败cmdList == nullptr
}

调试日志

CreateTexture: start
CreateRenderTargetView: start
CreateCommandList: start
  m_commandQueue=00000180F8F722E0
  m_device=00000180F22B5B30
  CreateCommandAllocator hr=887A0005
CreateCommandList: allocator init failed

关键发现

1. 通过的测试(关键差异)

以下测试通过它们不创建纹理或只创建SRV

// ✅ 通过 - 只创建纹理不创建RTV
TEST_P(RHITestFixture, CommandList_Reset_Close) {
    CommandListDesc cmdDesc = {};
    cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
    RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
    ASSERT_NE(cmdList, nullptr);  // ✅ 通过
    cmdList->Shutdown();
    delete cmdList;
}

// ✅ 通过 - 创建纹理+SRV但不创建RTV
TEST_P(RHITestFixture, CommandList_TransitionBarrier_WithRealResource) {
    RHITexture* texture = GetDevice()->CreateTexture(texDesc);  // ✅
    RHIResourceView* srv = GetDevice()->CreateShaderResourceView(texture, {});  // ✅
    RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);  // ✅ 通过
}

2. 失败的测试(共同点)

所有失败的测试都先创建纹理再创建RTV然后创建命令列表

3. OpenGL vs D3D12

  • OpenGL: 全部117个测试通过
  • D3D12 (RHITestFixture): 112/117 通过5个失败
  • D3D12TestFixture: 全部53个测试通过

代码分析

CreateCommandList 实现

// engine/src/RHI/D3D12/D3D12Device.cpp:409
RHICommandList* D3D12Device::CreateCommandList(const CommandListDesc& desc) {
    auto* allocator = new D3D12CommandAllocator();
    
    // 失败发生在这里
    if (!allocator->Initialize(m_device.Get(), static_cast<CommandQueueType>(desc.commandListType))) {
        delete allocator;
        return nullptr;  // 返回 nullptr
    }
    
    auto* cmdList = new D3D12CommandList();
    if (!cmdList->Initialize(m_device.Get(), ...)) {
        delete allocator;
        delete cmdList;
        return nullptr;
    }
    return cmdList;
}

CreateCommandAllocator 实现

// engine/src/RHI/D3D12/D3D12CommandAllocator.cpp:15
bool D3D12CommandAllocator::Initialize(ID3D12Device* device, CommandQueueType type) {
    m_type = type;
    HRESULT hResult = device->CreateCommandAllocator(
        ToD3D12(type),  // D3D12_COMMAND_LIST_TYPE_DIRECT
        IID_PPV_ARGS(&m_commandAllocator));
    return SUCCEEDED(hResult);  // 返回 falsehr=887A0005
}

CreateRenderTargetView 实现

// engine/src/RHI/D3D12/D3D12Device.cpp:455
RHIResourceView* D3D12Device::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
    auto* view = new D3D12ResourceView();
    auto* d3d12Texture = static_cast<D3D12Texture*>(texture);
    ID3D12Resource* resource = d3d12Texture->GetResource();

    D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
    rtvDesc.Format = static_cast<DXGI_FORMAT>(desc.format);  // desc.format = 0 (Unknown!)
    rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;

    // 创建非 shader-visible 的 descriptor heap
    DescriptorHeapDesc heapDesc = {};
    heapDesc.descriptorCount = 1;
    heapDesc.heapType = static_cast<uint32_t>(DescriptorHeapType::RTV);
    heapDesc.shaderVisible = false;  // 非 shader-visible
    auto* heapPool = CreateDescriptorHeap(heapDesc);

    view->InitializeAsRenderTarget(m_device.Get(), resource, &rtvDesc, heap, 0);
    return view;
}

已排除的原因

  1. 调试层问题 - 禁用后仍然失败
  2. 命令队列类型不匹配 - 使用相同的 CommandQueueType::Direct
  3. 静态全局状态污染 - 每个测试创建独立设备
  4. 内存泄漏导致descriptor heap耗尽 - 内存问题不会导致此错误码
  5. 测试顺序依赖 - 每个测试有独立的 SetUp/TearDown

疑点

  1. 为什么 CreateCommandAllocator 在创建 RTV非shader-visible descriptor heap之后会失败
  2. D3D12TestFixture使用相同设备创建方式为什么全部通过
  3. 为什么 SRVshader-visible descriptor heap不会触发这个问题只有 RTV 会?

相关文件

  • engine/src/RHI/D3D12/D3D12Device.cpp - CreateCommandList, CreateTexture, CreateRenderTargetView
  • engine/src/RHI/D3D12/D3D12CommandAllocator.cpp - CreateCommandAllocator
  • engine/src/RHI/D3D12/D3D12DescriptorHeap.cpp - CreateDescriptorHeap
  • engine/src/RHI/D3D12/D3D12ResourceView.cpp - InitializeAsRenderTarget
  • tests/RHI/unit/test_command_list.cpp - 失败的测试
  • tests/RHI/unit/fixtures/RHITestFixture.cpp - 测试fixture

测试环境

  • 平台: Windows
  • RHI后端: D3D12
  • 测试框架: Google Test
  • 编译配置: Debug