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:
2026-09-22 13:46:05 +08:00
parent 7d628f8d8a
commit 8afdd9b245
33 changed files with 3013 additions and 561 deletions
+31
View File
@@ -0,0 +1,31 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/Assets/AssetManager.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Core/Log.h>
namespace Engine::Assets {
AssetManager::AssetManager(Device& device, MemoryAllocator& allocator,
DescriptorManager& descriptors,
vk::DescriptorSetLayout textureLayout)
: m_Device(device), m_Allocator(allocator),
m_Descriptors(descriptors), m_TextureLayout(textureLayout)
{
ENGINE_INFO("AssetManager created");
}
ModelHandle AssetManager::LoadModel(const std::string& path)
{
return m_Models.Load(path, [this](const std::string& p) -> Scope<Model> {
// GPU 上传在此发生:资产层调用渲染层接口完成 buffer/image 创建
return CreateScope<Model>(
m_Device, m_Allocator, m_Descriptors, m_TextureLayout, p);
});
}
} // namespace Engine::Assets
+115
View File
@@ -0,0 +1,115 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/Core/Application.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <Engine/ECS/Scene.h>
#include <Engine/Renderer/Renderer.h>
#include <Engine/Assets/AssetManager.h>
#include <Engine/Vulkan/Device.h>
#include <algorithm>
namespace Engine {
Application::Application(std::string title, u32 width, u32 height)
{
m_Window = CreateScope<Window>(std::move(title), width, height);
// 事件通道:GLFW 回调 → 本类 DispatchEvent → 派生类钩子
m_Window->SetEventCallback(
[this](const Event& e) { DispatchEvent(e); });
// 场景(ECS 世界)+ 渲染器
m_Scene = CreateScope<ECS::Scene>();
m_Renderer = CreateScope<Renderer>(*m_Window);
// 资产层:复用渲染器的 GPU 上下文;
// 声明在 Renderer 之后 → 析构先于 Renderer(它引用其子模块)
m_Assets = CreateScope<Assets::AssetManager>(
m_Renderer->GetDevice(),
m_Renderer->GetAllocator(),
m_Renderer->GetDescriptorManager(),
m_Renderer->GetTextureSetLayout());
}
Application::~Application()
{
// 关键:必须在成员析构之前等 GPU 空闲。
//
// 成员析构序(后声明先析构)是 m_Assets → m_Renderer → m_Scene → m_Window:
// 资产层持有全部 GPU buffer/texture/sampler,会先于 Renderer 析构,
// 而 Renderer 的 waitIdle 在它自己的析构里——那时资产已经销毁了。
// 若最后一帧命令缓冲仍在 GPU 上执行,就会出现
// VUID-vkDestroyBuffer-buffer-00922(资源销毁时仍在使用)。
// 这里显式等待,保证成员按任意顺序析构都是安全的。
if (m_Renderer) {
m_Renderer->GetDevice().GetDevice().waitIdle();
}
}
void Application::Run()
{
ENGINE_INFO("Application starting");
m_Running = true;
m_LastFrameTime = std::chrono::steady_clock::now();
OnInit();
while (m_Running && !m_Window->ShouldClose()) {
m_Window->PollEvents();
auto now = std::chrono::steady_clock::now();
f32 dt = std::chrono::duration<f32>(now - m_LastFrameTime).count();
// 钳制卡顿(断点/窗口拖动导致的超大 dt),避免物理/动画穿模
m_DeltaTime = std::clamp(dt, 0.0f, 0.25f);
m_LastFrameTime = now;
OnUpdate(m_DeltaTime);
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
m_Scene->FlushDestroyQueue();
OnRender();
++m_FrameCount;
}
OnShutdown();
ENGINE_INFO("Application stopped ({} frames)", m_FrameCount);
}
void Application::OnRender()
{
m_Renderer->Render(*m_Scene, *m_Assets);
}
void Application::DispatchEvent(const Event& event)
{
OnEvent(event);
switch (event.type) {
case EventType::WindowClose: OnWindowClose(event); break;
case EventType::WindowResize: OnWindowResize(event); break;
case EventType::WindowFocus: OnWindowFocus(event); break;
case EventType::WindowLostFocus: OnWindowLostFocus(event); break;
case EventType::KeyPress: OnKeyPress(event); break;
case EventType::KeyRelease: OnKeyRelease(event); break;
case EventType::MouseButtonPress: OnMouseButtonPress(event); break;
case EventType::MouseButtonRelease: OnMouseButtonRelease(event); break;
case EventType::MouseMove: OnMouseMove(event); break;
case EventType::MouseScroll: OnMouseScroll(event); break;
default: break;
}
}
void Application::OnWindowClose(const Event& event)
{
// 默认行为:直接退出。需要"退出确认"的程序覆写本钩子。
Close();
}
} // namespace Engine
+72
View File
@@ -14,6 +14,73 @@ namespace Engine
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
self->m_FramebufferResized = true;
if (self->m_EventCallback) {
Event e;
e.type = EventType::WindowResize;
e.width = width;
e.height = height;
self->m_EventCallback(e);
}
}
void Window::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (!self->m_EventCallback || key == GLFW_KEY_UNKNOWN) return;
Event e;
e.key = key;
e.type = (action == GLFW_PRESS) ? EventType::KeyPress
: (action == GLFW_RELEASE) ? EventType::KeyRelease
: EventType::None; // GLFW_REPEAT 暂不透出
if (e.type != EventType::None) self->m_EventCallback(e);
}
void Window::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (!self->m_EventCallback) return;
Event e;
e.button = button;
e.type = (action == GLFW_PRESS) ? EventType::MouseButtonPress
: EventType::MouseButtonRelease;
self->m_EventCallback(e);
}
void Window::CursorPosCallback(GLFWwindow* window, double xpos, double ypos)
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (!self->m_EventCallback) return;
Event e;
e.type = EventType::MouseMove;
e.x = static_cast<f32>(xpos);
e.y = static_cast<f32>(ypos);
self->m_EventCallback(e);
}
void Window::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (!self->m_EventCallback) return;
Event e;
e.type = EventType::MouseScroll;
e.deltaX = static_cast<f32>(xoffset);
e.deltaY = static_cast<f32>(yoffset);
self->m_EventCallback(e);
}
void Window::WindowFocusCallback(GLFWwindow* window, int focused)
{
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (!self->m_EventCallback) return;
Event e;
e.type = focused ? EventType::WindowFocus : EventType::WindowLostFocus;
self->m_EventCallback(e);
}
Window::Window(const std::string& title, u32 width, u32 height)
@@ -42,6 +109,11 @@ namespace Engine
glfwSetWindowUserPointer(m_Window, this);
glfwSetFramebufferSizeCallback(m_Window, FramebufferResizeCallback);
glfwSetKeyCallback(m_Window, KeyCallback);
glfwSetMouseButtonCallback(m_Window, MouseButtonCallback);
glfwSetCursorPosCallback(m_Window, CursorPosCallback);
glfwSetScrollCallback(m_Window, ScrollCallback);
glfwSetWindowFocusCallback(m_Window, WindowFocusCallback);
}
Window::~Window()
+62
View File
@@ -0,0 +1,62 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/ECS/Scene.h>
#include <Engine/Core/Log.h>
namespace Engine::ECS {
Entity Scene::CreateEntity(std::string name)
{
Entity e = m_Registry.Create();
// IdComponent 自动挂载:每个实体出生即有稳定 UUID
m_Registry.Add<IdComponent>(e, IdComponent{UUID::New()});
if (!name.empty()) {
m_Registry.Add<NameComponent>(e, NameComponent{std::move(name)});
}
return e;
}
Entity Scene::FindEntityByUUID(UUID uuid)
{
if (!uuid.Valid()) return {};
Entity found{};
m_Registry.Each<IdComponent>([&](Entity e, IdComponent& id) {
if (!found && id.uuid == uuid) found = e;
});
return found;
}
void Scene::DestroyEntity(Entity e)
{
if (!m_Registry.IsAlive(e)) {
ENGINE_FATAL("Scene::DestroyEntity: stale entity ({},{})",
e.index, e.generation);
}
m_PendingDestroy.push_back(e);
}
void Scene::FlushDestroyQueue()
{
if (m_PendingDestroy.empty()) return;
for (Entity e : m_PendingDestroy) {
if (m_Registry.IsAlive(e)) {
m_Registry.Destroy(e);
}
}
m_PendingDestroy.clear();
}
CameraComponent* Scene::GetPrimaryCamera()
{
CameraComponent* found = nullptr;
m_Registry.Each<CameraComponent>([&](Entity, CameraComponent& cc) {
if (!found && cc.primary) found = &cc;
});
return found;
}
} // namespace Engine::ECS
+781
View File
@@ -0,0 +1,781 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/RenderGraph/RenderGraph.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Core/Log.h>
#include <algorithm>
#include <set>
namespace Engine::RG {
namespace {
bool IsWriteUse(ResourceUse use)
{
switch (use) {
case ResourceUse::ColorAttachment:
case ResourceUse::DepthAttachment:
case ResourceUse::TransferDst:
case ResourceUse::Present:
return true;
case ResourceUse::ShaderRead:
case ResourceUse::TransferSrc:
return false;
}
return false;
}
} // namespace
// ---------- 构造 / 析构 ----------
RenderGraph::RenderGraph(Device& device, MemoryAllocator& allocator, u32 framesInFlight)
: m_Device(device), m_Allocator(allocator), m_FramesInFlight(framesInFlight)
{
}
RenderGraph::~RenderGraph()
{
ReleasePhysical();
const auto& vkDevice = m_Device.GetDevice();
for (auto& [key, images] : m_Pool) {
for (auto& img : images) {
if (!img->valid) continue;
vkDevice.destroyImageView(img->view);
vkDevice.destroyImage(img->image);
m_Allocator.Free(img->allocation);
}
}
m_Pool.clear();
}
// ---------- 资源声明 ----------
TextureHandle RenderGraph::CreateTexture(std::string name, const TextureDesc& desc)
{
CheckMutable();
TextureHandle h{static_cast<u32>(m_Textures.size()), 1};
TextureEntry entry;
entry.name = std::move(name);
entry.index = h.index;
entry.desc = desc;
entry.imported = false;
entry.generation = h.generation;
entry.physical.resize(m_FramesInFlight, nullptr);
entry.slots.resize(m_FramesInFlight);
m_Textures.push_back(std::move(entry));
return h;
}
TextureHandle RenderGraph::ImportSwapchain(std::string name, vk::Format format)
{
CheckMutable();
TextureHandle h{static_cast<u32>(m_Textures.size()), 1};
TextureEntry entry;
entry.name = std::move(name);
entry.index = h.index;
entry.desc.format = format;
entry.imported = true;
entry.generation = h.generation;
entry.physical.resize(m_FramesInFlight, nullptr);
entry.slots.resize(m_FramesInFlight);
m_Textures.push_back(std::move(entry));
return h;
}
void RenderGraph::SetImportedImage(TextureHandle h, vk::Image image, vk::ImageView view)
{
auto& entry = GetEntry(h);
if (!entry.imported) {
ENGINE_FATAL("RenderGraph: SetImportedImage on non-imported resource '{}'", entry.name);
}
entry.importedImage = image;
entry.importedView = view;
}
// ---------- Pass 声明 ----------
RenderGraph::PassBuilder RenderGraph::AddPass(std::string name, ExecuteFn exec)
{
CheckMutable();
PassNode node;
node.name = std::move(name);
node.exec = std::move(exec);
m_Passes.push_back(std::move(node));
return PassBuilder(*this, static_cast<u32>(m_Passes.size() - 1));
}
RenderGraph::PassBuilder& RenderGraph::PassBuilder::Write(TextureHandle h, ResourceUse use)
{
m_Graph.CheckMutable();
m_Graph.m_Passes[m_PassIndex].uses.push_back({h.index, use, {}});
return *this;
}
RenderGraph::PassBuilder& RenderGraph::PassBuilder::Read(TextureHandle h, ResourceUse use,
vk::ShaderStageFlags stage)
{
m_Graph.CheckMutable();
m_Graph.m_Passes[m_PassIndex].uses.push_back({h.index, use, stage});
return *this;
}
RenderGraph::PassBuilder& RenderGraph::PassBuilder::SideEffect()
{
m_Graph.CheckMutable();
m_Graph.m_Passes[m_PassIndex].sideEffect = true;
return *this;
}
// ---------- 编译 ----------
void RenderGraph::Compile()
{
if (m_TargetExtent.width == 0 || m_TargetExtent.height == 0) {
ENGINE_FATAL("RenderGraph: Compile before SetTargetExtent");
}
ReleasePhysical();
// 清理上一次编译的中间结果
for (auto& entry : m_Textures) {
entry.usage = {};
entry.firstPass = -1;
entry.lastPass = -1;
entry.chainLayout = vk::ImageLayout::eUndefined;
entry.chainStage = {};
entry.chainAccess = {};
entry.chainWasWrite = false;
}
for (auto& pass : m_Passes) {
pass.culled = false;
pass.barriers.clear();
}
for (auto& entry : m_Textures) ResolveUsage(entry);
CullPasses();
Toposort();
ComputeLifetimes();
PrecomputeBarriers();
AllocatePhysical();
m_Compiled = true;
m_ActivePassCount = static_cast<u32>(
std::count_if(m_Passes.begin(), m_Passes.end(),
[](const PassNode& p) { return !p.culled; }));
ENGINE_INFO("RenderGraph compiled: {} passes ({} active), {} textures, {} barriers, {} gpu images",
m_Passes.size(), m_ActivePassCount, m_Textures.size(),
GetBarrierCount(), GetPooledImageCount());
}
void RenderGraph::SetTargetExtent(vk::Extent2D extent)
{
if (extent == m_TargetExtent) return;
// 尺寸变化:调用方必须保证 GPU 空闲(通常在 swapchain recreate 的 waitIdle 之后)
ReleasePhysical();
PurgePool(extent);
// 物理镜像全部换新,槽位状态清零
for (auto& entry : m_Textures) {
for (auto& slot : entry.slots) slot.used = false;
}
m_TargetExtent = extent;
Compile();
}
void RenderGraph::ResolveUsage(TextureEntry& entry)
{
if (entry.imported) return;
vk::ImageUsageFlags usage{};
for (const auto& pass : m_Passes) {
for (const auto& use : pass.uses) {
if (use.resIndex != entry.index) continue;
switch (use.use) {
case ResourceUse::ColorAttachment:
case ResourceUse::Present:
usage |= vk::ImageUsageFlagBits::eColorAttachment;
break;
case ResourceUse::DepthAttachment:
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
entry.aspect = vk::ImageAspectFlagBits::eDepth;
break;
case ResourceUse::ShaderRead:
usage |= vk::ImageUsageFlagBits::eSampled;
break;
case ResourceUse::TransferSrc:
usage |= vk::ImageUsageFlagBits::eTransferSrc;
break;
case ResourceUse::TransferDst:
usage |= vk::ImageUsageFlagBits::eTransferDst;
break;
}
}
}
entry.usage = usage;
}
void RenderGraph::CullPasses()
{
// 引用计数剔除:一个 pass 存活当且仅当
// a) 声明了 sideEffect,或
// b) 它读写某个导入资源(对外可见),或
// c) 它写出的某个资源被某个存活 pass 读取。
// 迭代到不动点(最多 pass 数轮)。
std::vector<bool> kept(m_Passes.size(), false);
std::vector<bool> readByKept(m_Textures.size(), false);
bool changed = true;
while (changed) {
changed = false;
for (u32 i = 0; i < m_Passes.size(); ++i) {
if (kept[i]) continue;
const auto& pass = m_Passes[i];
bool keep = pass.sideEffect;
for (const auto& use : pass.uses) {
if (IsWriteUse(use.use) && readByKept[use.resIndex]) {
keep = true;
} else if (m_Textures[use.resIndex].imported) {
keep = true;
}
}
if (!keep) continue;
kept[i] = true;
changed = true;
for (const auto& use : pass.uses) {
if (!IsWriteUse(use.use)) readByKept[use.resIndex] = true;
}
}
}
for (u32 i = 0; i < m_Passes.size(); ++i) {
m_Passes[i].culled = !kept[i];
}
}
void RenderGraph::Toposort()
{
// writer → reader 建边(去重),Kahn 拓扑排序。
// 声明顺序通常已是拓扑序,但引擎级实现必须显式校验,
// 避免上层重构 pass 顺序时引入环。
std::vector<std::vector<u32>> writers(m_Textures.size());
for (u32 i = 0; i < m_Passes.size(); ++i) {
if (m_Passes[i].culled) continue;
for (const auto& use : m_Passes[i].uses) {
if (IsWriteUse(use.use)) {
writers[use.resIndex].push_back(i);
}
}
}
std::vector<std::set<u32>> adj(m_Passes.size());
std::vector<u32> indegree(m_Passes.size(), 0);
for (u32 q = 0; q < m_Passes.size(); ++q) {
if (m_Passes[q].culled) continue;
std::set<u32> seenWriters;
for (const auto& use : m_Passes[q].uses) {
if (IsWriteUse(use.use)) continue;
for (u32 p : writers[use.resIndex]) {
if (p == q || m_Passes[p].culled) continue;
if (seenWriters.insert(p).second) {
adj[p].insert(q);
++indegree[q];
}
}
}
}
m_ExecutionOrder.clear();
m_ExecutionOrder.reserve(m_Passes.size());
// 同层按声明序出队,保证确定性
std::set<u32> ready;
for (u32 i = 0; i < m_Passes.size(); ++i) {
if (!m_Passes[i].culled && indegree[i] == 0) ready.insert(i);
}
while (!ready.empty()) {
u32 p = *ready.begin();
ready.erase(ready.begin());
m_ExecutionOrder.push_back(p);
for (u32 q : adj[p]) {
if (--indegree[q] == 0) ready.insert(q);
}
}
u32 activeCount = 0;
for (const auto& pass : m_Passes) {
if (!pass.culled) ++activeCount;
}
if (m_ExecutionOrder.size() != activeCount) {
ENGINE_FATAL("RenderGraph: cycle detected in pass declarations");
}
}
void RenderGraph::ComputeLifetimes()
{
for (u32 pos = 0; pos < m_ExecutionOrder.size(); ++pos) {
const u32 passIdx = m_ExecutionOrder[pos];
for (const auto& use : m_Passes[passIdx].uses) {
auto& entry = m_Textures[use.resIndex];
if (entry.firstPass < 0) entry.firstPass = static_cast<i32>(pos);
entry.lastPass = static_cast<i32>(pos);
}
}
}
void RenderGraph::PrecomputeBarriers()
{
// 两阶段:
// Pass A:按执行序推演每个资源的状态链,得到稳态(resting)。
// Pass B:真正生成 barrier。
//
// entry barrier(资源在该 pass 首次被使用)有两个变体:
// first — 该物理实例的槽位尚未用过:old = Undefined(无历史依赖)
// steady — 槽位已用过:old = resting(上一轮执行结束时的状态)。
// 同一槽位两次进入图之间有 CPU fence 全等保,状态链闭合。
// imported 资源(swapchain)acquire 后 layout 语义上总是 Undefined,恒用 first。
//
// 非 entry:layout 变化或存在写读/读写/写写依赖时插入(read-read 无害跳过)。
// ---- Pass A:状态链推演 ----
for (const u32 passIdx : m_ExecutionOrder) {
for (const auto& use : m_Passes[passIdx].uses) {
auto& entry = m_Textures[use.resIndex];
entry.chainLayout = UseLayout(use.use);
entry.chainStage = UseDstStage(use);
entry.chainAccess = UseDstAccess(use);
entry.chainWasWrite = IsWriteUse(use.use);
}
}
for (auto& entry : m_Textures) {
if (entry.imported || entry.firstPass < 0) continue;
entry.restingLayout = entry.chainLayout;
entry.restingStage = entry.chainStage;
entry.restingAccess = entry.chainAccess;
}
// ---- Pass B:barrier 生成 ----
for (auto& entry : m_Textures) {
entry.chainLayout = vk::ImageLayout::eUndefined;
entry.chainStage = {};
entry.chainAccess = {};
entry.chainWasWrite = false;
}
for (u32 pos = 0; pos < m_ExecutionOrder.size(); ++pos) {
auto& pass = m_Passes[m_ExecutionOrder[pos]];
for (const auto& use : pass.uses) {
auto& entry = m_Textures[use.resIndex];
const vk::ImageLayout desiredLayout = UseLayout(use.use);
const vk::PipelineStageFlags2 dstStage = UseDstStage(use);
const vk::AccessFlags2 dstAccess = UseDstAccess(use);
const bool curIsWrite = IsWriteUse(use.use);
auto makeBarrier = [&](vk::ImageLayout oldLayout,
vk::PipelineStageFlags2 srcStage,
vk::AccessFlags2 srcAccess) {
vk::ImageMemoryBarrier2 b;
b.setSrcStageMask(srcStage)
.setSrcAccessMask(srcAccess)
.setDstStageMask(dstStage)
.setDstAccessMask(dstAccess)
.setOldLayout(oldLayout)
.setNewLayout(desiredLayout)
// image 句柄由 AllocatePhysical/每帧导入回填
.setSubresourceRange({entry.aspect, 0, 1, 0, 1});
return b;
};
if (entry.firstPass == static_cast<i32>(pos)) {
// entry barrier:两个变体
vk::ImageMemoryBarrier2 first = makeBarrier(
vk::ImageLayout::eUndefined,
vk::PipelineStageFlagBits2::eNone,
vk::AccessFlagBits2::eNone);
vk::ImageMemoryBarrier2 steady;
if (entry.imported) {
// swapchain:每帧 acquire 后都是 Undefined,两变体相同
steady = first;
} else {
steady = makeBarrier(entry.restingLayout,
entry.restingStage,
entry.restingAccess);
}
pass.barriers.push_back({use.resIndex, steady, first});
entry.chainLayout = desiredLayout;
entry.chainStage = dstStage;
entry.chainAccess = dstAccess;
entry.chainWasWrite = curIsWrite;
continue;
}
// 非 entry:layout 变化,或存在写读/读写/写写依赖时插入
bool need = entry.chainLayout != desiredLayout ||
entry.chainWasWrite || curIsWrite;
if (!need) continue;
vk::ImageMemoryBarrier2 b = makeBarrier(
entry.chainLayout, entry.chainStage, entry.chainAccess);
pass.barriers.push_back({use.resIndex, b, b});
entry.chainLayout = desiredLayout;
entry.chainStage = dstStage;
entry.chainAccess = dstAccess;
entry.chainWasWrite = curIsWrite;
}
}
}
void RenderGraph::AllocatePhysical()
{
for (auto& entry : m_Textures) {
if (entry.imported || entry.firstPass < 0) continue;
vk::Extent2D extent{
entry.desc.width ? entry.desc.width : m_TargetExtent.width,
entry.desc.height ? entry.desc.height : m_TargetExtent.height
};
for (u32 slot = 0; slot < m_FramesInFlight; ++slot) {
entry.physical[slot] = AcquirePhysical(
entry.desc.format, extent, entry.usage, entry.aspect);
}
// barrier 的 image 句柄在 Execute 期按帧槽位绑定(见 Execute)
}
}
void RenderGraph::ReleasePhysical()
{
for (auto& entry : m_Textures) {
if (entry.imported) continue;
for (u32 slot = 0; slot < entry.physical.size(); ++slot) {
auto* img = entry.physical[slot];
if (!img) continue;
PoolKey key{
static_cast<u32>(img->format),
img->extent.width, img->extent.height,
static_cast<u32>(img->usage)
};
m_Pool[key].push_back(Scope<PhysicalImage>(img));
entry.physical[slot] = nullptr;
}
}
}
void RenderGraph::PurgePool(vk::Extent2D target)
{
const auto& vkDevice = m_Device.GetDevice();
for (auto it = m_Pool.begin(); it != m_Pool.end();) {
if (it->first.width != target.width || it->first.height != target.height) {
for (auto& img : it->second) {
if (!img->valid) continue;
vkDevice.destroyImageView(img->view);
vkDevice.destroyImage(img->image);
m_Allocator.Free(img->allocation);
}
it = m_Pool.erase(it);
} else {
++it;
}
}
}
PhysicalImage* RenderGraph::AcquirePhysical(vk::Format format, vk::Extent2D extent,
vk::ImageUsageFlags usage,
vk::ImageAspectFlagBits aspect)
{
PoolKey key{static_cast<u32>(format), extent.width, extent.height,
static_cast<u32>(usage)};
// 池中取空闲镜像
auto it = m_Pool.find(key);
if (it != m_Pool.end() && !it->second.empty()) {
PhysicalImage* img = it->second.back().release();
it->second.pop_back();
return img;
}
// 新建:所有权归 TextureEntry.physical,Release 时归还池
auto img = CreateScope<PhysicalImage>();
img->extent = extent;
img->format = format;
img->usage = usage;
img->valid = true;
const auto& vkDevice = m_Device.GetDevice();
vk::ImageCreateInfo imageInfo;
imageInfo.setImageType(vk::ImageType::e2D)
.setFormat(format)
.setExtent({extent.width, extent.height, 1})
.setMipLevels(1)
.setArrayLayers(1)
.setSamples(vk::SampleCountFlagBits::e1)
.setTiling(vk::ImageTiling::eOptimal)
.setUsage(usage)
.setSharingMode(vk::SharingMode::eExclusive)
.setInitialLayout(vk::ImageLayout::eUndefined);
img->image = vkDevice.createImage(imageInfo);
auto reqs = vkDevice.getImageMemoryRequirements(img->image);
img->allocation = m_Allocator.Allocate(reqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
vkDevice.bindImageMemory(img->image, img->allocation.Memory, img->allocation.Offset);
vk::ImageViewCreateInfo viewInfo;
viewInfo.setImage(img->image)
.setViewType(vk::ImageViewType::e2D)
.setFormat(format)
.setSubresourceRange({aspect, 0, 1, 0, 1});
img->view = vkDevice.createImageView(viewInfo);
return img.release();
}
// ---------- 执行 ----------
void RenderGraph::Execute(const FrameContext& frame)
{
for (const u32 passIdx : m_ExecutionOrder) {
auto& pass = m_Passes[passIdx];
if (pass.culled) continue;
// barrier:按槽位状态挑选变体,批量提交
m_ScratchBarriers.clear();
for (const auto& bp : pass.barriers) {
auto& entry = m_Textures[bp.resIndex];
bool useFirst;
if (entry.imported) {
useFirst = true; // 恒为 Undefined 起点
} else {
auto& slot = entry.slots[frame.frameSlot];
useFirst = !slot.used;
slot.used = true;
}
vk::ImageMemoryBarrier2 b = useFirst ? bp.first : bp.steady;
// image 句柄在执行期绑定:imported 用当帧 acquire 的 image,
// created 用当前帧槽位的物理镜像(每个槽位各有一份)
b.setImage(entry.imported
? entry.importedImage
: entry.physical[frame.frameSlot]->image);
m_ScratchBarriers.push_back(b);
}
if (!m_ScratchBarriers.empty()) {
vk::DependencyInfo dep;
dep.setImageMemoryBarriers(m_ScratchBarriers);
frame.cmd.pipelineBarrier2(dep);
}
if (pass.exec) {
RenderGraphContext ctx;
ctx.cmd = frame.cmd;
ctx.frame = frame;
ctx.extent = m_TargetExtent;
ctx.graph = this;
pass.exec(ctx);
}
}
}
// ---------- RenderGraphContext ----------
vk::ImageView RenderGraphContext::View(TextureHandle h) const
{
const auto& entry = graph->GetEntry(h);
if (entry.imported) return entry.importedView;
return entry.physical[frame.frameSlot]->view;
}
vk::Image RenderGraphContext::Image(TextureHandle h) const
{
const auto& entry = graph->GetEntry(h);
if (entry.imported) return entry.importedImage;
return entry.physical[frame.frameSlot]->image;
}
// ---------- 工具 / 统计 ----------
TextureEntry& RenderGraph::GetEntry(TextureHandle h)
{
CheckHandle(h);
return m_Textures[h.index];
}
const TextureEntry& RenderGraph::GetEntry(TextureHandle h) const
{
CheckHandle(h);
return m_Textures[h.index];
}
void RenderGraph::CheckHandle(TextureHandle h) const
{
if (!h.Valid() || h.index >= m_Textures.size() ||
m_Textures[h.index].generation != h.generation) {
ENGINE_FATAL("RenderGraph: invalid texture handle (index={}, gen={})",
h.index, h.generation);
}
}
void RenderGraph::CheckMutable() const
{
// 图结构(资源/pass/use)只在声明期可变;Compile 后 barrier 已预算,
// 静默接受修改会产生错误同步,直接拒绝。
if (m_Compiled) {
ENGINE_FATAL("RenderGraph: graph is compiled; structural changes are not allowed");
}
}
vk::ImageLayout RenderGraph::UseLayout(ResourceUse use)
{
switch (use) {
case ResourceUse::ColorAttachment: return vk::ImageLayout::eColorAttachmentOptimal;
case ResourceUse::DepthAttachment: return vk::ImageLayout::eDepthAttachmentOptimal;
case ResourceUse::ShaderRead: return vk::ImageLayout::eShaderReadOnlyOptimal;
case ResourceUse::TransferSrc: return vk::ImageLayout::eTransferSrcOptimal;
case ResourceUse::TransferDst: return vk::ImageLayout::eTransferDstOptimal;
case ResourceUse::Present: return vk::ImageLayout::ePresentSrcKHR;
}
return vk::ImageLayout::eUndefined;
}
vk::PipelineStageFlags2 RenderGraph::UseDstStage(const PassNode::Use& use)
{
switch (use.use) {
case ResourceUse::ColorAttachment:
return vk::PipelineStageFlagBits2::eColorAttachmentOutput;
case ResourceUse::DepthAttachment:
return vk::PipelineStageFlagBits2::eEarlyFragmentTests |
vk::PipelineStageFlagBits2::eLateFragmentTests;
case ResourceUse::ShaderRead:
// ShaderStageFlags 与 PipelineStageFlags2 位值兼容,需显式转换
return vk::PipelineStageFlags2(
static_cast<VkPipelineStageFlags2>(
static_cast<VkShaderStageFlags>(use.readStage)));
case ResourceUse::TransferSrc:
case ResourceUse::TransferDst:
return vk::PipelineStageFlagBits2::eTransfer;
case ResourceUse::Present:
return vk::PipelineStageFlagBits2::eNone;
}
return vk::PipelineStageFlagBits2::eNone;
}
vk::PipelineStageFlags2 RenderGraph::UseSrcStage(ResourceUse use)
{
switch (use) {
case ResourceUse::ColorAttachment:
case ResourceUse::Present:
return vk::PipelineStageFlagBits2::eColorAttachmentOutput;
case ResourceUse::DepthAttachment:
return vk::PipelineStageFlagBits2::eEarlyFragmentTests |
vk::PipelineStageFlagBits2::eLateFragmentTests;
case ResourceUse::ShaderRead:
return vk::PipelineStageFlagBits2::eFragmentShader;
case ResourceUse::TransferSrc:
case ResourceUse::TransferDst:
return vk::PipelineStageFlagBits2::eTransfer;
}
return vk::PipelineStageFlagBits2::eNone;
}
vk::AccessFlags2 RenderGraph::UseDstAccess(const PassNode::Use& use)
{
switch (use.use) {
case ResourceUse::ColorAttachment:
return vk::AccessFlagBits2::eColorAttachmentWrite;
case ResourceUse::DepthAttachment:
return vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
case ResourceUse::ShaderRead:
return vk::AccessFlagBits2::eShaderRead;
case ResourceUse::TransferSrc:
return vk::AccessFlagBits2::eTransferRead;
case ResourceUse::TransferDst:
return vk::AccessFlagBits2::eTransferWrite;
case ResourceUse::Present:
// layout-only 转换(交给 presentation engine),无访问掩码
return vk::AccessFlagBits2::eNone;
}
return vk::AccessFlagBits2::eNone;
}
vk::AccessFlags2 RenderGraph::UseSrcAccess(ResourceUse use)
{
switch (use) {
case ResourceUse::ColorAttachment:
case ResourceUse::Present:
return vk::AccessFlagBits2::eColorAttachmentWrite;
case ResourceUse::DepthAttachment:
return vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
case ResourceUse::ShaderRead:
return vk::AccessFlagBits2::eShaderRead;
case ResourceUse::TransferSrc:
return vk::AccessFlagBits2::eTransferRead;
case ResourceUse::TransferDst:
return vk::AccessFlagBits2::eTransferWrite;
}
return vk::AccessFlagBits2::eNone;
}
u32 RenderGraph::GetPassCount() const { return static_cast<u32>(m_Passes.size()); }
u32 RenderGraph::GetActivePassCount() const { return m_ActivePassCount; }
u32 RenderGraph::GetTextureCount() const { return static_cast<u32>(m_Textures.size()); }
u32 RenderGraph::GetBarrierCount() const
{
u32 total = 0;
for (const auto& pass : m_Passes) {
total += static_cast<u32>(pass.barriers.size());
}
return total;
}
u32 RenderGraph::GetPooledImageCount() const
{
// 池中空闲 + 租借中(挂在资源上的物理镜像)
u32 total = 0;
for (const auto& [key, images] : m_Pool) {
total += static_cast<u32>(images.size());
}
for (const auto& entry : m_Textures) {
if (entry.imported) continue;
for (auto* img : entry.physical) {
if (img) ++total;
}
}
return total;
}
} // namespace Engine::RG
+64
View File
@@ -0,0 +1,64 @@
//
// Created by atdunbg on 2026/9/22.
//
#include <Engine/Renderer/Camera.h>
#include <Engine/Core/Window.h>
#include <glm/gtc/matrix_transform.hpp>
#include "GLFW/glfw3.h"
namespace Engine {
void Camera::Update(Window& window, f32 deltaTime)
{
// 鼠标旋转(需显式启用,配合 cursor capture 使用)
if (m_MouseLookEnabled)
{
f32 mouseDX = 0, mouseDY = 0;
window.GetMouseDelta(mouseDX, mouseDY);
if (mouseDX != 0 || mouseDY != 0)
{
m_Yaw += mouseDX * m_MouseSensitivity;
m_Pitch -= mouseDY * m_MouseSensitivity;
m_Pitch = glm::clamp(m_Pitch, -89.0f, 89.0f);
}
}
UpdateBasis();
f32 speed = m_MoveSpeed * deltaTime;
if (window.IsKeyPressed(GLFW_KEY_W)) m_Position += m_Forward * speed;
if (window.IsKeyPressed(GLFW_KEY_S)) m_Position -= m_Forward * speed;
if (window.IsKeyPressed(GLFW_KEY_A)) m_Position -= m_Right * speed;
if (window.IsKeyPressed(GLFW_KEY_D)) m_Position += m_Right * speed;
if (window.IsKeyPressed(GLFW_KEY_SPACE)) m_Position.y += speed;
if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) m_Position.y -= speed;
}
void Camera::UpdateBasis()
{
m_Forward.x = cosf(glm::radians(m_Yaw)) * cosf(glm::radians(m_Pitch));
m_Forward.y = sinf(glm::radians(m_Pitch));
m_Forward.z = sinf(glm::radians(m_Yaw)) * cosf(glm::radians(m_Pitch));
m_Forward = glm::normalize(m_Forward);
m_Right = glm::normalize(glm::cross(m_Forward, glm::vec3(0, 1, 0)));
m_Up = glm::normalize(glm::cross(m_Right, m_Forward));
}
glm::mat4 Camera::GetView() const
{
return glm::lookAt(m_Position, m_Position + m_Forward, m_Up);
}
glm::mat4 Camera::GetProjection(f32 aspect, f32 nearZ, f32 farZ) const
{
auto proj = glm::perspective(glm::radians(45.0f), aspect, nearZ, farZ);
proj[1][1] *= -1.0f; // Vulkan Y 翻转
return proj;
}
} // namespace Engine
+287 -373
View File
@@ -4,6 +4,11 @@
#include <Engine/Renderer/Renderer.h>
#include <Engine/Renderer/Material.h>
#include <Engine/Renderer/Model.h>
#include <Engine/Renderer/Vertex.h>
#include <Engine/ECS/Scene.h>
#include <Engine/ECS/Components.h>
#include <Engine/Assets/AssetManager.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/DescriptorManager.h>
@@ -20,26 +25,30 @@
#include <array>
#include <limits>
#include <vector>
#include "Engine/Core/Stats.h"
#include "GLFW/glfw3.h"
namespace Engine
{
using namespace ECS; // TransformComponent / Entity 等直接使用
struct Light {
glm::vec4 direction;
glm::vec4 color; // rgb = 颜色,a = 强度
};
namespace
{
struct Light {
glm::vec4 direction; // xyz = 方向,w 未用
glm::vec4 color; // rgb = 颜色,a = 强度
};
struct UniformBufferObject {
glm::mat4 view;
glm::mat4 proj;
glm::vec4 ambientColor;
Light lights[4];
};
struct UniformBufferObject {
glm::mat4 view;
glm::mat4 proj;
glm::vec4 ambientColor;
Light lights[4];
};
constexpr u32 kUboBinding = 0;
constexpr u32 kUBOSetIndex = 0;
constexpr u32 kTextureSetIndex = 1;
constexpr u32 kMaxLights = 4;
}
Renderer::Renderer(Window& window)
: m_Window(window)
@@ -55,7 +64,8 @@ namespace Engine
// 4. Set 0: UBO
std::vector<DescriptorBinding> uboBindings = {
{0, vk::DescriptorType::eUniformBuffer, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment },
{kUboBinding, vk::DescriptorType::eUniformBuffer,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
};
m_UBOSetLayout = m_DescriptorManager->CreateLayout(uboBindings);
@@ -66,277 +76,350 @@ namespace Engine
m_TextureSetLayout = m_DescriptorManager->CreateLayout(texBindings);
// 6. PipelineLayout:两个 set
std::vector<vk::DescriptorSetLayout> layouts = {
m_UBOSetLayout, m_TextureSetLayout
};
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(
layouts, sizeof(glm::mat4));
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
// 7. ShaderManager
// 7. ShaderManager(VK_EXT_shader_object)
m_ShaderManager = CreateScope<ShaderManager>(*m_Device);
std::string shaderDir = "shaders/";
auto vert = m_ShaderManager->CreateFromFile(
shaderDir + "simple_shader.vert.spv",
vk::ShaderStageFlagBits::eVertex,
layouts, sizeof(glm::mat4));
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
auto frag = m_ShaderManager->CreateFromFile(
shaderDir + "simple_shader.frag.spv",
vk::ShaderStageFlagBits::eFragment,
layouts, sizeof(glm::mat4));
{m_UBOSetLayout, m_TextureSetLayout}, sizeof(glm::mat4));
m_VertShader = vert.Handle;
m_FragShader = frag.Handle;
if (!m_VertShader || !m_FragShader)
{
if (!m_VertShader || !m_FragShader) {
ENGINE_FATAL("Failed to create shader objects");
}
// 8. Swapchain
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, window);
// 8. Swapchain(只管呈现;深度图由 RenderGraph 创建)
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
m_Extent = m_Swapchain->GetExtent();
m_Format = m_Swapchain->GetFormat();
// 9. SyncManager
m_SyncManager = CreateScope<SyncManager>(*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
// 9. Sync / Command(per-frame)
m_SyncManager = CreateScope<SyncManager>(
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
m_CommandManager = CreateScope<CommandManager>(*m_Device, kMaxFramesInFlight);
// 10. CommandManager
m_CommandManager = CreateScope<CommandManager>(*m_Device);
// 11. Uniform Buffer
// 10. Per-frame UBO ring + 每槽位的 UBO descriptor set
m_UniformBuffer = CreateScope<UniformBuffer>(
*m_Device, *m_Allocator, sizeof(UniformBufferObject));
*m_Device, *m_Allocator, sizeof(UniformBufferObject), kMaxFramesInFlight);
// 12. UBO DescriptorSet
m_UBODescriptorSet = m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
m_DescriptorManager->UpdateUniformBuffer(
m_UBODescriptorSet, 0, m_UniformBuffer->Get(), m_UniformBuffer->GetSize());
// 13. Model(模型自己加载纹理)
m_Model = CreateScope<Model>(
*m_Device, *m_Allocator, *m_DescriptorManager,
m_TextureSetLayout,
"assets/models/backpack/backpack.obj");
// 14. 创建几个物体,摆一排
for (int i = 0; i < 3; ++i) {
Object obj;
obj.model = m_Model.get();
obj.transform = glm::translate(
glm::mat4(1.0f),
glm::vec3((i - 1) * 1.5f, 0.0f, 0.0f));
m_Objects.push_back(obj);
m_UBODescriptorSets.resize(kMaxFramesInFlight);
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_UBODescriptorSets[slot] =
m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
m_DescriptorManager->UpdateUniformBuffer(
m_UBODescriptorSets[slot], kUboBinding,
m_UniformBuffer->Get(slot), m_UniformBuffer->GetSize());
}
m_StartTime = std::chrono::steady_clock::now();
// 11. RenderGraph:声明 + 编译(场景在 Render(Scene&) 时按帧绑定)
BuildGraph();
ENGINE_INFO("Renderer initialized");
}
Renderer::~Renderer()
{
m_Device->GetDevice().waitIdle();
if (m_VertShader)
{
if (m_VertShader) {
m_Device->GetDevice().destroyShaderEXT(m_VertShader);
}
if (m_FragShader)
{
if (m_FragShader) {
m_Device->GetDevice().destroyShaderEXT(m_FragShader);
}
m_Device->GetDevice().destroyPipelineLayout(m_PipelineLayout);
m_Device->GetDevice().destroyDescriptorSetLayout(m_UBOSetLayout);
m_Device->GetDevice().destroyDescriptorSetLayout(m_TextureSetLayout);
}
void Renderer::Render()
void Renderer::BuildGraph()
{
m_RenderGraph = CreateScope<RG::RenderGraph>(
*m_Device, *m_Allocator, kMaxFramesInFlight);
if (m_Window.WasResized())
{
// 导入 swapchain 颜色附件
m_Backbuffer = m_RenderGraph->ImportSwapchain(
"Backbuffer", m_Swapchain->GetFormat());
// 深度图:目标尺寸,格式由 Device 能力决定
const vk::Format depthFormat = m_Device->FindSupportedFormat(
{vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint,
vk::Format::eD24UnormS8Uint},
vk::ImageTiling::eOptimal,
vk::FormatFeatureFlagBits::eDepthStencilAttachment);
m_Depth = m_RenderGraph->CreateTexture(
"MainDepth", RG::TextureDesc{depthFormat});
// ---- Forward pass:直接渲染到 swapchain ----
// 场景数据每帧经 m_BoundScene 从 ECS 读取
m_RenderGraph->AddPass("Forward",
[this](RG::RenderGraphContext& ctx) {
const vk::CommandBuffer cmd = ctx.cmd;
vk::ClearValue clearValue;
clearValue.color = vk::ClearColorValue(
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(ctx.View(m_Backbuffer))
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(clearValue);
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(ctx.View(m_Depth))
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setClearValue(vk::ClearValue(
vk::ClearDepthStencilValue(1.0f, 0)));
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
.setLayerCount(1)
.setColorAttachments(colorAttachment)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
// 绑定 shader object
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_VertShader, m_FragShader};
cmd.bindShadersEXT(stages, shaders);
// Set 0:本帧槽位的 UBO
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_PipelineLayout, kUBOSetIndex,
m_UBODescriptorSets[ctx.frame.frameSlot], {});
// ---- 动态状态 ----
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
cmd.setDepthBoundsTestEnable(VK_FALSE);
cmd.setStencilTestEnable(VK_FALSE);
cmd.setDepthBiasEnable(VK_FALSE);
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
cmd.setCullMode(vk::CullModeFlagBits::eNone);
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
cmd.setPrimitiveRestartEnable(VK_FALSE);
auto binding = Vertex::GetBindingDescription();
auto attributes = Vertex::GetAttributeDescriptions();
cmd.setVertexInputEXT(binding, attributes);
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
vk::SampleMask sampleMask = 0xFFFFFFFF;
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
cmd.setAlphaToOneEnableEXT(VK_FALSE);
vk::Viewport viewport(0.0f, 0.0f,
static_cast<float>(ctx.extent.width),
static_cast<float>(ctx.extent.height),
0.0f, 1.0f);
cmd.setViewportWithCount(viewport);
vk::Rect2D scissor({0, 0}, ctx.extent);
cmd.setScissorWithCount(scissor);
vk::Bool32 blendEnable = VK_FALSE;
cmd.setColorBlendEnableEXT(0, blendEnable);
vk::ColorBlendEquationEXT blendEquation;
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
.setDstColorBlendFactor(vk::BlendFactor::eZero)
.setColorBlendOp(vk::BlendOp::eAdd)
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
.setAlphaBlendOp(vk::BlendOp::eAdd);
cmd.setColorBlendEquationEXT(0, blendEquation);
vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorWriteMaskEXT(0, colorWriteMask);
// ---- 绘制:遍历 ECS 渲染体 ----
// Each 以 Transform/MeshRenderer 中较小的池为主表——
// 大场景下遍历开销与最小池规模成正比
m_BoundScene->Each<TransformComponent, MeshRendererComponent>(
[&](Entity, TransformComponent& transform,
MeshRendererComponent& meshRenderer) {
// 句柄 → 资产(悬空句柄会被 AssetCache FATAL 拦截)
Model* model = m_BoundAssets->GetModel(meshRenderer.model);
if (!model) return;
cmd.pushConstants(m_PipelineLayout,
vk::ShaderStageFlagBits::eVertex,
0, sizeof(glm::mat4), &transform.world);
for (auto& mesh : model->GetMeshes()) {
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_PipelineLayout, kTextureSetIndex,
mesh->GetMaterial()->GetDescriptorSet(), {});
mesh->Draw(cmd);
}
});
cmd.endRendering();
})
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
.Write(m_Depth, RG::ResourceUse::DepthAttachment);
// ---- Present pass:transition 到 ePresentSrcKHR ----
m_RenderGraph->AddPass("Present",
[](RG::RenderGraphContext&) {
// present 的 layout 转换由 RG 的 barrier 完成,
// 实际 vkQueuePresentKHR 由 Renderer 驱动。
})
.Read(m_Backbuffer, RG::ResourceUse::Present);
m_RenderGraph->SetTargetExtent(m_Extent); // 触发首次 Compile
}
void Renderer::HandleResize()
{
m_Device->GetDevice().waitIdle();
m_Swapchain->Recreate(m_Window);
m_Extent = m_Swapchain->GetExtent();
// image 数量可能变化
m_SyncManager = CreateScope<SyncManager>(
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
// RG 重编译:物理镜像按新尺寸轮换
m_RenderGraph->SetTargetExtent(m_Extent);
}
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
{
m_BoundScene = &scene;
m_BoundAssets = &assets;
if (m_Window.WasResized()) {
m_Window.ResetResizedFlag();
m_Device->GetDevice().waitIdle();
m_Swapchain->Recreate(m_Window);
m_Extent = m_Swapchain->GetExtent();
m_SyncManager = CreateScope<SyncManager>(
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
HandleResize();
}
u32 frameIndex = m_CurrentFrame;
const u32 frameSlot = m_CurrentFrame;
const auto& vkDevice = m_Device->GetDevice();
// 1. 更新 MVP 矩阵
auto now = std::chrono::steady_clock::now();
float currentTime = std::chrono::duration<float>(now - m_StartTime).count();
m_DeltaTime = currentTime - m_LastFrameTime;
m_LastFrameTime = currentTime;
// 更新每个物体的 transform
for (size_t i = 0; i < m_Objects.size(); ++i) {
float angle = currentTime * glm::radians(60.0f) + i * 0.7f;
glm::mat4 t = glm::translate(
glm::mat4(1.0f),
glm::vec3((static_cast<float>(i) - 1.0f) * 1.5f, 0.0f, 0.0f));
glm::mat4 r = glm::rotate(glm::mat4(1.0f), angle, glm::vec3(0.0f, 1.0f, 0.0f));
m_Objects[i].transform = t * r;
// 1. UBO:从 ECS 场景组装(primary 相机 + 灯光 + 环境色)
auto* cameraComp = scene.GetPrimaryCamera();
if (!cameraComp) {
// 无相机:跳过本帧(正常场景必有相机,这里只是安全网)
return;
}
// 鼠标控制方向
/*
float mouseDX = 0, mouseDY = 0;
m_Window.GetMouseDelta(mouseDX, mouseDY);
if (mouseDX != 0 || mouseDY != 0)
{
m_CameraYaw += mouseDX * m_MouseSensitivity;
m_CameraPitch -= mouseDY * m_MouseSensitivity;
m_CameraPitch = glm::clamp(m_CameraPitch, -89.0f, 89.0f);
}
*/
// 相机前方向量
glm::vec3 forward;
forward.x = cosf(glm::radians(m_CameraYaw)) * cos(glm::radians(m_CameraPitch));
forward.y = sinf(glm::radians(m_CameraPitch));
forward.z = sinf(glm::radians(m_CameraYaw)) * cos(glm::radians(m_CameraPitch));
forward = glm::normalize(forward);
glm::vec3 right = glm::normalize(glm::cross(forward, glm::vec3(0, 1, 0)));
glm::vec3 up = glm::normalize(glm::cross(right, forward));
// WASD 移动
float speed = m_CameraSpeed * m_DeltaTime;
if (m_Window.IsKeyPressed(GLFW_KEY_W)) m_CameraPos += forward * speed;
if (m_Window.IsKeyPressed(GLFW_KEY_S)) m_CameraPos -= forward * speed;
if (m_Window.IsKeyPressed(GLFW_KEY_A)) m_CameraPos -= right * speed;
if (m_Window.IsKeyPressed(GLFW_KEY_D)) m_CameraPos += right * speed;
if (m_Window.IsKeyPressed(GLFW_KEY_SPACE)) m_CameraPos.y += speed;
if (m_Window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) m_CameraPos.y -= speed;
auto view = glm::lookAt(m_CameraPos,
m_CameraPos + forward,
up);
const Camera& camera = cameraComp->camera;
float aspect = static_cast<f32>(m_Extent.width) /
static_cast<f32>(m_Extent.height);
auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
proj[1][1] *= -1.0f; // Vulkan Y 翻转
static_cast<f32>(m_Extent.height);
UniformBufferObject ubo{};
ubo.view = view;
ubo.proj = proj;
ubo.view = camera.GetView();
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
ubo.ambientColor = scene.GetSettings().ambientColor;
ubo.ambientColor = glm::vec4(0.15f, 0.15f, 0.2f, 0.0f);
u32 lightCount = 0;
scene.Each<LightComponent>([&](Entity, LightComponent& light) {
if (lightCount >= kMaxLights) return;
ubo.lights[lightCount].direction =
glm::length(light.direction) > 0.0f
? glm::vec4(glm::normalize(light.direction), 0.0f)
: glm::vec4(0.0f);
ubo.lights[lightCount].color = light.color;
++lightCount;
});
for (u32 i = lightCount; i < kMaxLights; ++i) {
ubo.lights[i].direction = glm::vec4(0.0f);
ubo.lights[i].color = glm::vec4(0.0f);
}
// 主光:暖色,从左上方照下
ubo.lights[0].direction = glm::vec4(glm::normalize(glm::vec3(-0.5f, -1.0f, -0.3f)), 0.0f);
ubo.lights[0].color = glm::vec4(1.0f, 0.95f, 0.9f, 1.0f);
// 此时本槽位的 fence 尚未 reset——先等它(保证 GPU 已完成本槽位上一轮),
// 再写 UBO,写-读竞争关闭。
m_SyncManager->WaitFence(frameSlot);
m_UniformBuffer->Update(frameSlot, &ubo, sizeof(ubo));
m_SyncManager->ResetFence(frameSlot);
// 补光:冷色,从右下方照上
ubo.lights[1].direction = glm::vec4(glm::normalize(glm::vec3(0.5f, -0.5f, 0.3f)), 0.0f);
ubo.lights[1].color = glm::vec4(0.3f, 0.5f, 1.0f, 0.8f);
// 未使用:强度 0
ubo.lights[2].direction = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
ubo.lights[2].color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
ubo.lights[3].direction = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
ubo.lights[3].color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
m_UniformBuffer->Update(&ubo, sizeof(ubo));
// 等待这一帧上一次完成
m_SyncManager->WaitFence(frameIndex);
// 2. 获取交换链 image
// 2. acquire swapchain image
u32 imageIndex = 0;
try
{
try {
const auto result = vkDevice.acquireNextImageKHR(
m_Swapchain->Get(),
std::numeric_limits<u64>::max(),
m_SyncManager->GetImageAvailable(frameIndex));
m_SyncManager->GetImageAvailable(frameSlot));
if (result.result == vk::Result::eErrorOutOfDateKHR)
{
m_Swapchain->Recreate(m_Window);
return; // ← fence 没被 reset,下帧 wait 立即返回
if (result.result == vk::Result::eErrorOutOfDateKHR) {
HandleResize();
return;
}
imageIndex = result.value;
}
catch (const vk::OutOfDateKHRError&)
{
m_Swapchain->Recreate(m_Window);
catch (const vk::OutOfDateKHRError&) {
HandleResize();
return;
}
// 3. acquire 成功, reset fence
m_SyncManager->ResetFence(frameIndex);
// 3. 录制:FrameContext + RenderGraph
FrameContext frame;
frame.frameSlot = frameSlot;
frame.imageIndex = imageIndex;
frame.cmd = m_CommandManager->BeginFrame(frameSlot);
// 4. 录制命令
auto cmd = m_CommandManager->BeginFrame();
m_RenderGraph->SetImportedImage(
m_Backbuffer,
m_Swapchain->GetImages()[imageIndex],
m_Swapchain->GetImageViews()[imageIndex]);
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal);
m_RenderGraph->Execute(frame);
// 深度转换
vk::ImageMemoryBarrier2 depthBarrier;
depthBarrier.setSrcStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
.setSrcAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
.setDstStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
.setDstAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setImage(m_Swapchain->GetDepthImage())
.setSubresourceRange({
vk::ImageAspectFlagBits::eDepth,
0, 1, 0, 1
});
m_CommandManager->EndFrame(frameSlot);
vk::DependencyInfo depthDep;
depthDep.setImageMemoryBarriers(depthBarrier);
cmd.pipelineBarrier2(depthDep);
RecordCommands(cmd, imageIndex);
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR);
m_CommandManager->EndFrame();
// 5. 提交
// 4. 提交
std::array<vk::PipelineStageFlags, 1> waitStages = {
vk::PipelineStageFlagBits::eColorAttachmentOutput
};
std::array<vk::Semaphore, 1> waitSemaphores = {
m_SyncManager->GetImageAvailable(frameIndex)
m_SyncManager->GetImageAvailable(frameSlot)
};
std::array<vk::Semaphore, 1> signalSemaphores = {
m_SyncManager->GetRenderFinished(imageIndex)
};
vk::SubmitInfo submitInfo;
submitInfo.setCommandBuffers(cmd)
submitInfo.setCommandBuffers(frame.cmd)
.setWaitSemaphores(waitSemaphores)
.setWaitDstStageMask(waitStages)
.setSignalSemaphores(signalSemaphores);
m_Device->GetGraphicsQueue().submit(submitInfo, m_SyncManager->GetFrameFence(frameIndex));
m_Device->GetGraphicsQueue().submit(
submitInfo, m_SyncManager->GetFrameFence(frameSlot));
// 6. 呈现
// 5. 呈现
std::array<vk::SwapchainKHR, 1> swapchains = {m_Swapchain->Get()};
std::array<vk::Semaphore, 1> presentWait = {
m_SyncManager->GetRenderFinished(imageIndex)
@@ -347,186 +430,17 @@ namespace Engine
.setSwapchains(swapchains)
.setWaitSemaphores(presentWait);
try
{
try {
auto result = m_Device->GetPresentQueue().presentKHR(presentInfo);
if (result == vk::Result::eSuboptimalKHR)
{
m_Swapchain->Recreate(m_Window);
if (result == vk::Result::eSuboptimalKHR) {
HandleResize();
}
}
catch (const vk::OutOfDateKHRError&)
{
m_Swapchain->Recreate(m_Window);
catch (const vk::OutOfDateKHRError&) {
HandleResize();
}
if (m_Window.IsKeyPressed(GLFW_KEY_F1))
STATS.PrintComparison();
m_CurrentFrame = (m_CurrentFrame + 1) % kMaxFramesInFlight;
}
void Renderer::RecordCommands(vk::CommandBuffer cmd, u32 imageIndex)
{
vk::ClearValue clearValue;
clearValue.color = vk::ClearColorValue(std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
// 颜色附件
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(m_Swapchain->GetImageViews()[imageIndex])
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(clearValue);
// 深度附件
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(m_Swapchain->GetDepthImageView())
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setClearValue(vk::ClearValue(vk::ClearDepthStencilValue(1.0f, 0)));
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, m_Extent})
.setLayerCount(1)
.setColorAttachments(colorAttachment)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
// 绑定着色器
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_VertShader, m_FragShader};
cmd.bindShadersEXT(stages, shaders);
// 绑定描述符集
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_PipelineLayout, 0,
m_UBODescriptorSet, {});
// ===== 动态状态 =====
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
cmd.setDepthBoundsTestEnable(VK_FALSE);
cmd.setStencilTestEnable(VK_FALSE);
cmd.setDepthBiasEnable(VK_FALSE);
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
cmd.setCullMode(vk::CullModeFlagBits::eNone);
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
cmd.setPrimitiveRestartEnable(VK_FALSE);
// 顶点输入
/*
vk::VertexInputBindingDescription2EXT binding;
binding.setBinding(0)
.setStride(sizeof(f32) * 5)
.setInputRate(vk::VertexInputRate::eVertex)
.setDivisor(1);
std::array<vk::VertexInputAttributeDescription2EXT, 2> attributes;
// location 0: position
attributes[0].setLocation(0)
.setBinding(0)
.setFormat(vk::Format::eR32G32B32Sfloat)
.setOffset(0);
// location 1: uv
attributes[1].setLocation(1)
.setBinding(0)
.setFormat(vk::Format::eR32G32Sfloat)
.setOffset(sizeof(f32) * 3);
*/
auto binding = Vertex::GetBindingDescription();
auto attributes = Vertex::GetAttributeDescriptions();
cmd.setVertexInputEXT(binding, attributes);
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
vk::SampleMask sampleMask = 0xFFFFFFFF;
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
cmd.setAlphaToOneEnableEXT(VK_FALSE);
vk::Viewport viewport(0.0f, 0.0f,
static_cast<float>(m_Extent.width),
static_cast<float>(m_Extent.height),
0.0f, 1.0f);
cmd.setViewportWithCount(viewport);
vk::Rect2D scissor({0, 0}, m_Extent);
cmd.setScissorWithCount(scissor);
vk::Bool32 blendEnable = VK_FALSE;
cmd.setColorBlendEnableEXT(0, blendEnable);
vk::ColorBlendEquationEXT blendEquation;
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
.setDstColorBlendFactor(vk::BlendFactor::eZero)
.setColorBlendOp(vk::BlendOp::eAdd)
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
.setAlphaBlendOp(vk::BlendOp::eAdd);
cmd.setColorBlendEquationEXT(0, blendEquation);
vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorWriteMaskEXT(0, colorWriteMask);
// 绑定顶点缓冲并绘制
for (auto& obj : m_Objects) {
cmd.pushConstants(m_PipelineLayout,
vk::ShaderStageFlagBits::eVertex,
0, sizeof(glm::mat4), &obj.transform);
for (auto& mesh : obj.model->GetMeshes()) {
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_PipelineLayout, 1,
mesh->GetMaterial()->GetDescriptorSet(), {});
mesh->Draw(cmd);
}
}
/*
cmd.bindVertexBuffers(0, m_VertexBuffer->Get(), {0});
cmd.bindIndexBuffer(m_IndexBuffer->Get(), 0, vk::IndexType::eUint32);
// cmd.draw(3, 1, 0, 0);
cmd.drawIndexed(36, 1, 0, 0, 0);
*/
cmd.endRendering();
}
void Renderer::TransitionImageLayout(vk::CommandBuffer cmd, vk::Image image,
vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
{
vk::ImageMemoryBarrier2 barrier;
barrier.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
.setOldLayout(oldLayout)
.setNewLayout(newLayout)
.setImage(image)
.setSubresourceRange({
vk::ImageAspectFlagBits::eColor,
0, 1, 0, 1
});
vk::DependencyInfo dependencyInfo;
dependencyInfo.setImageMemoryBarriers(barrier);
cmd.pipelineBarrier2(dependencyInfo);
}
} // namespace Engine
+14 -12
View File
@@ -8,25 +8,26 @@
namespace Engine {
CommandManager::CommandManager(Device& device)
CommandManager::CommandManager(Device& device, u32 frameCount)
: m_Device(device)
{
// 创建命令池
// eResetCommandBuffer:允许单独 reset 某个 buffer,而不是整池 reset。
// 若改为 per-frame pool + 整池 reset,可在帧槽 fence 等待后一次 reset,
// 驱动开销更低——当前规模下单池多 buffer 已足够。
vk::CommandPoolCreateInfo poolInfo;
poolInfo.setQueueFamilyIndex(m_Device.GetGraphicsFamily())
.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
m_Pool = m_Device.GetDevice().createCommandPool(poolInfo);
// 分配一个命令缓冲
vk::CommandBufferAllocateInfo allocInfo;
allocInfo.setCommandPool(m_Pool)
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1);
.setCommandBufferCount(frameCount);
m_Buffer = m_Device.GetDevice().allocateCommandBuffers(allocInfo)[0];
m_Buffers = m_Device.GetDevice().allocateCommandBuffers(allocInfo);
ENGINE_INFO("CommandManager created");
ENGINE_INFO("CommandManager created: {} frame buffers", frameCount);
}
CommandManager::~CommandManager()
@@ -34,20 +35,21 @@ namespace Engine {
m_Device.GetDevice().destroyCommandPool(m_Pool);
}
vk::CommandBuffer CommandManager::BeginFrame()
vk::CommandBuffer CommandManager::BeginFrame(u32 frameSlot)
{
m_Buffer.reset();
auto cmd = m_Buffers[frameSlot];
cmd.reset();
vk::CommandBufferBeginInfo beginInfo;
beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
m_Buffer.begin(beginInfo);
cmd.begin(beginInfo);
return m_Buffer;
return cmd;
}
void CommandManager::EndFrame()
void CommandManager::EndFrame(u32 frameSlot)
{
m_Buffer.end();
m_Buffers[frameSlot].end();
}
} // namespace Engine
+20
View File
@@ -310,6 +310,26 @@ uint32_t Device::FindMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags pro
return 0;
}
vk::Format Device::FindSupportedFormat(const std::vector<vk::Format>& candidates,
vk::ImageTiling tiling,
vk::FormatFeatureFlags features) const
{
for (auto format : candidates) {
auto props = m_PhysicalDevice.getFormatProperties(format);
if (tiling == vk::ImageTiling::eLinear &&
(props.linearTilingFeatures & features) == features) {
return format;
}
if (tiling == vk::ImageTiling::eOptimal &&
(props.optimalTilingFeatures & features) == features) {
return format;
}
}
ENGINE_FATAL("No supported format found in candidate list");
return candidates.empty() ? vk::Format::eUndefined : candidates.front();
}
// ---------- 验证层检查 ----------
bool Device::CheckValidationLayerSupport()
-78
View File
@@ -99,14 +99,10 @@ namespace Engine
m_Extent.width, m_Extent.height,
m_Images.size(),
vk::to_string(m_Format));
CreateDepthResources();
}
void Swapchain::Destroy()
{
DestroyDepthResources();
const auto& vkDevice = m_Device.GetDevice();
for (auto view : m_ImageViews)
@@ -181,78 +177,4 @@ namespace Engine
return extent;
}
vk::Format Swapchain::FindDepthFormat()
{
// 优先 D32_SFLOAT,退回 D32_SFLOAT_S8_UINT,再退回 D24_UNORM_S8_UINT
std::array<vk::Format, 3> candidates = {
vk::Format::eD32Sfloat,
vk::Format::eD32SfloatS8Uint,
vk::Format::eD24UnormS8Uint
};
for (auto format : candidates)
{
auto props = m_Device.GetPhysicalDevice().getFormatProperties(format);
if (props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment)
{
return format;
}
}
ENGINE_FATAL("No depth format supported");
return vk::Format::eD32Sfloat;
}
void Swapchain::CreateDepthResources()
{
m_DepthFormat = FindDepthFormat();
vk::ImageCreateInfo imageInfo;
imageInfo.setImageType(vk::ImageType::e2D)
.setFormat(m_DepthFormat)
.setExtent({m_Extent.width, m_Extent.height, 1})
.setMipLevels(1)
.setArrayLayers(1)
.setSamples(vk::SampleCountFlagBits::e1)
.setTiling(vk::ImageTiling::eOptimal)
.setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment)
.setSharingMode(vk::SharingMode::eExclusive)
.setInitialLayout(vk::ImageLayout::eUndefined);
m_DepthImage = m_Device.GetDevice().createImage(imageInfo);
auto reqs = m_Device.GetDevice().getImageMemoryRequirements(m_DepthImage);
auto alloc = m_Allocator.Allocate(reqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
m_DepthMemory = alloc.Memory;
m_Device.GetDevice().bindImageMemory(m_DepthImage, m_DepthMemory, alloc.Offset);
vk::ImageViewCreateInfo viewInfo;
viewInfo.setImage(m_DepthImage)
.setViewType(vk::ImageViewType::e2D)
.setFormat(m_DepthFormat)
.setSubresourceRange({
vk::ImageAspectFlagBits::eDepth,
0, 1, 0, 1
});
m_DepthImageView = m_Device.GetDevice().createImageView(viewInfo);
}
void Swapchain::DestroyDepthResources()
{
const auto& vkDevice = m_Device.GetDevice();
if (m_DepthImageView)
{
vkDevice.destroyImageView(m_DepthImageView);
m_DepthImageView = nullptr;
}
if (m_DepthImage)
{
vkDevice.destroyImage(m_DepthImage);
m_DepthImage = nullptr;
}
// 内存由 MemoryAllocator 统一释放
}
} // namespace Engine
+15 -17
View File
@@ -9,28 +9,26 @@
namespace Engine {
UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator, u64 size)
UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator,
u64 size, u32 frameCount)
: m_Size(size)
{
// Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT
m_Buffer = CreateScope<Buffer>(
device, allocator,
size,
vk::BufferUsageFlagBits::eUniformBuffer,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent);
m_Buffers.reserve(frameCount);
for (u32 i = 0; i < frameCount; ++i) {
// Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT
m_Buffers.push_back(CreateScope<Buffer>(
device, allocator, size,
vk::BufferUsageFlagBits::eUniformBuffer,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent));
}
ENGINE_INFO("UniformBuffer created: {} bytes", size);
ENGINE_INFO("UniformBuffer created: {} bytes x {} frames", size, frameCount);
}
UniformBuffer::~UniformBuffer()
void UniformBuffer::Update(u32 frameSlot, const void* data, u64 size)
{
// Buffer 析构时自动销毁 vk::Buffer,内存由 MemoryAllocator 统一释放
m_Buffers[frameSlot]->Upload(data, size);
}
void UniformBuffer::Update(const void* data, u64 size)
{
m_Buffer->Upload(data, size);
}
} // namespace Engine
} // namespace Engine