// // Created by atdunbg on 2026/9/20. // #include #include #include #include #include #include #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& formats) { // 用 Unorm 而非 Srgb:当前管线(场景光照 + ImGui)都是把 // "显示空间"颜色直接写进 framebuffer,若用 Srgb 附件硬件会再 // 做一次 linear→sRGB 提亮 → 整体泛白(ImGui 原生蓝 #4296FA // 会被洗成 #8CCBFC)。等上 HDR/tonemap 管线后再切回 Srgb。 for (const auto& f : formats) { if (f.format == vk::Format::eB8G8R8A8Unorm && f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { return f; } } return formats[0]; } vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector& modes) { // vsync 开:Mailbox(三缓冲垂直同步,低延迟);关:Immediate //(不限速,可能撕裂)。FIFO 是所有实现必须支持的兜底 const vk::PresentModeKHR preferred = m_VSync ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate; for (const auto& m : modes) { if (m == preferred) { return m; } } return vk::PresentModeKHR::eFifo; } vk::Extent2D Swapchain::ChooseExtent(const vk::SurfaceCapabilitiesKHR& caps, Window& window) { if (caps.currentExtent.width != std::numeric_limits::max()) { return caps.currentExtent; } int fbWidth = 0, fbHeight = 0; window.GetFramebufferSize(fbWidth, fbHeight); vk::Extent2D extent = { static_cast(fbWidth), static_cast(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