feat: ImGui 叠加层接入 RenderGraph(docking 分支,dynamic rendering)
框架与 UI 的职责边界: - ImGuiLayer(Engine/ImGui):只做管线桥接——上下文初始化、 NewFrame/BuildDrawData、draw data 录入 cmd;框架自身永不绘制 任何 ImGui 控件,UI 内容全部由派生类在 OnRenderOverlay() 定义 - 每帧顺序:NewFrame → OnRenderOverlay(声明控件)→ BuildDrawData → Renderer.Render(RG: Forward → ImGui(loadOp=Load 叠加) → Present) - ImGui pass 常驻 RG,未启用叠加层时空转,无图结构开销 - 后端共存:VK_EXT_shader_object 引擎无 vk::Pipeline,ImGui 用 UseDynamicRendering + PipelineInfoMain.PipelineRenderingCreateInfo (swapchain 格式),不需要 RenderPass - 析构序:ImGuiLayer 在 Application 成员序中后声明(先于 Renderer 析构),析构内 waitIdle 保证后端资源安全销毁 App 示例(证明职责边界):Scene Debug 面板实时拖动阴影光方向, ` 键反折叠加层
This commit is contained in:
@@ -13,3 +13,7 @@
|
|||||||
[submodule "Lib/entt"]
|
[submodule "Lib/entt"]
|
||||||
path = Lib/entt
|
path = Lib/entt
|
||||||
url = https://github.com/skypjack/entt.git
|
url = https://github.com/skypjack/entt.git
|
||||||
|
[submodule "Lib/imgui"]
|
||||||
|
path = Lib/imgui
|
||||||
|
url = https://github.com/ocornut/imgui.git
|
||||||
|
branch = docking
|
||||||
|
|||||||
+40
-4
@@ -16,6 +16,8 @@
|
|||||||
#include <Engine/ECS/Components.h>
|
#include <Engine/ECS/Components.h>
|
||||||
#include <Engine/Assets/AssetManager.h>
|
#include <Engine/Assets/AssetManager.h>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
@@ -39,6 +41,9 @@ protected:
|
|||||||
{
|
{
|
||||||
Scene& scene = GetScene();
|
Scene& scene = GetScene();
|
||||||
|
|
||||||
|
// ImGui 叠加层(框架只做管线桥接,UI 内容全部在这里定义)
|
||||||
|
EnableImGui();
|
||||||
|
|
||||||
// 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄)
|
// 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄)
|
||||||
auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
|
auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
|
||||||
|
|
||||||
@@ -72,10 +77,10 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 灯光实体(第一个灯 = 阴影光源)
|
// 灯光实体(第一个灯 = 阴影光源)
|
||||||
scene.CreateEntity("KeyLight")
|
m_KeyLight = scene.CreateEntity("KeyLight");
|
||||||
.AddComponent<LightComponent>(
|
m_KeyLight.AddComponent<LightComponent>(
|
||||||
LightComponent{glm::vec3(-0.5f, -0.6f, -0.35f),
|
LightComponent{glm::vec3(-0.5f, -0.6f, -0.35f),
|
||||||
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)});
|
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)});
|
||||||
|
|
||||||
scene.CreateEntity("FillLight")
|
scene.CreateEntity("FillLight")
|
||||||
.AddComponent<LightComponent>(
|
.AddComponent<LightComponent>(
|
||||||
@@ -103,8 +108,37 @@ protected:
|
|||||||
|
|
||||||
// ---- 事件 ----
|
// ---- 事件 ----
|
||||||
|
|
||||||
|
// ---- 叠加层 UI(唯一允许出现 ImGui 控件的地方)----
|
||||||
|
|
||||||
|
void OnRenderOverlay() override
|
||||||
|
{
|
||||||
|
if (!m_OverlayVisible) return;
|
||||||
|
|
||||||
|
ImGui::Begin("Scene Debug");
|
||||||
|
|
||||||
|
// 阴影光源方向:拖动滑条实时看影子变化
|
||||||
|
if (auto* light = m_KeyLight.TryGetComponent<LightComponent>()) {
|
||||||
|
ImGui::Text("Key Light (shadow caster)");
|
||||||
|
ImGui::SliderFloat3("direction",
|
||||||
|
&light->direction.x, -1.0f, 1.0f);
|
||||||
|
if (ImGui::Button("reset")) {
|
||||||
|
light->direction = glm::vec3(-0.5f, -0.6f, -0.35f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("fps: %.1f frame: %llu",
|
||||||
|
1.0f / GetDeltaTime(),
|
||||||
|
static_cast<unsigned long long>(GetFrameCount()));
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
void OnKeyPress(const Event& event) override
|
void OnKeyPress(const Event& event) override
|
||||||
{
|
{
|
||||||
|
if (event.key == GLFW_KEY_GRAVE_ACCENT) {
|
||||||
|
m_OverlayVisible = !m_OverlayVisible; // ` 反折叠加层
|
||||||
|
}
|
||||||
if (event.key == GLFW_KEY_F1) {
|
if (event.key == GLFW_KEY_F1) {
|
||||||
STATS.PrintComparison();
|
STATS.PrintComparison();
|
||||||
}
|
}
|
||||||
@@ -121,7 +155,9 @@ protected:
|
|||||||
private:
|
private:
|
||||||
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
|
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
|
||||||
EntityHandle m_Ground{};
|
EntityHandle m_Ground{};
|
||||||
|
EntityHandle m_KeyLight{};
|
||||||
std::vector<EntityHandle> m_Spinners;
|
std::vector<EntityHandle> m_Spinners;
|
||||||
|
bool m_OverlayVisible = true;
|
||||||
f32 m_Time = 0.0f;
|
f32 m_Time = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -2,13 +2,26 @@ file(GLOB_RECURSE ENGINE_SOURCES
|
|||||||
src/*.cpp
|
src/*.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ImGui(docking 分支,vendored submodule)
|
||||||
|
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/Lib/imgui)
|
||||||
|
set(IMGUI_SOURCES
|
||||||
|
${IMGUI_DIR}/imgui.cpp
|
||||||
|
${IMGUI_DIR}/imgui_draw.cpp
|
||||||
|
${IMGUI_DIR}/imgui_tables.cpp
|
||||||
|
${IMGUI_DIR}/imgui_widgets.cpp
|
||||||
|
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
||||||
|
${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# 编译成静态库
|
# 编译成静态库
|
||||||
add_library(Engine STATIC ${ENGINE_SOURCES})
|
add_library(Engine STATIC ${ENGINE_SOURCES} ${IMGUI_SOURCES})
|
||||||
|
|
||||||
|
|
||||||
target_include_directories(Engine PUBLIC
|
target_include_directories(Engine PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${CMAKE_SOURCE_DIR}/Lib/entt/src
|
${CMAKE_SOURCE_DIR}/Lib/entt/src
|
||||||
|
${IMGUI_DIR}
|
||||||
|
${IMGUI_DIR}/backends
|
||||||
)
|
)
|
||||||
|
|
||||||
# 链接第三方库
|
# 链接第三方库
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
// 和事件钩子(OnEvent + 类型化事件)定义程序行为;
|
// 和事件钩子(OnEvent + 类型化事件)定义程序行为;
|
||||||
// runtime、editor 等不同程序形态只是不同的派生类。
|
// runtime、editor 等不同程序形态只是不同的派生类。
|
||||||
//
|
//
|
||||||
// 主循环固定为:PollEvents → OnUpdate(dt) → [主渲染 → OnRenderOverlay] → 判退出。
|
// 主循环固定为:PollEvents → OnUpdate(dt) → [NewFrame → OnRenderOverlay →
|
||||||
// 主渲染(Renderer.Render(scene))是框架行为,派生类不可覆盖也不会丢失;
|
// BuildDrawData] → 主渲染(Renderer.Render)→ 判退出。
|
||||||
// 派生类的渲染扩展(ImGui/调试视图/编辑器叠加层)走 OnRenderOverlay,
|
// UI 构建与 GPU 提交分离:OnRenderOverlay 只"声明"控件,真正的 draw data
|
||||||
// 在主渲染之后、Present 之前执行,不覆写则什么都不发生。
|
// 提交发生在主渲染管线中的 ImGui pass(Forward 之后、Present 之前,
|
||||||
|
// loadOp=Load 叠加)。主渲染是框架行为,派生类不可覆盖也不会丢失;
|
||||||
|
// 框架自身永远不绘制任何 ImGui 控件——UI 内容全部由派生类定义。
|
||||||
// dt 由框架统一计时(含首帧与卡顿钳制),派生层无需自己管理。
|
// dt 由框架统一计时(含首帧与卡顿钳制),派生层无需自己管理。
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ namespace Engine {
|
|||||||
|
|
||||||
class Window;
|
class Window;
|
||||||
class Renderer;
|
class Renderer;
|
||||||
|
class ImGuiLayer;
|
||||||
|
|
||||||
namespace ECS { class Scene; }
|
namespace ECS { class Scene; }
|
||||||
namespace Assets { class AssetManager; }
|
namespace Assets { class AssetManager; }
|
||||||
@@ -46,6 +49,11 @@ namespace Engine {
|
|||||||
// 请求退出(当帧结束后生效)
|
// 请求退出(当帧结束后生效)
|
||||||
void Close() { m_Running = false; }
|
void Close() { m_Running = false; }
|
||||||
|
|
||||||
|
// 启用 ImGui 叠加层(OnInit 内调用)。启用后每帧:
|
||||||
|
// NewFrame → OnRenderOverlay(派生类画控件)→ BuildDrawData →
|
||||||
|
// 主渲染(ImGui pass 把 draw data 叠加到场景上)。
|
||||||
|
void EnableImGui();
|
||||||
|
|
||||||
// ---- 供派生类访问的模块 ----
|
// ---- 供派生类访问的模块 ----
|
||||||
Window& GetWindow() const { return *m_Window; }
|
Window& GetWindow() const { return *m_Window; }
|
||||||
Renderer& GetRenderer() const { return *m_Renderer; }
|
Renderer& GetRenderer() const { return *m_Renderer; }
|
||||||
@@ -92,6 +100,9 @@ namespace Engine {
|
|||||||
Scope<ECS::Scene> m_Scene;
|
Scope<ECS::Scene> m_Scene;
|
||||||
Scope<Renderer> m_Renderer;
|
Scope<Renderer> m_Renderer;
|
||||||
Scope<Assets::AssetManager> m_Assets;
|
Scope<Assets::AssetManager> m_Assets;
|
||||||
|
// 注意成员序:ImGuiLayer 必须后声明(先于 Renderer/Window 析构,
|
||||||
|
// 其后端销毁需要 Device 与 GLFWwindow 存活)
|
||||||
|
Scope<ImGuiLayer> m_ImGuiLayer;
|
||||||
|
|
||||||
bool m_Running = false;
|
bool m_Running = false;
|
||||||
f32 m_DeltaTime = 0.0f;
|
f32 m_DeltaTime = 0.0f;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/22.
|
||||||
|
//
|
||||||
|
// ImGuiLayer:ImGui 与 Vulkan/RenderGraph 的桥接层。
|
||||||
|
//
|
||||||
|
// 职责边界(重要):
|
||||||
|
// - 本层只做"管线级"工作:上下文初始化、每帧 NewFrame、
|
||||||
|
// draw data 构建、把 draw data 录进给定的 command buffer。
|
||||||
|
// - 本层永远不创建任何 ImGui 窗口/控件(框架不渲染 UI 内容);
|
||||||
|
// 所有 UI 内容由派生类在 OnRenderOverlay() 里自行绘制。
|
||||||
|
// - GPU 提交由 Renderer 的 "ImGui" RG pass 调 RenderDrawData 完成
|
||||||
|
// (Forward 之后、Present 之前,loadOp=Load 叠加在场景上)。
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef HELLOVULKAN_IMGUI_LAYER_H
|
||||||
|
#define HELLOVULKAN_IMGUI_LAYER_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Engine/Core/Types.h>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
namespace Engine {
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
class Renderer;
|
||||||
|
class Device;
|
||||||
|
|
||||||
|
class ImGuiLayer {
|
||||||
|
public:
|
||||||
|
// 需要 Renderer 就绪后创建(依赖 Device + swapchain 格式)
|
||||||
|
ImGuiLayer(Window& window, Renderer& renderer);
|
||||||
|
~ImGuiLayer();
|
||||||
|
|
||||||
|
ImGuiLayer(const ImGuiLayer&) = delete;
|
||||||
|
ImGuiLayer& operator=(const ImGuiLayer&) = delete;
|
||||||
|
|
||||||
|
// ---- 每帧三段式(由 Application::Run 驱动)----
|
||||||
|
|
||||||
|
// 开始 UI 构建(ImGui::NewFrame)。此后到 BuildDrawData 之间,
|
||||||
|
// 派生类可任意调用 ImGui::* 绘制控件。
|
||||||
|
void NewFrame();
|
||||||
|
|
||||||
|
// 结束 UI 构建:把本帧的控件调用编译成 draw data(ImGui::Render)。
|
||||||
|
void BuildDrawData();
|
||||||
|
|
||||||
|
// 把 draw data 录入 cmd(RG ImGui pass 内调用)。
|
||||||
|
// 无 UI 内容时 draw data 为空,是合法 no-op。
|
||||||
|
void RenderDrawData(vk::CommandBuffer cmd);
|
||||||
|
|
||||||
|
bool IsEnabled() const { return m_Enabled; }
|
||||||
|
void SetEnabled(bool enabled) { m_Enabled = enabled; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// 生命周期保证:Application 成员序中 ImGuiLayer 在 Renderer 之后声明
|
||||||
|
//(先析构),因此 Device 引用在整个层生命周期内有效
|
||||||
|
Device& m_Device;
|
||||||
|
vk::DescriptorPool m_Pool;
|
||||||
|
bool m_Enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
|
|
||||||
|
#endif //HELLOVULKAN_IMGUI_LAYER_H
|
||||||
@@ -30,6 +30,7 @@ namespace Engine {
|
|||||||
|
|
||||||
namespace ECS { class Scene; }
|
namespace ECS { class Scene; }
|
||||||
namespace Assets { class AssetManager; }
|
namespace Assets { class AssetManager; }
|
||||||
|
class ImGuiLayer;
|
||||||
|
|
||||||
// Renderer 职责(收敛后):
|
// Renderer 职责(收敛后):
|
||||||
// 1. 装配底层模块(Device/Swapchain/Sync/Command/Descriptor/Shader)
|
// 1. 装配底层模块(Device/Swapchain/Sync/Command/Descriptor/Shader)
|
||||||
@@ -52,6 +53,12 @@ namespace Engine {
|
|||||||
MemoryAllocator& GetAllocator() const { return *m_Allocator; }
|
MemoryAllocator& GetAllocator() const { return *m_Allocator; }
|
||||||
DescriptorManager& GetDescriptorManager() const { return *m_DescriptorManager; }
|
DescriptorManager& GetDescriptorManager() const { return *m_DescriptorManager; }
|
||||||
vk::DescriptorSetLayout GetTextureSetLayout() const { return m_TextureSetLayout; }
|
vk::DescriptorSetLayout GetTextureSetLayout() const { return m_TextureSetLayout; }
|
||||||
|
vk::Format GetSwapchainFormat() const; // Swapchain 不完整,实现在 cpp
|
||||||
|
u32 GetSwapchainImageCount() const;
|
||||||
|
|
||||||
|
// 可选叠加层(ImGui):不设置则 RG 里的 ImGui pass 为空转。
|
||||||
|
// 生命周期:层必须先于 Renderer 析构(Application 成员序保证)。
|
||||||
|
void SetImGuiLayer(ImGuiLayer* layer) { m_ImGuiLayer = layer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
void BuildGraph(); // 声明 pass 结构 + 首次 Compile
|
||||||
@@ -99,6 +106,9 @@ namespace Engine {
|
|||||||
Assets::AssetManager* m_BoundAssets = nullptr;
|
Assets::AssetManager* m_BoundAssets = nullptr;
|
||||||
glm::mat4 m_LightSpace{1.0f}; // 阴影光源 VP(每帧由 Render 算好)
|
glm::mat4 m_LightSpace{1.0f}; // 阴影光源 VP(每帧由 Render 算好)
|
||||||
|
|
||||||
|
// 可选叠加层(不拥有;Application 持有)
|
||||||
|
ImGuiLayer* m_ImGuiLayer = nullptr;
|
||||||
|
|
||||||
vk::Extent2D m_Extent{};
|
vk::Extent2D m_Extent{};
|
||||||
u32 m_CurrentFrame = 0;
|
u32 m_CurrentFrame = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <Engine/Renderer/Renderer.h>
|
#include <Engine/Renderer/Renderer.h>
|
||||||
#include <Engine/Assets/AssetManager.h>
|
#include <Engine/Assets/AssetManager.h>
|
||||||
#include <Engine/Vulkan/Device.h>
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/ImGui/ImGuiLayer.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@@ -50,6 +51,14 @@ namespace Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::EnableImGui()
|
||||||
|
{
|
||||||
|
if (m_ImGuiLayer) return;
|
||||||
|
|
||||||
|
m_ImGuiLayer = CreateScope<ImGuiLayer>(GetWindow(), *m_Renderer);
|
||||||
|
m_Renderer->SetImGuiLayer(m_ImGuiLayer.get());
|
||||||
|
}
|
||||||
|
|
||||||
void Application::Run()
|
void Application::Run()
|
||||||
{
|
{
|
||||||
ENGINE_INFO("Application starting");
|
ENGINE_INFO("Application starting");
|
||||||
@@ -73,10 +82,21 @@ namespace Engine {
|
|||||||
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
|
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
|
||||||
m_Scene->FlushDestroyQueue();
|
m_Scene->FlushDestroyQueue();
|
||||||
|
|
||||||
// 主渲染是框架行为(不可覆盖);叠加层钩子在提交前执行
|
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
||||||
m_Renderer->Render(*m_Scene, *m_Assets);
|
if (m_ImGuiLayer) {
|
||||||
|
m_ImGuiLayer->NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
OnRenderOverlay();
|
OnRenderOverlay();
|
||||||
|
|
||||||
|
if (m_ImGuiLayer) {
|
||||||
|
m_ImGuiLayer->BuildDrawData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 主渲染(框架行为):RG 顺序 Forward → ImGui(loadOp=Load) →
|
||||||
|
// Present,draw data 在 ImGui pass 里才真正提交 GPU ----
|
||||||
|
m_Renderer->Render(*m_Scene, *m_Assets);
|
||||||
|
|
||||||
++m_FrameCount;
|
++m_FrameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// 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();
|
||||||
|
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();
|
||||||
|
|
||||||
|
ImGui_ImplVulkan_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
m_Device.GetDevice().destroyDescriptorPool(m_Pool);
|
||||||
|
|
||||||
|
ENGINE_INFO("ImGuiLayer shut down");
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <Engine/Vulkan/UniformBuffer.h>
|
#include <Engine/Vulkan/UniformBuffer.h>
|
||||||
#include <Engine/Core/Window.h>
|
#include <Engine/Core/Window.h>
|
||||||
#include <Engine/Core/Log.h>
|
#include <Engine/Core/Log.h>
|
||||||
|
#include <Engine/ImGui/ImGuiLayer.h>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
@@ -208,6 +209,16 @@ namespace Engine
|
|||||||
vkDevice.destroyDescriptorSetLayout(m_ShadowSetLayout);
|
vkDevice.destroyDescriptorSetLayout(m_ShadowSetLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vk::Format Renderer::GetSwapchainFormat() const
|
||||||
|
{
|
||||||
|
return m_Swapchain->GetFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Renderer::GetSwapchainImageCount() const
|
||||||
|
{
|
||||||
|
return m_Swapchain->GetImageCount();
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::BuildGraph()
|
void Renderer::BuildGraph()
|
||||||
{
|
{
|
||||||
m_RenderGraph = CreateScope<RG::RenderGraph>(
|
m_RenderGraph = CreateScope<RG::RenderGraph>(
|
||||||
@@ -455,6 +466,35 @@ namespace Engine
|
|||||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||||
vk::ShaderStageFlagBits::eFragment);
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
|
||||||
|
// ---- ImGui pass:叠加层(loadOp=Load,不清屏)----
|
||||||
|
// 框架只负责把 draw data 录进 cmd;UI 内容全部由派生类
|
||||||
|
// 在 OnRenderOverlay() 里构建,框架自身不画任何 ImGui 控件。
|
||||||
|
// 未启用叠加层时空转(begin/end 一次空 rendering region)。
|
||||||
|
m_RenderGraph->AddPass("ImGui",
|
||||||
|
[this](RG::RenderGraphContext& ctx) {
|
||||||
|
const vk::CommandBuffer cmd = ctx.cmd;
|
||||||
|
|
||||||
|
vk::RenderingAttachmentInfo colorAttachment;
|
||||||
|
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
||||||
|
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||||
|
.setLoadOp(vk::AttachmentLoadOp::eLoad) // 关键:叠加
|
||||||
|
.setStoreOp(vk::AttachmentStoreOp::eStore);
|
||||||
|
|
||||||
|
vk::RenderingInfo renderingInfo;
|
||||||
|
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||||
|
.setLayerCount(1)
|
||||||
|
.setColorAttachments(colorAttachment);
|
||||||
|
|
||||||
|
cmd.beginRendering(renderingInfo);
|
||||||
|
|
||||||
|
if (m_ImGuiLayer) {
|
||||||
|
m_ImGuiLayer->RenderDrawData(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.endRendering();
|
||||||
|
})
|
||||||
|
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment);
|
||||||
|
|
||||||
// ---- Present pass:transition 到 ePresentSrcKHR ----
|
// ---- Present pass:transition 到 ePresentSrcKHR ----
|
||||||
m_RenderGraph->AddPass("Present",
|
m_RenderGraph->AddPass("Present",
|
||||||
[](RG::RenderGraphContext&) {
|
[](RG::RenderGraphContext&) {
|
||||||
|
|||||||
Submodule
+1
Submodule Lib/imgui added at 3bae66c735
Reference in New Issue
Block a user