Files
HelloVulkan/Engine/src/Vulkan/Swapchain.cpp
T
atdunbg 8afdd9b245 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 实测)
2026-09-22 13:46:05 +08:00

181 lines
5.5 KiB
C++

//
// Created by atdunbg on 2026/9/20.
//
#include <Engine/Vulkan/Swapchain.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <algorithm>
#include <limits>
#include "Engine/Vulkan/MemoryAllocator.h"
namespace Engine
{
Swapchain::Swapchain(Device& device, MemoryAllocator& allocator, Window& window)
: m_Device(device), m_Allocator(allocator)
{
Create(window);
}
Swapchain::~Swapchain()
{
Destroy();
}
void Swapchain::Create(Window& window)
{
const auto& vkDevice = m_Device.GetDevice();
auto surface = m_Device.GetSurface();
auto physicalDevice = m_Device.GetPhysicalDevice();
// 查询支持
auto caps = physicalDevice.getSurfaceCapabilitiesKHR(surface);
auto formats = physicalDevice.getSurfaceFormatsKHR(surface);
auto modes = physicalDevice.getSurfacePresentModesKHR(surface);
auto surfaceFormat = ChooseSurfaceFormat(formats);
auto presentMode = ChoosePresentMode(modes);
auto extent = ChooseExtent(caps, window);
m_Format = surfaceFormat.format;
m_Extent = extent;
// 交换链 image 数量
uint32_t imageCount = caps.minImageCount + 1;
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount)
{
imageCount = caps.maxImageCount;
}
vk::SwapchainCreateInfoKHR createInfo;
createInfo.setSurface(surface)
.setMinImageCount(imageCount)
.setImageFormat(surfaceFormat.format)
.setImageColorSpace(surfaceFormat.colorSpace)
.setImageExtent(extent)
.setImageArrayLayers(1)
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
.setImageSharingMode(vk::SharingMode::eExclusive)
.setPreTransform(caps.currentTransform)
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
.setPresentMode(presentMode)
.setClipped(VK_TRUE)
.setOldSwapchain(nullptr);
// graphics 和 present 队列族不同时,用 concurrent
uint32_t indices[] = {m_Device.GetGraphicsFamily(), m_Device.GetPresentFamily()};
if (indices[0] != indices[1])
{
createInfo.setImageSharingMode(vk::SharingMode::eConcurrent)
.setQueueFamilyIndices(indices);
}
m_Swapchain = vkDevice.createSwapchainKHR(createInfo);
// 拿 image
m_Images = vkDevice.getSwapchainImagesKHR(m_Swapchain);
// 创建 image view
m_ImageViews.resize(m_Images.size());
for (size_t i = 0; i < m_Images.size(); ++i)
{
vk::ImageViewCreateInfo viewInfo;
viewInfo.setImage(m_Images[i])
.setViewType(vk::ImageViewType::e2D)
.setFormat(m_Format)
.setSubresourceRange({
vk::ImageAspectFlagBits::eColor,
0, 1,
0, 1
});
m_ImageViews[i] = vkDevice.createImageView(viewInfo);
}
ENGINE_INFO("Swapchain created: {}x{}, {} images, format={}",
m_Extent.width, m_Extent.height,
m_Images.size(),
vk::to_string(m_Format));
}
void Swapchain::Destroy()
{
const auto& vkDevice = m_Device.GetDevice();
for (auto view : m_ImageViews)
{
vkDevice.destroyImageView(view);
}
m_ImageViews.clear();
m_Images.clear();
if (m_Swapchain)
{
vkDevice.destroySwapchainKHR(m_Swapchain);
m_Swapchain = nullptr;
}
}
void Swapchain::Recreate(Window& window)
{
const auto& vkDevice = m_Device.GetDevice();
vkDevice.waitIdle();
Destroy();
Create(window);
ENGINE_INFO("Swapchain recreated: {}x{}", m_Extent.width, m_Extent.height);
}
// ---------- 选择函数 ----------
vk::SurfaceFormatKHR Swapchain::ChooseSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats)
{
for (const auto& f : formats)
{
if (f.format == vk::Format::eB8G8R8A8Srgb &&
f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
{
return f;
}
}
return formats[0];
}
vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector<vk::PresentModeKHR>& modes)
{
for (const auto& m : modes)
{
if (m == vk::PresentModeKHR::eMailbox)
{
return m;
}
}
return vk::PresentModeKHR::eFifo;
}
vk::Extent2D Swapchain::ChooseExtent(const vk::SurfaceCapabilitiesKHR& caps, Window& window)
{
if (caps.currentExtent.width != std::numeric_limits<uint32_t>::max())
{
return caps.currentExtent;
}
int fbWidth = 0, fbHeight = 0;
window.GetFramebufferSize(fbWidth, fbHeight);
vk::Extent2D extent = {
static_cast<uint32_t>(fbWidth),
static_cast<uint32_t>(fbHeight)
};
extent.width = std::clamp(extent.width, caps.minImageExtent.width, caps.maxImageExtent.width);
extent.height = std::clamp(extent.height, caps.minImageExtent.height, caps.maxImageExtent.height);
return extent;
}
} // namespace Engine