- 根因:~Application 体内 Logger::Shutdown() 先于成员析构执行, Device 销毁期间 Vulkan validation 回调仍打日志 → spdlog 悬空调用 - 修复:app 置于 main 独立作用域,所有成员析构完毕后再 Shutdown - 退出序 = waitIdle(~Application 体内)→ 成员析构(shader 等 Device 子对象销毁)→ Logger::Shutdown - 顺带注释掉窗口全透明(调试期遗留)
140 lines
5.8 KiB
C++
140 lines
5.8 KiB
C++
//
|
||
// Created by atdunbg on 2026/9/22.
|
||
//
|
||
// Application:程序生命周期基类。
|
||
//
|
||
// 分层目标:main 不再知道 Window/Renderer 的存在——
|
||
// main 只 new 一个派生类并 Run();
|
||
// 派生类通过生命周期钩子(OnInit/OnUpdate/OnRenderOverlay/OnShutdown)
|
||
// 和事件钩子(OnEvent + 类型化事件)定义程序行为;
|
||
// runtime、editor 等不同程序形态只是不同的派生类。
|
||
//
|
||
// 主循环固定为:PollEvents → OnUpdate(FrameTime) → [NewFrame → OnRenderOverlay →
|
||
// BuildDrawData] → 主渲染(Renderer.Render)→ 判退出。
|
||
// UI 构建与 GPU 提交分离:OnRenderOverlay 只"声明"控件,真正的 draw data
|
||
// 提交发生在主渲染管线中的 ImGui pass(Forward 之后、Present 之前,
|
||
// loadOp=Load 叠加)。主渲染是框架行为,派生类不可覆盖也不会丢失;
|
||
// 框架自身永远不绘制任何 ImGui 控件——UI 内容全部由派生类定义。
|
||
// 时间由框架单源采集:帧首 Time::BeginFrame 落定 FrameTime(含卡顿钳制),
|
||
// 派生层经 OnUpdate(frame) 或 Time 门面只读,不再自己碰 chrono。
|
||
//
|
||
|
||
#ifndef HELLOVULKAN_APPLICATION_H
|
||
#define HELLOVULKAN_APPLICATION_H
|
||
|
||
#include <Engine/Core/Types.h>
|
||
#include <Engine/Core/Event.h>
|
||
#include <Engine/Core/Time.h>
|
||
#include <chrono>
|
||
#include <string>
|
||
|
||
#include "Engine/ImGui/ImGuiLayer.h"
|
||
|
||
namespace Engine {
|
||
|
||
class Window;
|
||
class Renderer;
|
||
class ImGuiLayer;
|
||
class PhysicsWorld;
|
||
|
||
namespace ECS { class Scene; }
|
||
namespace Assets { class AssetManager; }
|
||
|
||
class Application {
|
||
public:
|
||
Application(const std::string& title, u32 width, u32 height);
|
||
virtual ~Application();
|
||
|
||
Application(const Application&) = delete;
|
||
Application& operator=(const Application&) = delete;
|
||
|
||
// 进入主循环(阻塞直到 Close() 或窗口关闭)
|
||
void Run();
|
||
|
||
// 请求退出(当帧结束后生效)
|
||
void Close() { m_Running = false; }
|
||
|
||
// 启用 ImGui 叠加层(OnInit 内调用)。启用后每帧:
|
||
// NewFrame → OnRenderOverlay(派生类画控件)→ BuildDrawData →
|
||
// 主渲染(ImGui pass 把 draw data 叠加到场景上)。
|
||
void EnableImGui();
|
||
|
||
// ---- 帧率控制 ----
|
||
// vsync 开启时 present 自带节奏,手动限帧不生效;
|
||
// 限帧目标最低 24fps(防止过低刷新导致交互卡死),无上限
|
||
void SetVSync(bool vsync);
|
||
bool GetVSync() const { return m_VSync; }
|
||
void SetTargetFps(f32 fps) { m_TargetFps = std::max(24.0f, fps); }
|
||
f32 GetTargetFps() const { return m_TargetFps; }
|
||
|
||
// ---- 供派生类访问的模块 ----
|
||
Window& GetWindow() const { return *m_Window; }
|
||
Renderer& GetRenderer() const { return *m_Renderer; }
|
||
ECS::Scene& GetScene() const { return *m_Scene; }
|
||
Assets::AssetManager& GetAssets() const { return *m_Assets; }
|
||
PhysicsWorld& GetPhysics() const { return *m_Physics; }
|
||
u64 GetFrameCount() const { return m_FrameCount; }
|
||
|
||
// ImGui相关
|
||
void SetImGuiEnabled(const bool enable) { if (m_ImGuiLayer) m_ImGuiLayer->SetEnabled(enable); }
|
||
// 理论上ImGuiLayer不应该会空,暂时静默处理
|
||
bool GetImGuiEnabled() const { return m_ImGuiLayer ? m_ImGuiLayer->IsEnabled() : false; }
|
||
|
||
protected:
|
||
// ---- 生命周期钩子 ----
|
||
|
||
// 框架装配完成(Window/Renderer 可用),派生类做初始化(建场景等)
|
||
virtual void OnInit() {}
|
||
|
||
// 每帧逻辑更新(输入、动画、物理……)。frame 当帧只读:
|
||
// Dt() = scaled(游戏逻辑),UnscaledDt() = 真实(编辑器相机/UI)
|
||
virtual void OnUpdate(const FrameTime& frame) {}
|
||
|
||
// 主渲染之后、当帧提交之前的叠加层钩子(ImGui/调试视图/编辑器 UI)。
|
||
// 主渲染由框架固定执行;这里只负责"额外的"渲染,不覆写则无叠加层。
|
||
virtual void OnImGuiRender() {}
|
||
|
||
// 主循环结束后、析构前(保存、清理场景等)
|
||
virtual void OnShutdown() {}
|
||
|
||
// ---- 事件钩子 ----
|
||
// OnEvent 收到所有事件(原始入口,适合做全局日志/拦截);
|
||
// 类型化钩子按事件类型分别回调,派生类按需覆写。
|
||
virtual void OnEvent(const Event& event) {}
|
||
virtual void OnWindowClose(const Event& event);
|
||
virtual void OnWindowResize(const Event& event) {}
|
||
virtual void OnWindowFocus(const Event& event) {}
|
||
virtual void OnWindowLostFocus(const Event& event) {}
|
||
virtual void OnKeyPress(const Event& event) {}
|
||
virtual void OnKeyRelease(const Event& event) {}
|
||
virtual void OnMouseButtonPress(const Event& event) {}
|
||
virtual void OnMouseButtonRelease(const Event& event) {}
|
||
virtual void OnMouseMove(const Event& event) {}
|
||
virtual void OnMouseScroll(const Event& event) {}
|
||
|
||
private:
|
||
void DispatchEvent(const Event& event);
|
||
|
||
Scope<Window> m_Window;
|
||
Scope<ECS::Scene> m_Scene;
|
||
Scope<Renderer> m_Renderer;
|
||
Scope<Assets::AssetManager> m_Assets;
|
||
Scope<PhysicsWorld> m_Physics; // Jolt 物理(Run 起循环时创建)
|
||
// 注意成员序:ImGuiLayer 必须后声明(先于 Renderer/Window 析构,
|
||
// 其后端销毁需要 Device 与 GLFWwindow 存活)
|
||
Scope<ImGuiLayer> m_ImGuiLayer;
|
||
|
||
bool m_Running = false;
|
||
u64 m_FrameCount = 0;
|
||
|
||
// 帧率控制
|
||
bool m_VSync = true; // 垂直同步(present 自带节奏)
|
||
f32 m_TargetFps = 60.0f; // 限帧目标(≥24)
|
||
|
||
std::chrono::steady_clock::time_point m_LastFrameTime;
|
||
};
|
||
|
||
} // namespace Engine
|
||
|
||
#endif //HELLOVULKAN_APPLICATION_H
|