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:
@@ -9,6 +9,7 @@
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/ImGui/ImGuiLayer.h>
|
||||
|
||||
#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()
|
||||
{
|
||||
ENGINE_INFO("Application starting");
|
||||
@@ -73,10 +82,21 @@ namespace Engine {
|
||||
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
|
||||
m_Scene->FlushDestroyQueue();
|
||||
|
||||
// 主渲染是框架行为(不可覆盖);叠加层钩子在提交前执行
|
||||
m_Renderer->Render(*m_Scene, *m_Assets);
|
||||
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
||||
if (m_ImGuiLayer) {
|
||||
m_ImGuiLayer->NewFrame();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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/Core/Window.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
#include <Engine/ImGui/ImGuiLayer.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
@@ -208,6 +209,16 @@ namespace Engine
|
||||
vkDevice.destroyDescriptorSetLayout(m_ShadowSetLayout);
|
||||
}
|
||||
|
||||
vk::Format Renderer::GetSwapchainFormat() const
|
||||
{
|
||||
return m_Swapchain->GetFormat();
|
||||
}
|
||||
|
||||
u32 Renderer::GetSwapchainImageCount() const
|
||||
{
|
||||
return m_Swapchain->GetImageCount();
|
||||
}
|
||||
|
||||
void Renderer::BuildGraph()
|
||||
{
|
||||
m_RenderGraph = CreateScope<RG::RenderGraph>(
|
||||
@@ -455,6 +466,35 @@ namespace Engine
|
||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||
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 ----
|
||||
m_RenderGraph->AddPass("Present",
|
||||
[](RG::RenderGraphContext&) {
|
||||
|
||||
Reference in New Issue
Block a user