diff --git a/main.cpp b/main.cpp index aa6a783..798a263 100644 --- a/main.cpp +++ b/main.cpp @@ -50,6 +50,7 @@ private: findPhysicalDeviceQueueFamilies(); createLogicalDevice(); getQueues(); + createSwapChain(); } void mainLoop() { @@ -59,6 +60,7 @@ private: } void cleanup() { + vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); vkDestroyDevice(mDevice, nullptr); vkDestroySurfaceKHR(mInstance, mSurface, nullptr); vkDestroyInstance(mInstance, nullptr); @@ -283,6 +285,95 @@ private: } } + void createSwapChain() { + SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice); + + VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details); + VkPresentModeKHR presentMode = pickSwapChainPresentMode(details); + VkExtent2D extent2D = pickSwapChainExtent(details.capabilities); + + // +1 so will not have to wait for device to finish frame to query an image to render to + uint32_t imageCount = details.capabilities.minImageCount + 1; + + if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) { + imageCount = details.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = mSurface, + .minImageCount = imageCount, + .imageFormat = surfaceFormat.format, + .imageExtent = extent2D, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .preTransform = details.capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + uint32_t queueIndices[] = { mGraphicsQueueFamilyIndex, mPresentationQueueFamilyIndex }; + + if (mGraphicsQueueFamilyIndex != mPresentationQueueFamilyIndex) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain"); + } + + mSwapChainExtent = extent2D; + mSwapChainFormat = surfaceFormat.format; + + uint32_t createdImageCount = 0; + vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, nullptr); + mSwapChainImages.resize(createdImageCount); + vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, mSwapChainImages.data()); + } + + static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails& details) { + for (const auto& format : details.formats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) { + return format; + } + } + + return details.formats.front(); + } + + static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails& details) { + for (const auto& mode : details.presentModes) { + if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return mode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists + } + + VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + // if set by vulkan just keep it + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } + + int width, height; + glfwGetFramebufferSize(mWindow, &width, &height); + + VkExtent2D out = { (uint32_t) width, (uint32_t) height }; + + out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return out; + } + private: GLFWwindow* mWindow = nullptr; @@ -297,6 +388,11 @@ private: VkQueue mPresentQueue = VK_NULL_HANDLE; VkSurfaceKHR mSurface = VK_NULL_HANDLE; + + VkSwapchainKHR mSwapChain = VK_NULL_HANDLE; + std::vector mSwapChainImages; + VkFormat mSwapChainFormat {}; + VkExtent2D mSwapChainExtent {}; }; int main() {