亲爱的 IMGUI 和 DirectX 12 覆盖 (DXGI_ERROR_INVALID_CALL)

Posted

技术标签:

【中文标题】亲爱的 IMGUI 和 DirectX 12 覆盖 (DXGI_ERROR_INVALID_CALL)【英文标题】:Dear IMGUI and DirectX 12 Overlay (DXGI_ERROR_INVALID_CALL) 【发布时间】:2020-08-27 12:07:26 【问题描述】:

我正在尝试使用 Dear IMGUI 为 DirectX 12 游戏制作一个简单的帧计数器。我只是想覆盖一个小的透明窗口,在游戏过程中显示帧的顺序。为此,我挂钩 Present(),因此我可以获得SwapChain,并计算该方法被调用的次数(帧计数)。 这不是作弊。我不是为游戏写作弊,我只是想记录帧数以用于分析目的。

我已使用此处提供的 ShowExampleAppSimpleOverlay() 示例成功为 DirectX 11 完成此操作:https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp

这是一个显示 DX 11 游戏中帧计数器的图像示例。

我现在正尝试对 DirectX 12 做同样的事情。挂钩 Present() 不是问题。

使用此处提供的示例代码:https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx12/main.cpp

我尝试再次使用ShowExampleAppSimpleOverlay() 方法,但是在我调用d3d12CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&d3d12CommandList);(以呈现覆盖)的代码中,它会导致错误提示(0x887A0001: DXGI_ERROR_INVALID_CALL)。这是下面提供的代码示例中的最后一行代码:

我不确定如何继续。有什么想法吗?

编辑:我忘了提到我也在连接和获取游戏命令 que。所以d3d12CommandQueue是直接从游戏中获取的。它不返回 NULL 所以我假设它是正确的对象。不过我可能是错的......

对于 Present() 的每次调用,请执行以下操作:

//iterate frame
Frame_Number = Frame_Number + 1;

//Get Device, using IDXGISwapChain3
ID3D12Device* device;
HRESULT gd = pSwapChain->GetDevice(__uuidof(ID3D12Device), (void**)&device);
assert(gd == S_OK);

//Get window handle from swapchain for IMGUI
DXGI_SWAP_CHAIN_DESC sd;
pSwapChain->GetDesc(&sd);
window = sd.OutputWindow;

//Get backbuffers
buffersCounts = sd.BufferCount;
frameContext = new FrameContext[buffersCounts];

D3D12_DESCRIPTOR_HEAP_DESC descriptorImGuiRender = ;
descriptorImGuiRender.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
descriptorImGuiRender.NumDescriptors = buffersCounts;
descriptorImGuiRender.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

// Create Descriptor Heap IMGUI render
if (device->CreateDescriptorHeap(&descriptorImGuiRender, IID_PPV_ARGS(&d3d12DescriptorHeapImGuiRender)) != S_OK)
    return false;

//Create Command Allocator
ID3D12CommandAllocator* allocator;
if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)) != S_OK)
    return false;

for (size_t i = 0; i < buffersCounts; i++) 
    frameContext[i].commandAllocator = allocator;


if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator, NULL, IID_PPV_ARGS(&d3d12CommandList)) != S_OK ||
    d3d12CommandList->Close() != S_OK)
    return false;

//create descriptor heap, describe and create a render target view (RTV) descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC descriptorBackBuffers;
descriptorBackBuffers.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
descriptorBackBuffers.NumDescriptors = buffersCounts;
descriptorBackBuffers.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
descriptorBackBuffers.NodeMask = 1;

if (device->CreateDescriptorHeap(&descriptorBackBuffers, IID_PPV_ARGS(&d3d12DescriptorHeapBackBuffers)) != S_OK)
    return false;
const auto rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

// Create frame resources.
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = d3d12DescriptorHeapBackBuffers->GetCPUDescriptorHandleForHeapStart();

// Create a RTV for each frame.
for (size_t i = 0; i < buffersCounts; i++) 
    ID3D12Resource* pBackBuffer = nullptr;

    frameContext[i].main_render_target_descriptor = rtvHandle;
    pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
    device->CreateRenderTargetView(pBackBuffer, nullptr, rtvHandle);
    frameContext[i].main_render_target_resource = pBackBuffer;
    rtvHandle.ptr += rtvDescriptorSize;


// Setup Platform/Renderer bindings dor IMGUI
ImGui_ImplWin32_Init(window);
ImGui_ImplDX12_Init(device, buffersCounts,
    DXGI_FORMAT_R8G8B8A8_UNORM, d3d12DescriptorHeapImGuiRender,
    d3d12DescriptorHeapImGuiRender->GetCPUDescriptorHandleForHeapStart(),
    d3d12DescriptorHeapImGuiRender->GetGPUDescriptorHandleForHeapStart());
ImGui::GetIO().ImeWindowHandle = window;

// Start the Dear ImGui frame
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();

//call imgui menues here
bool bShow = true;
ShowExampleAppSimpleOverlay(&bShow);

// Rendering (imgui)
FrameContext& currentFrameContext = frameContext[pSwapChain->GetCurrentBackBufferIndex()];
currentFrameContext.commandAllocator->Reset();

D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = currentFrameContext.main_render_target_resource;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;

d3d12CommandList->Reset(currentFrameContext.commandAllocator, nullptr);
d3d12CommandList->ResourceBarrier(1, &barrier);
d3d12CommandList->OMSetRenderTargets(1, &currentFrameContext.main_render_target_descriptor, FALSE, nullptr);

d3d12CommandList->SetDescriptorHeaps(1, &d3d12DescriptorHeapImGuiRender);

ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), d3d12CommandList);

barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;

d3d12CommandList->ResourceBarrier(1, &barrier);
d3d12CommandList->Close();

d3d12CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&d3d12CommandList);

【问题讨论】:

【参考方案1】:

DXGI_ERROR_INVALID_CALL 告诉列表中的一个命令无效,但不是哪个。 您需要在创建命令列表时使用 d3d12 调试层进行运行时检查。 调试层也会告诉你无效的原因。 请参阅msdn 了解更多信息。

您可以使用以下代码激活它,但需要在创建设备之前调用它

ID3D12Debug* debugInterface;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface)))) 
    debugInterface->EnableDebugLayer();

【讨论】:

以上是关于亲爱的 IMGUI 和 DirectX 12 覆盖 (DXGI_ERROR_INVALID_CALL)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GLFW 窗口中限制我的每秒帧数? (使用亲爱的 ImGui)

Simple2D-17(音乐播放器)嵌入 ImGui 库

窗口内的 DirectX 覆盖

从注入的 DLL 挂钩 DirectX EndScene

挂钩/覆盖 DirectX 游戏?

Imgui创建弹出框删除灰显的焦点屏幕