16void Check(VkResult result,
const char *what)
18 if (result != VK_SUCCESS)
20 throw std::runtime_error(std::string(
"vk swapchain: ") + what +
" failed (VkResult=" +
21 std::to_string(
static_cast<int>(result)) +
")");
25VkSurfaceFormatKHR PickFormat(
const std::vector<VkSurfaceFormatKHR> &available)
27 for (
const auto &f : available)
29 if (f.format == VK_FORMAT_B8G8R8A8_SRGB &&
30 f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
35 return available.front();
38VkPresentModeKHR PickPresentMode(
const std::vector<VkPresentModeKHR> &available)
40 for (
auto m : available)
42 if (m == VK_PRESENT_MODE_MAILBOX_KHR)
return m;
44 return VK_PRESENT_MODE_FIFO_KHR;
47VkExtent2D PickExtent(
const VkSurfaceCapabilitiesKHR &caps, GLFWwindow *window)
49 if (caps.currentExtent.width != UINT32_MAX)
51 return caps.currentExtent;
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);
78 glfwGetFramebufferSize(m_window, &w, &h);
79 while (w == 0 || h == 0)
82 glfwGetFramebufferSize(m_window, &w, &h);
89void Swapchain::Build()
94 VkSurfaceCapabilitiesKHR caps;
95 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys, surface, &caps);
97 uint32_t formatCount = 0;
98 vkGetPhysicalDeviceSurfaceFormatsKHR(phys, surface, &formatCount,
nullptr);
99 std::vector<VkSurfaceFormatKHR> formats(formatCount);
100 vkGetPhysicalDeviceSurfaceFormatsKHR(phys, surface, &formatCount, formats.data());
102 uint32_t presentModeCount = 0;
103 vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &presentModeCount,
nullptr);
104 std::vector<VkPresentModeKHR> presentModes(presentModeCount);
105 vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &presentModeCount, presentModes.data());
107 VkSurfaceFormatKHR fmt = PickFormat(formats);
108 VkPresentModeKHR pm = PickPresentMode(presentModes);
109 VkExtent2D extent = PickExtent(caps, m_window);
111 uint32_t imageCount = caps.minImageCount + 1;
112 if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount)
114 imageCount = caps.maxImageCount;
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;
133 uint32_t indices[] = { fams.
graphics, fams.present };
134 if (fams.graphics != fams.present)
136 info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
137 info.queueFamilyIndexCount = 2;
138 info.pQueueFamilyIndices = indices;
142 info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
145 Check(vkCreateSwapchainKHR(m_ctx.
GetDevice(), &info,
nullptr, &m_swapchain),
146 "vkCreateSwapchainKHR");
148 m_format = fmt.format;
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());
157 m_imageViews.resize(m_images.size());
158 for (
size_t i = 0; i < m_images.size(); ++i)
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");
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;
185 VkAttachmentReference colourRef{};
186 colourRef.attachment = 0;
187 colourRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
189 VkSubpassDescription subpass{};
190 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
191 subpass.colorAttachmentCount = 1;
192 subpass.pColorAttachments = &colourRef;
194 VkSubpassDependency dep{};
195 dep.srcSubpass = VK_SUBPASS_EXTERNAL;
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;
202 VkRenderPassCreateInfo rp{};
203 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
204 rp.attachmentCount = 1;
205 rp.pAttachments = &colour;
207 rp.pSubpasses = &subpass;
208 rp.dependencyCount = 1;
209 rp.pDependencies = &dep;
210 Check(vkCreateRenderPass(m_ctx.
GetDevice(), &rp,
nullptr, &m_renderPass),
211 "vkCreateRenderPass");
214 m_framebuffers.resize(m_imageViews.size());
215 for (
size_t i = 0; i < m_imageViews.size(); ++i)
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;
225 Check(vkCreateFramebuffer(m_ctx.
GetDevice(), &fbi,
nullptr, &m_framebuffers[i]),
226 "vkCreateFramebuffer");
230void Swapchain::Cleanup()
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; }
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,...
VkPhysicalDevice GetPhysicalDevice() const
QueueFamilies GetQueueFamilies() const
VkDevice GetDevice() const
VkSurfaceKHR GetSurface() const
Swapchain(Context &ctx, GLFWwindow *window)