DirectX12 ImGUI 라이브러리 세팅

2025. 4. 15. 08:23·공부/DirectX12

 엔진을 개발하다보니 여러 정보를 GUI를 통해 제어해야할 필요가 생겼다. MFC같은 고전적인 방법도 있겠지만 가장 처음 DirectX11을 접했을 때 들었던 강의 커리큘럼을 참고해서 ImGUI를 사용하기로 했다. 코드상으로 보기에 GUI를 구현하는 것이 굉장히 간단해보였는데 한국어로 기술된 자료가 적다는것 빼고는 이렇다할 단점을 찾지 못해 안쓸 이유가 없었다. 심지어 Valve, Nvidia, Ubisoft와 같은 회사들에서 지원을 받아 개발중이라고 하니 더 안심이 되기도 한다.

 

https://github.com/ocornut/imgui

 

GitHub - ocornut/imgui: Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies - ocornut/imgui

github.com

 

 재밌는건 해당 라이브러리의 1.91.6 이후의 버전부터 DirectX12에서 초기화에 대해 그 과정이 많이 복잡해 졌는데 공식 문서에서 말하기를 앞으로도 성능 및 기능 측면에서 더 나은 결과를 얻기 위해서라고 한다. 일단 GUI도 렌더링 대상인지라 SRV 할당을 거쳐 렌더링 파이프라인에 올라가야 하는데, 앞으로의 업데이트를 고려했을 때 할당해야되는 SRV Descriptor가 늘어날 것이기 때문에 바뀌었다는 내용인듯 하다. DirectX11의 경우 복잡한 과정 없이 그냥 Device와 DeviceContext를 파라미터로 넘겨주는 것으로 초기화가 끝난다...

 

 원래는 라이브러리를 받아오면 공식 문서를 참고해서 내 입맛대로 코드 구조를 바꿔버리는데 ImGUI의 경우, 특히 DirectX12 환경에서 초기화 과정에 구조체를 사용해서 초기화를 진행하는 등 잘못 건드렸다가는 첫 걸음도 못 뗄 것만 같은 직감이 들어서 일단 예제의 코드를 완전히 복붙해서 데모창을 띄워봤다. 물론 후에 리팩토링을 거쳐 따로 ImGUI의 초기화, 렌더링, 소멸을 관리하는 클래스를 만들어주기는 했다. 코드는 마지막에 첨부하도록 하겠다.

 

데모창 렌더링

 정상적으로 작동된다. 잘 작동되는 것을 확인했으니 이제 내 입맛대로 GUI 코드를 작성하면 된다. 특이한게 ImGUI의 경우 공식 문서에서 초기 세팅에 대해서만 코드를 제공하고 라이브러리의 사용법 자체는 기술돼있지 않다. 대신 라이브러리 자체에 데모창에 모든 기능을 서술해서 사용자가 이를 직접 보고 기능을 습득할 수 있도록 정리해둔 모양이다. 원래 문서를 보고 코딩을 하던 입장에선 조금 당황스럽긴 하지만 개인적으로는 이런 방식이 확 와닿게 알려준다고 생각하기 때문에 좋다고 생각한다.

 

 ImGUI를 제어하기위한 Manager 클래스다.

EngineGUIManager.h
#pragma once

struct DescriptorHeapAllocator
{
	ID3D12DescriptorHeap* Heap = nullptr;
	D3D12_DESCRIPTOR_HEAP_TYPE  HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
	D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu;
	D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu;
	UINT                        HeapHandleIncrement;
	ImVector<int>               FreeIndices;

	void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap)
	{
		IM_ASSERT(Heap == nullptr && FreeIndices.empty());
		Heap = heap;
		D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
		HeapType = desc.Type;
		HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart();
		HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart();
		HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType);
		FreeIndices.reserve((int)desc.NumDescriptors);
		for (int n = desc.NumDescriptors; n > 0; n--)
			FreeIndices.push_back(n - 1);
	}
	void Destroy()
	{
		Heap = nullptr;
		FreeIndices.clear();
	}
	void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
	{
		IM_ASSERT(FreeIndices.Size > 0);
		int idx = FreeIndices.back();
		FreeIndices.pop_back();
		out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement);
		out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement);
	}
	void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle)
	{
		int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement);
		int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement);
		IM_ASSERT(cpu_idx == gpu_idx);
		FreeIndices.push_back(cpu_idx);
	}
};

