MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKSwapchain.cpp
Go to the documentation of this file.
1#include "VKSwapchain.hpp"
3
4#include "VKEnumUtils.hpp"
5
6namespace MayaFlux::Core {
7
12
14 vk::SurfaceKHR surface,
15 const WindowCreateInfo& window_config)
16{
17 m_context = &context;
18 m_surface = surface;
19
20 const GraphicsSurfaceInfo& surface_info = m_context->get_surface_info();
21
22 GraphicsSurfaceInfo::SurfaceFormat desired_format = window_config.surface_format.has_value()
23 ? window_config.surface_format.value()
24 : surface_info.format;
25
26 GraphicsSurfaceInfo::ColorSpace desired_color_space = surface_info.color_space;
27
28 GraphicsSurfaceInfo::PresentMode desired_present_mode = window_config.present_mode.has_value()
29 ? window_config.present_mode.value()
30 : surface_info.present_mode;
31
32 uint32_t desired_image_count = surface_info.image_count;
33
34 vk::PhysicalDevice physical_device = m_context->get_physical_device();
35 vk::Device device = m_context->get_device();
36
37 SwapchainSupportDetails support = query_support(physical_device, surface);
38
39 if (!support.is_adequate()) {
41 "Swapchain support inadequate (formats: {}, modes: {})",
42 support.formats.size(), support.present_modes.size());
43 return false;
44 }
45
46 vk::SurfaceFormatKHR surface_format;
47
48 if (surface_info.enable_hdr) {
49 auto hdr_format = find_hdr_format(support.formats);
50
51 if (hdr_format.has_value()) {
52 surface_format = hdr_format.value();
53 } else {
55 "HDR requested but no HDR-capable format/colorspace supported by device/surface. Falling back to default.");
56
57 surface_format = choose_surface_format(support.formats, desired_format, desired_color_space);
58 }
59
60 } else {
61 surface_format = choose_surface_format(support.formats, desired_format, desired_color_space);
62 }
63
65 "Chosen swapchain format: {}, color space: {}",
66 vk::to_string(surface_format.format),
67 vk::to_string(surface_format.colorSpace));
68
69 vk::PresentModeKHR present_mode = choose_present_mode(
70 support.present_modes, desired_present_mode);
71 vk::Extent2D extent = choose_extent(support.capabilities, window_config.width, window_config.height);
72
73 uint32_t image_count = std::max(desired_image_count, support.capabilities.minImageCount);
74 if (support.capabilities.maxImageCount > 0) {
75 image_count = std::min(image_count, support.capabilities.maxImageCount);
76 }
77
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;
86
87 if (support.capabilities.supportedUsageFlags & vk::ImageUsageFlagBits::eTransferSrc) {
88 create_info.imageUsage |= vk::ImageUsageFlagBits::eTransferSrc;
89 } else {
91 "Surface does not support eTransferSrc; frame capture will be unavailable");
92 }
93
94 const auto& queue_families = m_context->get_queue_families();
95
96 std::vector<uint32_t> queue_family_indices;
97 uint32_t graphics_family = queue_families.graphics_family.value();
98 uint32_t present_family = queue_families.present_family.value();
99
100 if (graphics_family != present_family) {
101 queue_family_indices = { graphics_family, present_family };
102 create_info.imageSharingMode = vk::SharingMode::eConcurrent;
103 create_info.queueFamilyIndexCount = static_cast<uint32_t>(queue_family_indices.size());
104 create_info.pQueueFamilyIndices = queue_family_indices.data();
105 } else {
106 create_info.imageSharingMode = vk::SharingMode::eExclusive;
107 create_info.queueFamilyIndexCount = 0;
108 create_info.pQueueFamilyIndices = nullptr;
109 }
110
111 create_info.preTransform = support.capabilities.currentTransform;
112 create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
113 create_info.presentMode = present_mode;
114 create_info.clipped = VK_TRUE;
115 create_info.oldSwapchain = nullptr;
116
117 try {
118 m_swapchain = device.createSwapchainKHR(create_info);
119 } catch (const vk::SystemError& e) {
121 "Failed to create swapchain: {}", e.what());
122 return false;
123 }
124
125 m_images = device.getSwapchainImagesKHR(m_swapchain);
126
127 m_image_format = surface_format.format;
128 m_extent = extent;
129
130 m_window_config = &window_config;
131
132 if (!create_image_views()) {
134 return false;
135 }
136
138 "Swapchain created: {}x{}, {} images, format {}",
139 extent.width, extent.height, m_images.size(),
140 vk::to_string(surface_format.format));
141
142 return true;
143}
144
145bool VKSwapchain::recreate(uint32_t width, uint32_t height)
146{
147 if (!m_context) {
149 "Cannot recreate swapchain: no context set");
150 return false;
151 }
152
153 m_context->get_device().waitIdle();
154
156
158}
159
161{
163 m_context = nullptr;
164}
165
167{
168 if (!m_context) {
169 return;
170 }
171
172 vk::Device device = m_context->get_device();
173
174 for (auto image_view : m_image_views) {
175 device.destroyImageView(image_view);
176 }
177 m_image_views.clear();
178
179 if (m_swapchain) {
180 device.destroySwapchainKHR(m_swapchain);
181 m_swapchain = nullptr;
182 }
183
184 m_images.clear();
185}
186
187std::optional<uint32_t> VKSwapchain::acquire_next_image(
188 vk::Semaphore signal_semaphore,
189 uint64_t timeout_ns)
190{
191 if (!m_context) {
193 "Cannot acquire image: no context set");
194 return std::nullopt;
195 }
196
197 vk::Device device = m_context->get_device();
198
199 try {
200 auto result = device.acquireNextImageKHR(
202 timeout_ns,
203 signal_semaphore,
204 nullptr);
205
206 if (result.result == vk::Result::eErrorOutOfDateKHR) {
207 return std::nullopt;
208 }
209
210 if (result.result != vk::Result::eSuccess && result.result != vk::Result::eSuboptimalKHR) {
212 "Failed to acquire swapchain image");
213 return std::nullopt;
214 }
215
216 return result.value;
217
218 } catch (const vk::SystemError& e) {
220 "Exception acquiring swapchain image: {}", e.what());
221 return std::nullopt;
222 }
223}
224
225bool VKSwapchain::present(uint32_t image_index,
226 vk::Semaphore wait_semaphore,
227 vk::Queue present_queue)
228{
229 if (!m_context) {
231 "Cannot present: no context set");
232 return false;
233 }
234
235 vk::Queue queue = present_queue ? present_queue : m_context->get_graphics_queue();
236
237 vk::PresentInfoKHR present_info {};
238 present_info.waitSemaphoreCount = 1;
239 present_info.pWaitSemaphores = &wait_semaphore;
240
241 present_info.swapchainCount = 1;
242 present_info.pSwapchains = &m_swapchain;
243 present_info.pImageIndices = &image_index;
244 present_info.pResults = nullptr;
245
246 try {
247 auto result = queue.presentKHR(present_info);
248
249 if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR) {
250 return false;
251 }
252
253 if (result != vk::Result::eSuccess) {
255 "Failed to present swapchain image");
256 return false;
257 }
258
259 return true;
260
261 } catch (const vk::SystemError& e) {
263 "Exception presenting swapchain image: {}", e.what());
264 return false;
265 }
266}
267
269{
270 if (!m_context) {
272 "Cannot create image views: no context set");
273 return false;
274 }
275
276 vk::Device device = m_context->get_device();
277 m_image_views.resize(m_images.size());
278
279 for (size_t i = 0; i < m_images.size(); i++) {
280 vk::ImageViewCreateInfo create_info {};
281 create_info.image = m_images[i];
282 create_info.viewType = vk::ImageViewType::e2D;
283 create_info.format = m_image_format;
284
285 create_info.components.r = vk::ComponentSwizzle::eIdentity;
286 create_info.components.g = vk::ComponentSwizzle::eIdentity;
287 create_info.components.b = vk::ComponentSwizzle::eIdentity;
288 create_info.components.a = vk::ComponentSwizzle::eIdentity;
289
290 create_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
291 create_info.subresourceRange.baseMipLevel = 0;
292 create_info.subresourceRange.levelCount = 1;
293 create_info.subresourceRange.baseArrayLayer = 0;
294 create_info.subresourceRange.layerCount = 1;
295
296 try {
297 m_image_views[i] = device.createImageView(create_info);
298 } catch (const vk::SystemError& e) {
300 "Failed to create image view {}: {}", i, e.what());
301 return false;
302 }
303 }
304
305 return true;
306}
307
309 vk::PhysicalDevice physical_device,
310 vk::SurfaceKHR surface)
311{
313
314 details.capabilities = physical_device.getSurfaceCapabilitiesKHR(surface);
315 details.formats = physical_device.getSurfaceFormatsKHR(surface);
316 details.present_modes = physical_device.getSurfacePresentModesKHR(surface);
317
318 return details;
319}
320
322 const std::vector<vk::SurfaceFormatKHR>& available_formats,
324 GraphicsSurfaceInfo::ColorSpace desired_color_space) const
325{
326 vk::Format vk_format = to_vk_format(desired_format);
327 vk::ColorSpaceKHR vk_color_space = to_vk_color_space(desired_color_space);
328
329 for (const auto& format : available_formats) {
330 if (format.format == vk_format && format.colorSpace == vk_color_space) {
331 return format;
332 }
333 }
334
335 for (const auto& format : available_formats) {
336 if (format.format == vk_format) {
338 "Exact color space match not found, using format match with different color space");
339 return format;
340 }
341 }
342
344 "Desired format not available, falling back to: {}",
345 vk::to_string(available_formats[0].format));
346 return available_formats[0];
347}
348
350 const std::vector<vk::PresentModeKHR>& available_modes,
351 GraphicsSurfaceInfo::PresentMode desired_mode) const
352{
353 vk::PresentModeKHR vk_mode = to_vk_present_mode(desired_mode);
354
355 for (const auto& mode : available_modes) {
356 if (mode == vk_mode) {
357 return mode;
358 }
359 }
360
362 for (const auto& mode : available_modes) {
363 if (mode == vk::PresentModeKHR::eMailbox) {
365 "Desired present mode not available, using MAILBOX");
366 return mode;
367 }
368 }
369 for (const auto& mode : available_modes) {
370 if (mode == vk::PresentModeKHR::eImmediate) {
372 "Desired present mode not available, using IMMEDIATE");
373 return mode;
374 }
375 }
376 }
377
379 "Desired present mode not available, falling back to FIFO (VSync)");
380 return vk::PresentModeKHR::eFifo;
381}
382
383std::optional<vk::SurfaceFormatKHR> VKSwapchain::find_hdr_format(
384 const std::vector<vk::SurfaceFormatKHR>& available_formats) const
385{
386 vk::SurfaceFormatKHR selected_format;
387 std::vector<std::pair<vk::Format, vk::ColorSpaceKHR>> hdr_candidates = {
388 { vk::Format::eR16G16B16A16Sfloat, vk::ColorSpaceKHR::eHdr10St2084EXT },
389 { vk::Format::eR16G16B16A16Sfloat, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT },
390 { vk::Format::eA2B10G10R10UnormPack32, vk::ColorSpaceKHR::eHdr10St2084EXT },
391 { vk::Format::eA2B10G10R10UnormPack32, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT },
392 };
393
394 bool hdr_supported = false;
395 for (const auto& candidate : hdr_candidates) {
396 for (const auto& fmt : available_formats) {
397 if (fmt.format == candidate.first && fmt.colorSpace == candidate.second) {
398 selected_format = fmt;
399 hdr_supported = true;
400 break;
401 }
402 }
403 if (hdr_supported) {
404 return selected_format;
405 }
406 }
407 return std::nullopt;
408}
409
411 const vk::SurfaceCapabilitiesKHR& capabilities,
412 uint32_t width,
413 uint32_t height) const
414{
415 if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
416 return capabilities.currentExtent;
417 }
418 vk::Extent2D actual_extent = { width, height };
419
420 actual_extent.width = std::clamp(
421 actual_extent.width,
422 capabilities.minImageExtent.width,
423 capabilities.maxImageExtent.width);
424
425 actual_extent.height = std::clamp(
426 actual_extent.height,
427 capabilities.minImageExtent.height,
428 capabilities.maxImageExtent.height);
429
430 return actual_extent;
431}
432
433} // namespace MayaFlux::Core
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
uint32_t width
Definition Decoder.cpp:59
vk::Device get_device() const
Get logical device.
Definition VKContext.hpp:49
const GraphicsSurfaceInfo & get_surface_info() const
Get graphics surface info.
vk::Queue get_graphics_queue() const
Get graphics queue.
Definition VKContext.hpp:54
vk::PhysicalDevice get_physical_device() const
Get physical device.
Definition VKContext.hpp:44
const QueueFamilyIndices & get_queue_families() const
Get queue family indices.
Definition VKContext.hpp:69
High-level wrapper for Vulkan instance and device.
Definition VKContext.hpp:16
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.
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.
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
bool create(VKContext &context, vk::SurfaceKHR surface, const WindowCreateInfo &window_config)
Create swapchain for the given surface using VKContext.
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.
SurfaceFormat format
Default surface format for new windows.
System-wide configuration for visual stream processing.
std::optional< uint32_t > graphics_family
Definition VKDevice.hpp:14
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.