feat: 引入 RenderGraph、Application 框架、ECS 与资产句柄系统
架构分层(自上而下):
App(SandboxApp) → Application(生命周期+事件) → Scene(ECS) / AssetManager(句柄) → Renderer → RenderGraph → Vulkan 模块
- RenderGraph:声明式帧图。虚拟资源+生成号句柄、死 pass 剔除、
拓扑排序、资源生命周期、barrier 编译期预算(Synchronization2)、
物理镜像池按帧槽位隔离(帧间竞争从根上消除)
- 帧同步修复:per-frame-slot 命令缓冲与 UBO ring(原先单份存在
数据竞争);统一 kMaxFramesInFlight / FrameContext
- Application:生命周期钩子(OnInit/OnUpdate/OnRender/OnShutdown)
+ 事件系统(GLFW 回调 → POD 事件 → 类型化钩子);main 只 new 派生类
- ECS:自研稀疏集 Registry(Entity=(index,generation) 防悬空,
O(1) 增删查,Each 取最小池遍历);Scene 延迟销毁队列;
IdComponent{UUID} 稳定外部身份
- Assets:AssetManager + AssetHandle。组件存句柄不存裸指针/Ref
(单一所有者 + 多使用者),Renderer 删除 LoadModel 改为纯消费
- 修复退出析构:成员析构前 GPU waitIdle(资产层先于 Renderer
析构导致 VUID-00922 资源销毁时仍在使用)
- 引入 entt 子模块备用
validation 全程零错误零警告(RTX 5060 实测)
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/22.
|
||||
//
|
||||
// Application:程序生命周期基类。
|
||||
//
|
||||
// 分层目标:main 不再知道 Window/Renderer 的存在——
|
||||
// main 只 new 一个派生类并 Run();
|
||||
// 派生类通过生命周期钩子(OnInit/OnUpdate/OnRender/OnShutdown)
|
||||
// 和事件钩子(OnEvent + 类型化事件)定义程序行为;
|
||||
// runtime、editor 等不同程序形态只是不同的派生类。
|
||||
//
|
||||
// 主循环固定为:PollEvents → OnUpdate(dt) → OnRender → 判退出。
|
||||
// dt 由框架统一计时(含首帧与卡顿钳制),派生层无需自己管理。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_APPLICATION_H
|
||||
#define HELLOVULKAN_APPLICATION_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Core/Event.h>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Window;
|
||||
class Renderer;
|
||||
|
||||
namespace ECS { class Scene; }
|
||||
namespace Assets { class AssetManager; }
|
||||
|
||||
class Application {
|
||||
public:
|
||||
Application(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; }
|
||||
|
||||
// ---- 供派生类访问的模块 ----
|
||||
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; }
|
||||
f32 GetDeltaTime() const { return m_DeltaTime; }
|
||||
u64 GetFrameCount() const { return m_FrameCount; }
|
||||
|
||||
protected:
|
||||
// ---- 生命周期钩子 ----
|
||||
|
||||
// 框架装配完成(Window/Renderer 可用),派生类做初始化(建场景等)
|
||||
virtual void OnInit() {}
|
||||
|
||||
// 每帧逻辑更新(输入、动画、物理……)
|
||||
virtual void OnUpdate(f32 deltaTime) {}
|
||||
|
||||
// 每帧渲染;默认实现驱动 Renderer 主循环,派生类可覆写
|
||||
virtual void OnRender();
|
||||
|
||||
// 主循环结束后、析构前(保存、清理场景等)
|
||||
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;
|
||||
|
||||
bool m_Running = false;
|
||||
f32 m_DeltaTime = 0.0f;
|
||||
u64 m_FrameCount = 0;
|
||||
|
||||
std::chrono::steady_clock::time_point m_LastFrameTime;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_APPLICATION_H
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/22.
|
||||
//
|
||||
// Event:轻量级事件。
|
||||
//
|
||||
// 设计取舍:不用继承体系(每事件一个类 + dynamic_cast 分发),
|
||||
// 用单个 POD 结构 + 类型枚举。事件每帧可能产生成百上千条
|
||||
// (MouseMove 尤其频繁),POD 按值传递/排队最便宜;
|
||||
// 类型安全由字段约定保证,规模上来后再演进为 variant/队列池。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_EVENT_H
|
||||
#define HELLOVULKAN_EVENT_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
enum class EventType : u8 {
|
||||
None = 0,
|
||||
|
||||
// 窗口
|
||||
WindowClose,
|
||||
WindowResize,
|
||||
WindowFocus,
|
||||
WindowLostFocus,
|
||||
|
||||
// 键盘
|
||||
KeyPress,
|
||||
KeyRelease,
|
||||
|
||||
// 鼠标
|
||||
MouseButtonPress,
|
||||
MouseButtonRelease,
|
||||
MouseMove,
|
||||
MouseScroll,
|
||||
};
|
||||
|
||||
struct Event {
|
||||
EventType type = EventType::None;
|
||||
|
||||
// 键盘(GLFW key code)/ 鼠标按钮
|
||||
i32 key = 0;
|
||||
i32 button = 0;
|
||||
|
||||
// 鼠标位置 / 滚轮
|
||||
f32 x = 0.0f;
|
||||
f32 y = 0.0f;
|
||||
f32 deltaX = 0.0f;
|
||||
f32 deltaY = 0.0f;
|
||||
|
||||
// 窗口尺寸
|
||||
i32 width = 0;
|
||||
i32 height = 0;
|
||||
};
|
||||
|
||||
inline const char* ToString(EventType type)
|
||||
{
|
||||
switch (type) {
|
||||
case EventType::WindowClose: return "WindowClose";
|
||||
case EventType::WindowResize: return "WindowResize";
|
||||
case EventType::WindowFocus: return "WindowFocus";
|
||||
case EventType::WindowLostFocus: return "WindowLostFocus";
|
||||
case EventType::KeyPress: return "KeyPress";
|
||||
case EventType::KeyRelease: return "KeyRelease";
|
||||
case EventType::MouseButtonPress: return "MouseButtonPress";
|
||||
case EventType::MouseButtonRelease: return "MouseButtonRelease";
|
||||
case EventType::MouseMove: return "MouseMove";
|
||||
case EventType::MouseScroll: return "MouseScroll";
|
||||
default: return "None";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_EVENT_H
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/22.
|
||||
//
|
||||
// FrameContext:一帧在 GPU 侧的全部上下文。
|
||||
// 引擎中所有 per-frame 资源(命令缓冲、UBO ring、描述符池等)
|
||||
// 都以 frameSlot 为索引,保证 kMaxFramesInFlight 帧并发时互不踩踏。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_FRAMECONTEXT_H
|
||||
#define HELLOVULKAN_FRAMECONTEXT_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
// 引擎统一的最大并发帧数。
|
||||
// 所有 per-frame 资源必须以此为 ring 长度,不允许再出现"单独一个"的帧资源。
|
||||
inline constexpr u32 kMaxFramesInFlight = 2;
|
||||
|
||||
struct FrameContext {
|
||||
u32 frameSlot = 0; // 帧-in-flight 槽位 [0, kMaxFramesInFlight)
|
||||
u32 imageIndex = 0; // 本次 acquire 到的 swapchain image 索引
|
||||
vk::CommandBuffer cmd;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_FRAMECONTEXT_H
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/22.
|
||||
//
|
||||
// UUID:64 位随机唯一标识,实体的稳定外部身份。
|
||||
//
|
||||
// 用途:序列化、跨场景引用、编辑器对象选择——这些场景需要
|
||||
// "实体销毁重建后 ID 不变"或"独立于槽位分配"的稳定标识。
|
||||
// ECS 内部遍历仍用 (index, generation) 句柄(性能),UUID 只做外部身份。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_UUID_H
|
||||
#define HELLOVULKAN_UUID_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
struct UUID {
|
||||
UUID() = default;
|
||||
explicit UUID(u64 value) : m_Value(value) {}
|
||||
|
||||
// 生成全局唯一 ID(非确定性种子,跨进程不重复)
|
||||
static UUID New()
|
||||
{
|
||||
static std::mt19937_64 rng{std::random_device{}()};
|
||||
return UUID{rng()};
|
||||
}
|
||||
|
||||
u64 Value() const { return m_Value; }
|
||||
bool Valid() const { return m_Value != 0; }
|
||||
|
||||
bool operator==(const UUID& o) const = default;
|
||||
bool operator!=(const UUID& o) const = default;
|
||||
|
||||
private:
|
||||
u64 m_Value = 0; // 0 保留为"无效"
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
namespace std {
|
||||
template<> struct hash<Engine::UUID> {
|
||||
size_t operator()(const Engine::UUID& id) const noexcept {
|
||||
return std::hash<std::uint64_t>{}(id.Value());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //HELLOVULKAN_UUID_H
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "Types.h"
|
||||
#include "Event.h"
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
@@ -27,6 +28,11 @@ namespace Engine {
|
||||
bool ShouldClose() const;
|
||||
void PollEvents();
|
||||
|
||||
// 事件出口:GLFW 回调 → Event → 上层回调(由 Application 注册)。
|
||||
// 未注册时事件直接丢弃。
|
||||
using EventCallbackFn = std::function<void(const Event&)>;
|
||||
void SetEventCallback(EventCallbackFn callback) { m_EventCallback = std::move(callback); }
|
||||
|
||||
vk::SurfaceKHR CreateSurface(vk::Instance instance);
|
||||
void DestroySurface(vk::Instance instance, vk::SurfaceKHR surface);
|
||||
|
||||
@@ -57,7 +63,15 @@ namespace Engine {
|
||||
std::string m_Title;
|
||||
|
||||
bool m_FramebufferResized = false;
|
||||
EventCallbackFn m_EventCallback;
|
||||
|
||||
// GLFW 静态回调(通过 window user pointer 找回 this)
|
||||
static void FramebufferResizeCallback(GLFWwindow* window, int width, int height);
|
||||
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
|
||||
static void CursorPosCallback(GLFWwindow* window, double xpos, double ypos);
|
||||
static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
static void WindowFocusCallback(GLFWwindow* window, int focused);
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
Reference in New Issue
Block a user