Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
Swapchain.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <stdexcept>
6#include <string>
7
8#include <GLFW/glfw3.h>
9
11
12namespace mnd::vk
13{
14namespace
15{
16void Check(VkResult result, const char *what)
17{
18 if (result != VK_SUCCESS)
19 {
20 throw std::runtime_error(std::string("vk swapchain: ") + what + " failed (VkResult=" +
21 std::to_string(static_cast<int>(result)) + ")");
22 }
23}
24
25VkSurfaceFormatKHR PickFormat(const std::vector<VkSurfaceFormatKHR> &available)
26{
27 for (const auto &f : available)
28 {
29 if (f.format == VK_FORMAT_B8G8R8A8_SRGB &&
30 f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
31 {
32 return f;
33 }
34 }
35 return available.front();
36}
37
38VkPresentModeKHR PickPresentMode(const std::vector<VkPresentModeKHR> &available)
39{
40 for (auto m : available)
41 {
42 if (m == VK_PRESENT_MODE_MAILBOX_KHR) return m;
43 }
44 return VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be available
45}
46
47VkExtent2D PickExtent(const VkSurfaceCapabilitiesKHR &caps, GLFWwindow *window)
48{
49 if (caps.currentExtent.width != UINT32_MAX)
50 {
51 return caps.currentExtent;
52 }
53 int w = 0;
54 int h = 0;
55 glfwGetFramebufferSize(window, &w, &h);
56 VkExtent2D actual = {static_cast<uint32_t>(w), static_cast<uint32_t>(h)};
57 actual.width = std::clamp(actual.width, caps.minImageExtent.width, caps.maxImageExtent.width);
58 actual.height = std::clamp(actual.height, caps.minImageExtent.height, caps.maxImageExtent.height);
59 return actual;
60}
61} // namespace
62
63Swapchain::Swapchain(Context &ctx, GLFWwindow *window) : m_ctx(ctx), m_window(window)
64{
65 Build();
66}
67
69{
70 Cleanup();
71}
72
74{
75 // Block while the window is minimised — Vulkan rejects 0x0 swapchains.
76 int w = 0;
77 int h = 0;
78 glfwGetFramebufferSize(m_window, &w, &h);
79 while (w == 0 || h == 0)
80 {
81 glfwWaitEvents();
82 glfwGetFramebufferSize(m_window, &w, &h);
83 }
84 vkDeviceWaitIdle(m_ctx.GetDevice());
85 Cleanup();
86 Build();
87}
88
89void Swapchain::Build()
90{
91 auto phys = m_ctx.GetPhysicalDevice();
92 auto surface = m_ctx.GetSurface();
93
94 VkSurfaceCapabilitiesKHR caps;
95 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys, surface, &caps);
96
97 uint32_t formatCount = 0;
98 vkGetPhysicalDeviceSurfaceFormatsKHR(phys, surface, &formatCount, nullptr);
99 std::vector<VkSurfaceFormatKHR> formats(formatCount);
100 vkGetPhysicalDeviceSurfaceFormatsKHR(phys, surface, &formatCount, formats.data());
101
102 uint32_t presentModeCount = 0;
103 vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &presentModeCount, nullptr);
104 std::vector<VkPresentModeKHR> presentModes(presentModeCount);
105 vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &presentModeCount, presentModes.data());
106
107 VkSurfaceFormatKHR fmt = PickFormat(formats);
108 VkPresentModeKHR pm = PickPresentMode(presentModes);
109 VkExtent2D extent = PickExtent(caps, m_window);
110
111 uint32_t imageCount = caps.minImageCount + 1;
112 if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount)
113 {
114 imageCount = caps.maxImageCount;
115 }
116
117 VkSwapchainCreateInfoKHR info{};
118 info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
119 info.surface = surface;
120 info.minImageCount = imageCount;
121 info.imageFormat = fmt.format;
122 info.imageColorSpace = fmt.colorSpace;
123 info.imageExtent = extent;
124 info.imageArrayLayers = 1;
125 info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
126 info.preTransform = caps.currentTransform;
127 info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
128 info.presentMode = pm;
129 info.clipped = VK_TRUE;
130 info.oldSwapchain = VK_NULL_HANDLE;
131
132 auto fams = m_ctx.GetQueueFamilies();
133 uint32_t indices[] = { fams.graphics, fams.present };
134 if (fams.graphics != fams.present)
135 {
136 info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
137 info.queueFamilyIndexCount = 2;
138 info.pQueueFamilyIndices = indices;
139 }
140 else
141 {
142 info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
143 }
144
145 Check(vkCreateSwapchainKHR(m_ctx.GetDevice(), &info, nullptr, &m_swapchain),
146 "vkCreateSwapchainKHR");
147
148 m_format = fmt.format;
149 m_extent = extent;
150
151 uint32_t actualCount = 0;
152 vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, nullptr);
153 m_images.resize(actualCount);
154 vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, m_images.data());
155
156 // Per-image views.
157 m_imageViews.resize(m_images.size());
158 for (size_t i = 0; i < m_images.size(); ++i)
159 {
160 VkImageViewCreateInfo iv{};
161 iv.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
162 iv.image = m_images[i];
163 iv.viewType = VK_IMAGE_VIEW_TYPE_2D;
164 iv.format = m_format;
165 iv.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
166 iv.subresourceRange.baseMipLevel = 0;
167 iv.subresourceRange.levelCount = 1;
168 iv.subresourceRange.baseArrayLayer = 0;
169 iv.subresourceRange.layerCount = 1;
170 Check(vkCreateImageView(m_ctx.GetDevice(), &iv, nullptr, &m_imageViews[i]),
171 "vkCreateImageView");
172 }
173
174 // Render pass — single colour attachment, clear-load, present-layout-store.
175 VkAttachmentDescription colour{};
176 colour.format = m_format;
177 colour.samples = VK_SAMPLE_COUNT_1_BIT;
178 colour.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
179 colour.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
180 colour.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
181 colour.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
182 colour.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
183 colour.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
184
185 VkAttachmentReference colourRef{};
186 colourRef.attachment = 0;
187 colourRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
188
189 VkSubpassDescription subpass{};
190 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
191 subpass.colorAttachmentCount = 1;
192 subpass.pColorAttachments = &colourRef;
193
194 VkSubpassDependency dep{};
195 dep.srcSubpass = VK_SUBPASS_EXTERNAL;
196 dep.dstSubpass = 0;
197 dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
198 dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
199 dep.srcAccessMask = 0;
200 dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
201
202 VkRenderPassCreateInfo rp{};
203 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
204 rp.attachmentCount = 1;
205 rp.pAttachments = &colour;
206 rp.subpassCount = 1;
207 rp.pSubpasses = &subpass;
208 rp.dependencyCount = 1;
209 rp.pDependencies = &dep;
210 Check(vkCreateRenderPass(m_ctx.GetDevice(), &rp, nullptr, &m_renderPass),
211 "vkCreateRenderPass");
212
213 // Framebuffer per swapchain image view.
214 m_framebuffers.resize(m_imageViews.size());
215 for (size_t i = 0; i < m_imageViews.size(); ++i)
216 {
217 VkFramebufferCreateInfo fbi{};
218 fbi.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
219 fbi.renderPass = m_renderPass;
220 fbi.attachmentCount = 1;
221 fbi.pAttachments = &m_imageViews[i];
222 fbi.width = m_extent.width;
223 fbi.height = m_extent.height;
224 fbi.layers = 1;
225 Check(vkCreateFramebuffer(m_ctx.GetDevice(), &fbi, nullptr, &m_framebuffers[i]),
226 "vkCreateFramebuffer");
227 }
228}
229
230void Swapchain::Cleanup()
231{
232 auto device = m_ctx.GetDevice();
233 for (auto fb : m_framebuffers) vkDestroyFramebuffer(device, fb, nullptr);
234 m_framebuffers.clear();
235 if (m_renderPass) { vkDestroyRenderPass(device, m_renderPass, nullptr); m_renderPass = VK_NULL_HANDLE; }
236 for (auto v : m_imageViews) vkDestroyImageView(device, v, nullptr);
237 m_imageViews.clear();
238 if (m_swapchain) { vkDestroySwapchainKHR(device, m_swapchain, nullptr); m_swapchain = VK_NULL_HANDLE; }
239 m_images.clear();
240}
241
242} // namespace mnd::vk
Vulkan instance + physical/logical device + queue selection.
VkSwapchain + per-image views, render pass, framebuffers.
Owns the Vulkan instance, surface (tied to a GLFW window), physical + logical device,...
Definition Context.h:41
VkPhysicalDevice GetPhysicalDevice() const
Definition Context.h:55
QueueFamilies GetQueueFamilies() const
Definition Context.h:63
VkDevice GetDevice() const
Definition Context.h:57
VkSurfaceKHR GetSurface() const
Definition Context.h:53
Swapchain(Context &ctx, GLFWwindow *window)
Definition Swapchain.cpp:63