evan 2026.1.4
Evan
Loading...
Searching...
No Matches
DesktopSwapchainImage.cpp
1/*
2** ETIB PROJECT, 2026
3** evan
4** File description:
5** DesktopSwapchainImage
6*/
7
8#include "evan/glfw/DesktopSwapchainImage.hpp"
9
10#include "evan/CheckedCast.hpp"
11
12#include <algorithm>
13#include <limits>
14
15#include "evan/DeviceContext.hpp"
16
18 const DeviceContext &deviceContext, GLFWwindow *window,
20{
21 this->getLogger().info() << "Initializing DesktopSwapchainImage...";
22
24 *deviceContext.getDeviceBackend());
25
26 this->getLogger().info()
27 << "Querying swap chain support details for DesktopSwapchainImage...";
29 backend.querySwapChainSupport();
30
32 this->chooseSwapSurfaceFormat(swapChainSupport.formats);
34 this->chooseSwapPresentMode(swapChainSupport.presentModes);
36 this->chooseSwapExtent(swapChainSupport.capabilities, window);
37
38 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
39 if (swapChainSupport.capabilities.maxImageCount > 0
40 && imageCount > swapChainSupport.capabilities.maxImageCount) {
41 this->getLogger().warning()
42 << "Desired image count exceeds maximum supported by the swap "
43 "chain capabilities. Clamping to maxImageCount.";
44 imageCount = swapChainSupport.capabilities.maxImageCount;
45 }
46
47 VkSwapchainCreateInfoKHR createInfo {};
49 createInfo.surface = backend.getSurface();
50
51 createInfo.minImageCount = imageCount;
52 createInfo.imageFormat = surfaceFormat.format;
53 createInfo.imageColorSpace = surfaceFormat.colorSpace;
54 createInfo.imageExtent = extent;
55 createInfo.imageArrayLayers = 1;
56 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
57
58 evan::QueueFamilyIndices indices = backend.findQueueFamilies();
59 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(),
60 indices.presentFamily.value() };
61
62 if (indices.graphicsFamily != indices.presentFamily) {
63 this->getLogger().info()
64 << "Graphics and present queue families are different. Using "
65 "concurrent sharing mode for swapchain images.";
66 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
67 createInfo.queueFamilyIndexCount = 2;
68 createInfo.pQueueFamilyIndices = queueFamilyIndices;
69 } else {
70 this->getLogger().info()
71 << "Graphics and present queue families are the same. Using "
72 "exclusive sharing mode for swapchain images.";
73 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
74 }
75
76 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
77 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
78 createInfo.presentMode = presentMode;
79 createInfo.clipped = VK_TRUE;
80
81 createInfo.oldSwapchain = oldSwapchain;
82
83 this->getLogger().info()
84 << "Creating swapchain with the following parameters:";
85 this->getLogger().info()
86 << "Image Count = " << imageCount
87 << ", Image Format = " << surfaceFormat.format
88 << ", Color Space = " << surfaceFormat.colorSpace
89 << ", Image Extent = (" + std::to_string(extent.width) + ", "
90 + std::to_string(extent.height) + ")"
91 << ", Image Usage = " << createInfo.imageUsage << ", Sharing Mode = "
92 << (createInfo.imageSharingMode == VK_SHARING_MODE_CONCURRENT
93 ? "Concurrent"
94 : "Exclusive")
95 << ", Pre Transform = " << createInfo.preTransform
96 << ", Composite Alpha = " << createInfo.compositeAlpha
97 << ", Present Mode = " << createInfo.presentMode
98 << ", Clipped = " << (createInfo.clipped ? "True" : "False")
99 << ", Old Swapchain = "
100 << (createInfo.oldSwapchain != VK_NULL_HANDLE ? "Valid Handle"
101 : "None");
102 if (createInfo.oldSwapchain != VK_NULL_HANDLE) {
103 this->getLogger().info()
104 << "Old swapchain handle provided. This indicates that the "
105 "swapchain is being recreated, likely due to a window resize or "
106 "similar event. The old swapchain will be automatically cleaned "
107 "up by the Vulkan implementation once the new swapchain is "
108 "created successfully.";
109 }
110
111 if (vkCreateSwapchainKHR(backend.getDevice(), &createInfo, nullptr,
112 &_swapchain)
113 != VK_SUCCESS) {
114 this->getLogger().error()
115 << "Failed to create swap chain for DesktopSwapchainImage!";
116 return;
117 }
118
119 this->getLogger().info()
120 << "Swapchain created successfully for DesktopSwapchainImage.";
121
122 _extent = extent;
123 _format = surfaceFormat.format;
124
125 const VkSampleCountFlagBits msaaSamples = deviceContext.getMsaaSamples();
126 const ColorAttachmentMode colorAttachmentMode =
127 desktopColorAttachmentMode(msaaSamples);
128
129 this->createImages(backend.getDevice(), _swapchain);
131 if (colorAttachmentMode == ColorAttachmentMode::ResolveToSwapchain) {
132 this->createColorResources(backend, msaaSamples);
133 }
134 this->createDepthResources(deviceContext, msaaSamples);
135 this->createFramebuffers(backend.getDevice(), renderpass,
136 colorAttachmentMode);
137}
138
139evan::DesktopSwapchainImage::~DesktopSwapchainImage()
140{
141 this->getLogger().info() << "Destroying DesktopSwapchainImage...";
142 this->cleanup();
143}
144
146// Public Methods //
148
150{
151 (void)device;
152 this->cleanup();
153}
154
156// Private Methods //
158
159void evan::DesktopSwapchainImage::cleanup()
160{
161 this->getLogger().info()
162 << "Destroying swapchain image and releasing associated resources for "
163 "DesktopSwapchainImage...";
164
165 if (_device == VK_NULL_HANDLE) {
166 return;
167 }
168
169 this->getLogger().info()
170 << "Destroying framebuffers for DesktopSwapchainImage...";
171 for (auto framebuffer: _framebuffers) {
172 if (framebuffer != VK_NULL_HANDLE) {
173 vkDestroyFramebuffer(_device, framebuffer, nullptr);
174 }
175 }
176 _framebuffers.clear();
177
178 this->getLogger().info()
179 << "Destroying image views for DesktopSwapchainImage...";
180 for (auto imageView: _imageViews) {
181 if (imageView != VK_NULL_HANDLE) {
182 vkDestroyImageView(_device, imageView, nullptr);
183 }
184 }
185 _imageViews.clear();
186
187 this->getLogger().info()
188 << "Destroying swapchain for DesktopSwapchainImage...";
189 if (_swapchain != VK_NULL_HANDLE) {
190 vkDestroySwapchainKHR(_device, _swapchain, nullptr);
191 _swapchain = VK_NULL_HANDLE;
192 }
193
194 this->getLogger().info()
195 << "Destroying color resources for DesktopSwapchainImage...";
196 if (_colorView != VK_NULL_HANDLE) {
197 vkDestroyImageView(_device, _colorView, nullptr);
198 _colorView = VK_NULL_HANDLE;
199 }
200 if (_colorImage != VK_NULL_HANDLE) {
201 vkDestroyImage(_device, _colorImage, nullptr);
202 _colorImage = VK_NULL_HANDLE;
203 }
204 if (_colorMemory != VK_NULL_HANDLE) {
205 vkFreeMemory(_device, _colorMemory, nullptr);
206 _colorMemory = VK_NULL_HANDLE;
207 }
208
209 this->getLogger().info()
210 << "Destroying depth resources for DesktopSwapchainImage...";
211 if (_depthView != VK_NULL_HANDLE) {
212 vkDestroyImageView(_device, _depthView, nullptr);
213 _depthView = VK_NULL_HANDLE;
214 }
215 if (_depthImage != VK_NULL_HANDLE) {
216 vkDestroyImage(_device, _depthImage, nullptr);
217 _depthImage = VK_NULL_HANDLE;
218 }
219 if (_depthMemory != VK_NULL_HANDLE) {
220 vkFreeMemory(_device, _depthMemory, nullptr);
221 _depthMemory = VK_NULL_HANDLE;
222 }
223}
224
227{
228 presentInfo.swapchainCount = 1;
229 presentInfo.pSwapchains = &_swapchain;
230}
231
233// Private Methods //
235
236VkSurfaceFormatKHR evan::DesktopSwapchainImage::chooseSwapSurfaceFormat(
237 const std::vector<VkSurfaceFormatKHR> &availableFormats)
238{
239 this->getLogger().info()
240 << "Choosing swap surface format for DesktopSwapchainImage...";
244 };
245
246 for (const auto &availableFormat: availableFormats) {
247 for (VkFormat preferredFormat: kPreferredSwapchainFormats) {
248 if (availableFormat.format == preferredFormat
249 && availableFormat.colorSpace
250 == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
251 this->getLogger().info()
252 << "Selected preferred swap surface format: Format = "
253 << availableFormat.format
254 << ", Color Space = " << availableFormat.colorSpace;
255 return availableFormat;
256 }
257 }
258 }
259 this->getLogger().info()
260 << "No preferred swap surface format found, using default format.";
261 return availableFormats[0];
262}
263
264VkPresentModeKHR evan::DesktopSwapchainImage::chooseSwapPresentMode(
265 const std::vector<VkPresentModeKHR> &availablePresentModes)
266{
267 this->getLogger().info()
268 << "Choosing swap present mode for DesktopSwapchainImage...";
269 // Prefer FIFO (vsync) so the loop is paced by presentation and does not
270 // spin at unbounded frame rates now that the engine no longer sleeps to
271 // cap the frame rate by default.
272 for (const auto &availablePresentMode: availablePresentModes) {
273 if (availablePresentMode == VK_PRESENT_MODE_FIFO_KHR) {
274 this->getLogger().info() << "Selected preferred swap present mode: "
275 "VK_PRESENT_MODE_FIFO_KHR";
276 return availablePresentMode;
277 }
278 }
279 this->getLogger().info()
280 << "No preferred swap present mode found, using default mode.";
281 return VK_PRESENT_MODE_FIFO_KHR;
282}
283
284VkExtent2D evan::DesktopSwapchainImage::chooseSwapExtent(
285 const VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow *window)
286{
287 this->getLogger().info()
288 << "Choosing swap extent for DesktopSwapchainImage...";
289 int width = 0;
290 int height = 0;
291
292 if (capabilities.currentExtent.width
293 != (std::numeric_limits<uint32_t>::max)()) {
294 this->getLogger().info() << "Current extent is defined by the surface "
295 "capabilities. Using current extent: ("
296 + std::to_string(capabilities.currentExtent.width) + ", "
297 + std::to_string(capabilities.currentExtent.height) + ")";
298 return capabilities.currentExtent;
299 }
300
301 this->getLogger().info() << "Current extent is undefined. Querying "
302 "framebuffer size from GLFW window...";
303 glfwGetFramebufferSize(window, &width, &height);
304
305 VkExtent2D actualExtent = { static_cast<uint32_t>(width),
306 static_cast<uint32_t>(height) };
307
308 this->getLogger().info() << "Queried framebuffer size: ("
309 + std::to_string(actualExtent.width) + ", "
310 + std::to_string(actualExtent.height)
311 + "). Clamping to allowed extent range defined by surface "
312 "capabilities...";
313
314 actualExtent.width =
315 std::clamp(actualExtent.width, capabilities.minImageExtent.width,
316 capabilities.maxImageExtent.width);
317 actualExtent.height =
318 std::clamp(actualExtent.height, capabilities.minImageExtent.height,
319 capabilities.maxImageExtent.height);
320
321 this->getLogger().info() << "Final swap extent after clamping: ("
322 + std::to_string(actualExtent.width) + ", "
323 + std::to_string(actualExtent.height) + ")";
324
325 return actualExtent;
326}
void createFramebuffers(VkDevice logicalDevice, VkRenderPass renderPass, ColorAttachmentMode mode)
Creates framebuffers for the swapchain images.
void createImageViews(const ADeviceBackend &deviceBackend)
Creates image views for the swapchain images.
void createColorResources(const ADeviceBackend &deviceBackend, VkSampleCountFlagBits msaaSamples)
Creates a multisampled color attachment and its image view.
void createDepthResources(const DeviceContext &deviceContext, VkSampleCountFlagBits msaaSamples)
Creates depth resources for the swapchain images.
void createImages(VkDevice logicalDevice, VkSwapchainKHR swapchain)
Creates the Vulkan images for the swapchain.
void destroy(VkDevice device) override
Destroys the swapchain image and releases associated resources.
void fillPresentInfo(VkPresentInfoKHR &presentInfo) const override
Fills the VkPresentInfoKHR structure with the necessary information for presenting the swapchain imag...
DesktopSwapchainImage(const DeviceContext &deviceContext, GLFWwindow *window, VkRenderPass renderpass, VkSwapchainKHR oldSwapchain=VK_NULL_HANDLE)
Constructs a DesktopSwapchainImage instance for a given device context, GLFW window,...
Represents the device context for the engine, managing Vulkan-related resources and operations.
VkSampleCountFlagBits getMsaaSamples() const
Retrieves the number of MSAA samples used by this device context.
std::shared_ptr< ADeviceBackend > getDeviceBackend() const
Retrieves a shared pointer to the ADeviceBackend instance associated with this device context.
A structure to represent Vulkan queue family indices.
Small result wrapper used by helpers that must return both a status and a value (e....
Definition Error.hpp:89
Struct to encapsulate details about swap chain support.