class EngineGUIManager
{
	DECLARE_SINGLE(EngineGUIManager)
public:
	~EngineGUIManager();

public:
	void Init();
	void FixedUpdate();
	void Update();
	void Render();

private:
	static DescriptorHeapAllocator _srvHeapDescAllocator;
};
EngineGUIManager.cpp
#include "pch.h"
#include "EngineGUIManager.h"

#pragma region static fields

DescriptorHeapAllocator EngineGUIManager::_srvHeapDescAllocator;

#pragma endregion

EngineGUIManager::~EngineGUIManager()
{
	ImGui_ImplDX12_Shutdown();
	ImGui_ImplWin32_Shutdown();
	ImGui::DestroyContext();
}

void EngineGUIManager::Init()
{
	_srvHeapDescAllocator.Create(GRAPHIC->GetDevice().Get(), RENDER->GetShaderResourceViewHeap().Get());

	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO();
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

	ImGui_ImplWin32_Init(GRAPHIC->GetMainWnd());

	ImGui_ImplDX12_InitInfo init_info = {};
	init_info.Device = GRAPHIC->GetDevice().Get();
	init_info.CommandQueue = GRAPHIC->GetCommandQueue().Get();
	init_info.NumFramesInFlight = GRAPHIC->GetNumFrameResources();
	init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;

	init_info.SrvDescriptorHeap = RENDER->GetShaderResourceViewHeap().Get();
	init_info.SrvDescriptorAllocFn = [](
		ImGui_ImplDX12_InitInfo*,
		D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle,
		D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) {
			return _srvHeapDescAllocator.Alloc(out_cpu_handle, out_gpu_handle);
		};
	init_info.SrvDescriptorFreeFn = [](
		ImGui_ImplDX12_InitInfo*,
		D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle,
		D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) {
			return _srvHeapDescAllocator.Free(cpu_handle, gpu_handle);
		};

	ImGui_ImplDX12_Init(&init_info);
}

void EngineGUIManager::FixedUpdate()
{

}

void EngineGUIManager::Update()
{

}

void EngineGUIManager::Render()
{
	ImGui_ImplDX12_NewFrame();
	ImGui_ImplWin32_NewFrame();
	ImGui::NewFrame();

	{
		ImGui::ShowDemoWindow();
		//ImGui::Begin("Test");
		//ImGui::Text("Test Text");
		//ImGui::End();
	}

	ImGui::Render();
	ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), GRAPHIC->GetCommandList().Get());
}

 

 상단의 코드에서 EngineGUIManager.cpp의 Render() 메소드 내부 ImGui::ShowDemoWindow() 메소드를 타고 들어가면 모든 기능에 대한 코드를 열람할 수 있다.

 

imgui_demo.cpp

 ShowExampleApp~ 이라고 이름 붙여진 함수들을 확인하면 GUI를 만들기 위한 코드 작성법을 열람할 수 있다. 주석으로 설명들이 적혀있긴 하다만 한글은 당연히 없기 때문에 막힐 때 참고하는 정도로 알아두면 될듯 싶다.

'공부 > DirectX12' 카테고리의 다른 글

DirectX12 콜라이더 디버깅을 위한 직선 렌더링 시스템 구현  (1) 2025.07.09
std::max()에 3개 이상의 인자를 사용하는 방법이 안될 때  (0) 2025.05.25
메시와 머터리얼의 관계에 따른 문제 발생 및 해결  (0) 2025.04.10
툰 셰이딩에서 얼굴 명암 처리에 대해서  (0) 2025.04.04
앞으로의 개발 목표 정리  (0) 2025.03.29
'공부/DirectX12' 카테고리의 다른 글
  • DirectX12 콜라이더 디버깅을 위한 직선 렌더링 시스템 구현
  • std::max()에 3개 이상의 인자를 사용하는 방법이 안될 때
  • 메시와 머터리얼의 관계에 따른 문제 발생 및 해결
  • 툰 셰이딩에서 얼굴 명암 처리에 대해서
ayuriK152
ayuriK152
주로 게임 클라이언트 개발 공부를 해요 상용엔진이나 알고리즘 포스팅도 해요
  • ayuriK152
    아유릭공방
    ayuriK152
  • 전체
    오늘
    어제
    • 분류 전체보기 (24)
      • 공부 (19)
        • DirectX12 (11)
        • 유니티 (2)
        • 알고리즘(PS) (5)
      • 게임 (0)
        • 후기 (0)
      • 프로젝트 (5)
        • 리듬게임 프로젝트 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
ayuriK152
DirectX12 ImGUI 라이브러리 세팅
상단으로

티스토리툴바