14 vk::SurfaceKHR surface,
15 const WindowCreateInfo& window_config,
const std::vector<uint32_t>& sharing_families)
32 uint32_t desired_image_count = surface_info.
image_count;
41 "Swapchain support inadequate (formats: {}, modes: {})",
46 vk::SurfaceFormatKHR surface_format;
51 if (hdr_format.has_value()) {
52 surface_format = hdr_format.value();
55 "HDR requested but no HDR-capable format/colorspace supported by device/surface. Falling back to default.");
65 "Chosen swapchain format: {}, color space: {}",
66 vk::to_string(surface_format.format),
67 vk::to_string(surface_format.colorSpace));
73 uint32_t image_count = std::max(desired_image_count, support.
capabilities.minImageCount);
75 image_count = std::min(image_count, support.
capabilities.maxImageCount);
78 vk::SwapchainCreateInfoKHR create_info {};
79 create_info.surface = surface;
80 create_info.minImageCount = image_count;
81 create_info.imageFormat = surface_format.format;
82 create_info.imageColorSpace = surface_format.colorSpace;
83 create_info.imageExtent = extent;
84 create_info.imageArrayLayers = 1;
85 create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst;
87 if (support.
capabilities.supportedUsageFlags & vk::ImageUsageFlagBits::eTransferSrc) {
88 create_info.imageUsage |= vk::ImageUsageFlagBits::eTransferSrc;
91 "Surface does not support eTransferSrc; frame capture will be unavailable");
96 std::vector<uint32_t> unique_families;
97 for (uint32_t f : sharing_families) {
98 if (std::ranges::find(unique_families, f) == unique_families.end())
99 unique_families.push_back(f);
102 if (unique_families.size() > 1) {
103 create_info.imageSharingMode = vk::SharingMode::eConcurrent;
104 create_info.queueFamilyIndexCount =
static_cast<uint32_t
>(unique_families.size());
105 create_info.pQueueFamilyIndices = unique_families.data();
107 create_info.imageSharingMode = vk::SharingMode::eExclusive;
108 create_info.queueFamilyIndexCount = 0;
109 create_info.pQueueFamilyIndices =
nullptr;
112 create_info.preTransform = support.
capabilities.currentTransform;
113 create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
114 create_info.presentMode = present_mode;
115 create_info.clipped = VK_TRUE;
116 create_info.oldSwapchain =
nullptr;
120 }
catch (
const vk::SystemError& e) {
122 "Failed to create swapchain: {}", e.what());
139 "Swapchain created: {}x{}, {} images, format {}",
140 extent.width, extent.height,
m_images.size(),
141 vk::to_string(surface_format.format));
150 "Cannot recreate swapchain: no context set");
176 device.destroyImageView(image_view);
189 vk::Semaphore signal_semaphore,
194 "Cannot acquire image: no context set");
201 auto result =
device.acquireNextImageKHR(
207 if (result.result == vk::Result::eErrorOutOfDateKHR) {
211 if (result.result != vk::Result::eSuccess && result.result != vk::Result::eSuboptimalKHR) {
213 "Failed to acquire swapchain image");
219 }
catch (
const vk::SystemError& e) {
221 "Exception acquiring swapchain image: {}", e.what());
227 vk::Semaphore wait_semaphore,
228 vk::Queue present_queue)
232 "Cannot present: no context set");
238 vk::PresentInfoKHR present_info {};
239 present_info.waitSemaphoreCount = 1;
240 present_info.pWaitSemaphores = &wait_semaphore;
242 present_info.swapchainCount = 1;
244 present_info.pImageIndices = &image_index;
245 present_info.pResults =
nullptr;
248 auto result = queue.presentKHR(present_info);
250 if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR) {
254 if (result != vk::Result::eSuccess) {
256 "Failed to present swapchain image");
262 }
catch (
const vk::SystemError& e) {
264 "Exception presenting swapchain image: {}", e.what());
273 "Cannot create image views: no context set");
280 for (
size_t i = 0; i <
m_images.size(); i++) {
281 vk::ImageViewCreateInfo create_info {};
283 create_info.viewType = vk::ImageViewType::e2D;
286 create_info.components.r = vk::ComponentSwizzle::eIdentity;
287 create_info.components.g = vk::ComponentSwizzle::eIdentity;
288 create_info.components.b = vk::ComponentSwizzle::eIdentity;
289 create_info.components.a = vk::ComponentSwizzle::eIdentity;
291 create_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
292 create_info.subresourceRange.baseMipLevel = 0;
293 create_info.subresourceRange.levelCount = 1;
294 create_info.subresourceRange.baseArrayLayer = 0;
295 create_info.subresourceRange.layerCount = 1;
299 }
catch (
const vk::SystemError& e) {
301 "Failed to create image view {}: {}", i, e.what());
310 vk::PhysicalDevice physical_device,
311 vk::SurfaceKHR surface)
315 details.
capabilities = physical_device.getSurfaceCapabilitiesKHR(surface);
316 details.
formats = physical_device.getSurfaceFormatsKHR(surface);
317 details.
present_modes = physical_device.getSurfacePresentModesKHR(surface);
323 const std::vector<vk::SurfaceFormatKHR>& available_formats,
330 for (
const auto& format : available_formats) {
331 if (format.format == vk_format && format.colorSpace == vk_color_space) {
336 for (
const auto& format : available_formats) {
337 if (format.format == vk_format) {
339 "Exact color space match not found, using format match with different color space");
345 "Desired format not available, falling back to: {}",
346 vk::to_string(available_formats[0].format));
347 return available_formats[0];
351 const std::vector<vk::PresentModeKHR>& available_modes,
356 for (
const auto& mode : available_modes) {
357 if (mode == vk_mode) {
363 for (
const auto& mode : available_modes) {
364 if (mode == vk::PresentModeKHR::eMailbox) {
366 "Desired present mode not available, using MAILBOX");
370 for (
const auto& mode : available_modes) {
371 if (mode == vk::PresentModeKHR::eImmediate) {
373 "Desired present mode not available, using IMMEDIATE");
380 "Desired present mode not available, falling back to FIFO (VSync)");
381 return vk::PresentModeKHR::eFifo;
385 const std::vector<vk::SurfaceFormatKHR>& available_formats)
const
387 vk::SurfaceFormatKHR selected_format;
388 std::vector<std::pair<vk::Format, vk::ColorSpaceKHR>> hdr_candidates = {
389 { vk::Format::eR16G16B16A16Sfloat, vk::ColorSpaceKHR::eHdr10St2084EXT },
390 { vk::Format::eR16G16B16A16Sfloat, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT },
391 { vk::Format::eA2B10G10R10UnormPack32, vk::ColorSpaceKHR::eHdr10St2084EXT },
392 { vk::Format::eA2B10G10R10UnormPack32, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT },
395 bool hdr_supported =
false;
396 for (
const auto& candidate : hdr_candidates) {
397 for (
const auto& fmt : available_formats) {
398 if (fmt.format == candidate.first && fmt.colorSpace == candidate.second) {
399 selected_format = fmt;
400 hdr_supported =
true;
405 return selected_format;
412 const vk::SurfaceCapabilitiesKHR& capabilities,
416 if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
417 return capabilities.currentExtent;
421 actual_extent.width = std::clamp(
423 capabilities.minImageExtent.width,
424 capabilities.maxImageExtent.width);
426 actual_extent.height = std::clamp(
427 actual_extent.height,
428 capabilities.minImageExtent.height,
429 capabilities.maxImageExtent.height);
431 return actual_extent;
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
vk::PhysicalDevice device
vk::Device get_device() const
Get logical device.
const GraphicsSurfaceInfo & get_surface_info() const
Get graphics surface info.
vk::Queue get_graphics_queue() const
Get graphics queue.
vk::PhysicalDevice get_physical_device() const
Get physical device.
High-level wrapper for Vulkan instance and device.
vk::Extent2D choose_extent(const vk::SurfaceCapabilitiesKHR &capabilities, uint32_t width, uint32_t height) const
Choose swap extent based on capabilities.
vk::PresentModeKHR choose_present_mode(const std::vector< vk::PresentModeKHR > &available_modes, GraphicsSurfaceInfo::PresentMode desired_mode) const
Choose best present mode from available modes based on config.
bool create_image_views()
Create image views for swapchain images.
vk::Format m_image_format
static SwapchainSupportDetails query_support(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface)
Query swapchain support details for a device.
bool present(uint32_t image_index, vk::Semaphore wait_semaphore, vk::Queue present_queue=nullptr)
Present image to screen.
std::optional< uint32_t > acquire_next_image(vk::Semaphore signal_semaphore, uint64_t timeout_ns=UINT64_MAX)
Acquire next image from swapchain.
bool recreate(uint32_t width, uint32_t height)
Recreate swapchain (for window resize)
void cleanup()
Cleanup swapchain resources.
bool create(VKContext &context, vk::SurfaceKHR surface, const WindowCreateInfo &window_config, const std::vector< uint32_t > &sharing_families={})
Create swapchain for the given surface using VKContext.
std::optional< vk::SurfaceFormatKHR > find_hdr_format(const std::vector< vk::SurfaceFormatKHR > &available_formats) const
Find an HDR-capable format from available formats.
vk::SurfaceFormatKHR choose_surface_format(const std::vector< vk::SurfaceFormatKHR > &available_formats, GraphicsSurfaceInfo::SurfaceFormat desired_format, GraphicsSurfaceInfo::ColorSpace desired_color_space) const
Choose best surface format from available formats based on config.
void cleanup_swapchain()
Cleanup old swapchain during recreation.
std::vector< vk::Image > m_images
std::vector< uint32_t > m_sharing_families
std::vector< vk::ImageView > m_image_views
const WindowCreateInfo * m_window_config
vk::SwapchainKHR m_swapchain
vk::Format to_vk_format(GraphicsSurfaceInfo::SurfaceFormat fmt)
vk::PresentModeKHR to_vk_present_mode(GraphicsSurfaceInfo::PresentMode mode)
vk::ColorSpaceKHR to_vk_color_space(GraphicsSurfaceInfo::ColorSpace space)
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
bool enable_hdr
Enable HDR output if available.
PresentMode present_mode
Default presentation mode for new windows.
ColorSpace color_space
Default color space for new windows.
ColorSpace
Default color space for window surfaces.
SurfaceFormat
Default pixel format for window surfaces (Vulkan-compatible)
uint32_t image_count
Default number of swapchain images (double/triple buffering)
PresentMode
Frame presentation strategy.
@ MAILBOX
Triple buffering, no tear.
@ IMMEDIATE
No vsync, tear possible.
SurfaceFormat format
Default surface format for new windows.
System-wide configuration for visual stream processing.
std::vector< vk::PresentModeKHR > present_modes
std::vector< vk::SurfaceFormatKHR > formats
vk::SurfaceCapabilitiesKHR capabilities
Holds swapchain capability information for a physical device.
std::optional< GraphicsSurfaceInfo::PresentMode > present_mode
Override global present mode (nullopt = use global default)
std::optional< GraphicsSurfaceInfo::SurfaceFormat > surface_format
Override global surface format (nullopt = use global default)
uint32_t width
Initial window dimensions.
Configuration for creating a single window instance.