添加windows构建适配,禁用ImGui多视口功能

This commit is contained in:
2026-10-02 21:53:26 +08:00
parent b0d5ed4550
commit 56bb31638e
4 changed files with 614 additions and 603 deletions
+69 -60
View File
@@ -1,60 +1,69 @@
# ============ 第三方库目标(Engine 的依赖) ============
# ImGui(docking 分支,vendored submodule)
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/Lib/imgui)
add_library(imgui STATIC
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
# ${IMGUI_DIR}/imgui_demo.cpp # ShowDemoWindow 的实现(调试期保留)
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
)
target_include_directories(imgui PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends)
# 两个 backend 头文件分别依赖 GLFW / Vulkan 头
target_link_libraries(imgui PUBLIC glfw Vulkan::Vulkan)
# Tracy(CPU/GPU 帧分析)
add_library(tracy STATIC ${CMAKE_SOURCE_DIR}/Lib/tracy/public/TracyClient.cpp)
target_include_directories(tracy PUBLIC ${CMAKE_SOURCE_DIR}/Lib/tracy/public)
target_compile_definitions(tracy PUBLIC
TRACY_ENABLE
# 按需剖析:GPU context 宣告(GpuNewContext)与连接绑定——GUI 永远晚于
# 程序启动连接,没有这个宏宣告消息被丢弃,GPU 时间线永远空白
TRACY_ON_DEMAND)
find_package(Threads REQUIRED)
target_link_libraries(tracy PUBLIC Threads::Threads)
# ============ Engine ============
file(GLOB_RECURSE ENGINE_SOURCES CONFIGURE_DEPENDS src/*.cpp)
add_library(Engine STATIC ${ENGINE_SOURCES})
# PhysicsDebugDraw 派生 Jolt::DebugRendererSimple:Jolt 以 -fno-rtti 编译
#(有 key function 的基类 typeinfo 符号不存在),该 TU 必须同步关闭 RTTI,
# 否则派生类 typeinfo 引用悬空符号(链接期 undefined reference)
set_source_files_properties(src/Physics/PhysicsDebugDraw.cpp PROPERTIES
COMPILE_OPTIONS "-fno-rtti")
target_include_directories(Engine PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
# header-only 库:纯 include 路径
${CMAKE_SOURCE_DIR}/Lib/entt/src
${CMAKE_SOURCE_DIR}/Lib/VulkanMemoryAllocator/include)
target_link_libraries(Engine PUBLIC
glfw
glm::glm
Vulkan::Vulkan
spdlog::spdlog
stb
assimp::assimp
meshoptimizer
Jolt::Jolt
imgui
tracy)
target_compile_definitions(Engine PRIVATE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
# ============ 第三方库目标(Engine 的依赖) ============
# ImGui(docking 分支,vendored submodule)
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/Lib/imgui)
add_library(imgui STATIC
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
# ${IMGUI_DIR}/imgui_demo.cpp # ShowDemoWindow 的实现(调试期保留)
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
)
target_include_directories(imgui PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends)
# 两个 backend 头文件分别依赖 GLFW / Vulkan 头
target_link_libraries(imgui PUBLIC glfw Vulkan::Vulkan)
# Tracy(CPU/GPU 帧分析)
add_library(tracy STATIC ${CMAKE_SOURCE_DIR}/Lib/tracy/public/TracyClient.cpp)
target_include_directories(tracy PUBLIC ${CMAKE_SOURCE_DIR}/Lib/tracy/public)
target_compile_definitions(tracy PUBLIC
TRACY_ENABLE
# 按需剖析:GPU context 宣告(GpuNewContext)与连接绑定——GUI 永远晚于
# 程序启动连接,没有这个宏宣告消息被丢弃,GPU 时间线永远空白
TRACY_ON_DEMAND)
find_package(Threads REQUIRED)
if(WIN32)
target_link_libraries(tracy PUBLIC
Threads::Threads
ws2_32
dbghelp
secur32)
else()
target_link_libraries(tracy PUBLIC Threads::Threads)
endif()
# ============ Engine ============
file(GLOB_RECURSE ENGINE_SOURCES CONFIGURE_DEPENDS src/*.cpp)
add_library(Engine STATIC ${ENGINE_SOURCES})
# PhysicsDebugDraw 派生 Jolt::DebugRendererSimple:Jolt 以 -fno-rtti 编译
#(有 key function 的基类 typeinfo 符号不存在),该 TU 必须同步关闭 RTTI,
# 否则派生类 typeinfo 引用悬空符号(链接期 undefined reference)
set_source_files_properties(src/Physics/PhysicsDebugDraw.cpp PROPERTIES
COMPILE_OPTIONS "-fno-rtti")
target_include_directories(Engine PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
# header-only 库:纯 include 路径
${CMAKE_SOURCE_DIR}/Lib/entt/src
${CMAKE_SOURCE_DIR}/Lib/VulkanMemoryAllocator/include)
target_link_libraries(Engine PUBLIC
glfw
glm::glm
Vulkan::Vulkan
spdlog::spdlog
stb
assimp::assimp
meshoptimizer
Jolt::Jolt
imgui
tracy)
target_compile_definitions(Engine PRIVATE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
+154 -154
View File
@@ -1,154 +1,154 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/ImGui/ImGuiLayer.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Renderer/Renderer.h>
#include <imgui.h>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_vulkan.h>
namespace Engine {
namespace {
void CheckVkResult(VkResult err)
{
if (err != VK_SUCCESS) {
ENGINE_ERROR("ImGui Vulkan backend error: VkResult={}", static_cast<int>(err));
}
}
}
ImGuiLayer::ImGuiLayer(Window& window, Renderer& renderer)
: m_Device(renderer.GetDevice())
{
Device& device = renderer.GetDevice();
// 1. 描述符池(ImGui 自用:字体贴图 + 用户贴图)
std::array<vk::DescriptorPoolSize, 11> poolSizes = {{
{ vk::DescriptorType::eSampler, 1000 },
{ vk::DescriptorType::eCombinedImageSampler, 1000 },
{ vk::DescriptorType::eSampledImage, 1000 },
{ vk::DescriptorType::eStorageImage, 1000 },
{ vk::DescriptorType::eUniformTexelBuffer, 1000 },
{ vk::DescriptorType::eStorageTexelBuffer, 1000 },
{ vk::DescriptorType::eUniformBuffer, 1000 },
{ vk::DescriptorType::eStorageBuffer, 1000 },
{ vk::DescriptorType::eUniformBufferDynamic, 1000 },
{ vk::DescriptorType::eStorageBufferDynamic, 1000 },
{ vk::DescriptorType::eInputAttachment, 1000 },
}};
vk::DescriptorPoolCreateInfo poolInfo;
poolInfo.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet)
.setMaxSets(1000)
.setPoolSizes(poolSizes);
m_Pool = device.GetDevice().createDescriptorPool(poolInfo);
// 2. ImGui 上下文 + 平台/渲染后端
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark(); // 深色主题(ImGui 默认是灰白 Classic)
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // docking 分支
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // 多视口
ImGui_ImplGlfw_InitForVulkan(window.GetHandle(), true);
ImGui_ImplVulkan_InitInfo info = {};
info.ApiVersion = VK_API_VERSION_1_4;
info.Instance = device.GetInstance();
info.PhysicalDevice = device.GetPhysicalDevice();
info.Device = device.GetDevice();
info.QueueFamily = device.GetGraphicsFamily();
info.Queue = device.GetGraphicsQueue();
info.DescriptorPool = m_Pool;
info.MinImageCount = 2; // kMaxFramesInFlight
info.ImageCount = renderer.GetSwapchainImageCount();
info.CheckVkResultFn = CheckVkResult;
// 与引擎共存的关键:不用 RenderPass,走 dynamic rendering,
// 颜色格式 = swapchain 格式(引擎无 vk::Pipeline,全动态状态)
info.UseDynamicRendering = true;
const vk::Format colorFormat = renderer.GetSwapchainFormat();
VkPipelineRenderingCreateInfoKHR pri{};
pri.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
pri.colorAttachmentCount = 1;
pri.pColorAttachmentFormats =
reinterpret_cast<const VkFormat*>(&colorFormat);
info.PipelineInfoMain.PipelineRenderingCreateInfo = pri;
// 多视口子窗口同格式
info.PipelineInfoForViewports.PipelineRenderingCreateInfo = pri;
ImGui_ImplVulkan_Init(&info);
ENGINE_INFO("ImGuiLayer initialized (docking + viewports, dynamic rendering)");
}
ImGuiLayer::~ImGuiLayer()
{
// 后端销毁自己的管线/字体资源前先等 GPU 空闲(层先于 Renderer 析构)
m_Device.GetDevice().waitIdle();
// 场景纹理描述符先归还(后端 shutdown 前必须完成)
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
UpdateSceneTexture(slot, vk::ImageView{});
}
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
m_Device.GetDevice().destroyDescriptorPool(m_Pool);
ENGINE_INFO("ImGuiLayer shut down");
}
void ImGuiLayer::UpdateSceneTexture(u32 slot, vk::ImageView view)
{
if (slot >= kMaxFramesInFlight) return;
// 旧描述符先归还(池带 eFreeDescriptorSet;resize 重建时 GPU 已
// waitIdle,无在飞引用)
if (m_SceneTextures[slot]) {
ImGui_ImplVulkan_RemoveTexture(
(VkDescriptorSet)(intptr_t)m_SceneTextures[slot]);
m_SceneTextures[slot] = ImTextureID_Invalid;
}
if (view) {
// 后端自有采样器(linear):只需提供 view 与采样期 layout——
// RG 已保证 ImGuiPass 执行期处于 eShaderReadOnlyOptimal
m_SceneTextures[slot] = (ImTextureID)(intptr_t)
ImGui_ImplVulkan_AddTexture(
(VkImageView)view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
}
void ImGuiLayer::NewFrame()
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void ImGuiLayer::BuildDrawData()
{
ImGui::Render();
}
void ImGuiLayer::RenderDrawData(vk::CommandBuffer cmd)
{
if (!m_Enabled) return;
ImDrawData* drawData = ImGui::GetDrawData();
if (drawData && drawData->CmdListsCount > 0) {
ImGui_ImplVulkan_RenderDrawData(drawData, cmd);
}
}
} // namespace Engine
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/ImGui/ImGuiLayer.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Renderer/Renderer.h>
#include <imgui.h>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_vulkan.h>
namespace Engine {
namespace {
void CheckVkResult(VkResult err)
{
if (err != VK_SUCCESS) {
ENGINE_ERROR("ImGui Vulkan backend error: VkResult={}", static_cast<int>(err));
}
}
}
ImGuiLayer::ImGuiLayer(Window& window, Renderer& renderer)
: m_Device(renderer.GetDevice())
{
Device& device = renderer.GetDevice();
// 1. 描述符池(ImGui 自用:字体贴图 + 用户贴图)
std::array<vk::DescriptorPoolSize, 11> poolSizes = {{
{ vk::DescriptorType::eSampler, 1000 },
{ vk::DescriptorType::eCombinedImageSampler, 1000 },
{ vk::DescriptorType::eSampledImage, 1000 },
{ vk::DescriptorType::eStorageImage, 1000 },
{ vk::DescriptorType::eUniformTexelBuffer, 1000 },
{ vk::DescriptorType::eStorageTexelBuffer, 1000 },
{ vk::DescriptorType::eUniformBuffer, 1000 },
{ vk::DescriptorType::eStorageBuffer, 1000 },
{ vk::DescriptorType::eUniformBufferDynamic, 1000 },
{ vk::DescriptorType::eStorageBufferDynamic, 1000 },
{ vk::DescriptorType::eInputAttachment, 1000 },
}};
vk::DescriptorPoolCreateInfo poolInfo;
poolInfo.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet)
.setMaxSets(1000)
.setPoolSizes(poolSizes);
m_Pool = device.GetDevice().createDescriptorPool(poolInfo);
// 2. ImGui 上下文 + 平台/渲染后端
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark(); // 深色主题(ImGui 默认是灰白 Classic)
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // docking 分支
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // 多视口
ImGui_ImplGlfw_InitForVulkan(window.GetHandle(), true);
ImGui_ImplVulkan_InitInfo info = {};
info.ApiVersion = VK_API_VERSION_1_4;
info.Instance = device.GetInstance();
info.PhysicalDevice = device.GetPhysicalDevice();
info.Device = device.GetDevice();
info.QueueFamily = device.GetGraphicsFamily();
info.Queue = device.GetGraphicsQueue();
info.DescriptorPool = m_Pool;
info.MinImageCount = 2; // kMaxFramesInFlight
info.ImageCount = renderer.GetSwapchainImageCount();
info.CheckVkResultFn = CheckVkResult;
// 与引擎共存的关键:不用 RenderPass,走 dynamic rendering,
// 颜色格式 = swapchain 格式(引擎无 vk::Pipeline,全动态状态)
info.UseDynamicRendering = true;
const vk::Format colorFormat = renderer.GetSwapchainFormat();
VkPipelineRenderingCreateInfoKHR pri{};
pri.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
pri.colorAttachmentCount = 1;
pri.pColorAttachmentFormats =
reinterpret_cast<const VkFormat*>(&colorFormat);
info.PipelineInfoMain.PipelineRenderingCreateInfo = pri;
// 多视口子窗口同格式
info.PipelineInfoForViewports.PipelineRenderingCreateInfo = pri;
ImGui_ImplVulkan_Init(&info);
ENGINE_INFO("ImGuiLayer initialized (docking + viewports, dynamic rendering)");
}
ImGuiLayer::~ImGuiLayer()
{
// 后端销毁自己的管线/字体资源前先等 GPU 空闲(层先于 Renderer 析构)
m_Device.GetDevice().waitIdle();
// 场景纹理描述符先归还(后端 shutdown 前必须完成)
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
UpdateSceneTexture(slot, vk::ImageView{});
}
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
m_Device.GetDevice().destroyDescriptorPool(m_Pool);
ENGINE_INFO("ImGuiLayer shut down");
}
void ImGuiLayer::UpdateSceneTexture(u32 slot, vk::ImageView view)
{
if (slot >= kMaxFramesInFlight) return;
// 旧描述符先归还(池带 eFreeDescriptorSet;resize 重建时 GPU 已
// waitIdle,无在飞引用)
if (m_SceneTextures[slot]) {
ImGui_ImplVulkan_RemoveTexture(
(VkDescriptorSet)(intptr_t)m_SceneTextures[slot]);
m_SceneTextures[slot] = ImTextureID_Invalid;
}
if (view) {
// 后端自有采样器(linear):只需提供 view 与采样期 layout——
// RG 已保证 ImGuiPass 执行期处于 eShaderReadOnlyOptimal
m_SceneTextures[slot] = (ImTextureID)(intptr_t)
ImGui_ImplVulkan_AddTexture(
(VkImageView)view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
}
void ImGuiLayer::NewFrame()
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void ImGuiLayer::BuildDrawData()
{
ImGui::Render();
}
void ImGuiLayer::RenderDrawData(vk::CommandBuffer cmd)
{
if (!m_Enabled) return;
ImDrawData* drawData = ImGui::GetDrawData();
if (drawData && drawData->CmdListsCount > 0) {
ImGui_ImplVulkan_RenderDrawData(drawData, cmd);
}
}
} // namespace Engine