ImGui
在Win32API+DX12项目中引入ImGui
参考代码:在Win32API+DX12项目中引入ImGui
1. 添加ImGui依赖
我们使用vcpkg进行包管理,在项目根目录下的vcpkg.json文件的"dependencies"字段中添加以下内容:
| {
"name": "imgui",
"features": ["win32-binding","dx12-binding"]
}
|
随后,在项目的cmakeList.txt文件中找到target_link_libraries命令,进行以下修改:
| #在相邻的上方添加该行
find_package(imgui CONFIG REQUIRED)
target_link_libraries(UmbraForward PRIVATE
Microsoft::DirectX-Headers
d3d12.lib
dxgi.lib
d3dcompiler.lib
dxguid.lib
imgui::imgui #在命令内添加该行
)
|
2. 添加SRV描述符堆并绑定到渲染管线
包含文件
在代码文件中添加imgui相关内容时需要包含以下头文件:
| #include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx12.h"
|
由于ImGui需要加载字体纹理,在dx12中需要使用到SRV描述符堆。如果不存在,需要创建对应的描述符堆。
找到创建RTV描述符堆的代码并在下方添加:
| //create description heap for rtv.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
//...创建rtv描述符堆的描述和根据描述创建描述符堆的逻辑...
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
//目前资源数量为1,包含且仅包含ImGui使用的字体
srvHeapDesc.NumDescriptors = 1;
//声明这是一个CBV、SRV或UAV的描述符堆
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
//这个描述符堆对Shader是可见的
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap)));
|
3. 拦截Win32窗口消息
以处理鼠标点击、拖动等事件。
在Win32Application.cpp文件中找到Win32Application::WindowProc方法的实现,进行以下修改:
| //在WindowProc方法的实现上方添加本行,对该方法进行前向声明
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK Win32Application::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//添加拦截逻辑
// 如果 ImGui 需要处理当前输入,则直接返回 true 拦截消息
if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam))
return true;
RenderAppBase* pSample = reinterpret_cast<RenderAppBase*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
//……其它原本逻辑……
}
|
4. ImGui组件生命周期管理
-
初始化
在OnInit()中LoadAssets()方法执行完后,执行以下内容初始化imgui上下文:
| void SampleTriangle::LoadImgui(){
// 检查版本并创建上下文
IMGUI_CHECKVERSION();
ImGui::CreateContext();
// 配置键鼠支持
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // 启用键盘控制[cite: 1]
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // 启用手柄控制[cite: 1]
ImGui::StyleColorsDark(); // 设置暗黑主题风格[cite: 1]
// 初始化 Win32 后端
ImGui_ImplWin32_Init(Win32Application::GetHwnd());
// 配置 DX12 初始化参数结构体
ImGui_ImplDX12_InitInfo init_info = {};
init_info.Device = m_device.Get();
init_info.CommandQueue = m_commandQueue.Get();
init_info.NumFramesInFlight = FrameCount;
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
init_info.SrvDescriptorHeap = m_srvHeap.Get();
// 提供 SRV 描述符分配/释放的回调函数 (ImGui 1.91.6 之后的规范)
init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) {
* out_cpu_handle = info->SrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
* out_gpu_handle = info->SrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
};
init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE) {};
// 初始化 DX12 后端
ImGui_ImplDX12_Init(&init_info);
}
|
-
帧更新(Update)
这些逻辑在OnUpdate()中调用,用于通知ImGui开启新的一帧,创建所需的ui组件。
| void SampleTriangle::UpdateImgui() {
//about imgui component update.
// 开启 Dear ImGui 的新帧
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// 创建一个基础窗口
ImGui::Begin("Umbra Forward Control Panel");
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
// 将上面声明的 UI 逻辑烘焙为底层的绘制数据
ImGui::Render();
}
|
-
帧更新(Render)
Update中仅仅只是更新了绘制的数据,现在还需要将其添加到命令队列进行渲染。
在OnRender完成全部主要绘制后,切换rtv状态为呈现、关闭命令列表前,向命令列表中添加绘制ImGui组件的命令。
| //draw imgui component.
// 绑定供 ImGui 字体使用的 SRV 描述符堆
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
m_commandList->SetDescriptorHeaps(1, descriptorHeaps);
// 渲染 ImGui 收集到的绘制数据
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList.Get());
|
-
销毁(destroy)
在 OnDestroy() 中,确保在退出程序时销毁 ImGui 上下文以防止内存泄漏。
| //destroy imgui
// 确保 GPU 已经完成了所有的工作 (等待 Fence)
WaitForPreviousFrame();
// 卸载后端并销毁 ImGui 上下文
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
|