Try using imgui in Direct3D12

Writing a Direct3D application requires debug drawing. Debug drawing is, for example, drawing the frame rate and various settings on the screen. It is not used in the final product, so we want to make it so that it can be separated and compiled.

I have tried to incorporate this with the help of this website.

https://zenn.dev/norainu/articles/10856bfe120aa2

Put them together in a separate class so that they can be separated.

class ImGuiUtil {
public:
    ImGuiUtil() {

    }
    virtual ~ImGuiUtil() {
    }

    // initialization
    void Initialize(HWND hWnd, ID3D12Device *pDevice)
    {
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGuiIO& io = ImGui::GetIO();
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

        ImGui::StyleColorsLight();

        D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
        srvHeapDesc.NumDescriptors = 1;
        srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        pDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap));

        ImGui_ImplWin32_Init(hWnd);
        ImGui_ImplDX12_Init(pDevice, FRAME_BUFFER_COUNT,
            DXGI_FORMAT_R8G8B8A8_UNORM, m_srvHeap.Get(),
            m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
            m_srvHeap->GetGPUDescriptorHandleForHeapStart());

    }

    // end processing
    void Finalize()
    {
        ImGui_ImplDX12_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();
    }

    // Call at the beginning of the frame
    void NewFrame()
    {
        ImGui_ImplDX12_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
    }

    // Call at the end of the drawing
    void Render(ID3D12GraphicsCommandList *pCommandList)
    {
        ImGui::Render();

        ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
        pCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
        ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCommandList);
    }

private:
    ComPtr<ID3D12DescriptorHeap> m_srvHeap; 
};

Add this and the Imgui window function at the beginning of the Window event handling function.

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    //  ImGui's window processing
    if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam)) {
        return true;
    }

Now, add the following files to your project, compile it, and it will work.

The frame rate was displayed as a test.

// DebugWindow
ImGui::Begin("DebugWindow");
ImGui::Text("fps : %.2f", framerate);
ImGui::End();

Convenient.

I’ll make a note of what I got stuck on when incorporating it.

(1) Error occurred without setting heap for imgui when drawing.

ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
pCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCommandList);

(2) The window size was incorrectly set and was not drawn in dot-by-dot.

The size of the window, including the frame, was not the same as the size of the drawing area, and thus was different from the imgui calculation.

I fixed it by getting the buffer size like this.

    // Calculating the window size from the buffer size
    DWORD windowStyle = WS_OVERLAPPEDWINDOW;
    RECT rc = { 0, 0, INITIAL_WIDTH, INITIAL_HEIGHT };
    AdjustWindowRect(&rc, windowStyle, FALSE);
    HWND hWnd = CreateWindow(wcex.lpszClassName, "Direct3D 12 Sample", windowStyle, 100, 100, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, wcex.hInstance, nullptr);
// Sets the size of the window's drawing area to the size of the buffer
RECT rect;
GetClientRect(hWnd, &rect);
int sw = rect.right - rect.left;
int sh = rect.bottom - rect.top;