diff --git a/main.cpp b/main.cpp index 0ac51a0..abf6540 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" @@ -37,6 +41,16 @@ private: VkDevice device; VkQueue graphicsQueue; + VkQueue presentQueue; + + VkSurfaceKHR surface; + VkSwapchainKHR swapChain; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + + std::vector swapChainImages; + + std::vector swapChainImageViews; const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; @@ -51,35 +65,175 @@ private: void initVulkan() { createInstance(); + createSurface(); pickPhysicalDevice(); createLogicalDevice(); + createSwapChain(); + createImageViews(); + } + + void createSurface() { + VkResult result = glfwCreateWindowSurface(instance, window, nullptr, &surface); + + if (result != VK_SUCCESS) { + std::cout << "create surface error: " << result << std::endl; + throw std::runtime_error("failed to create window surface"); + } + } + + void createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + swapChainImageFormat = surfaceFormat.format; + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + swapChainExtent = extent; + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; // Optional + createInfo.pQueueFamilyIndices = nullptr; // Optional + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + VkResult result = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain); + if (result != VK_SUCCESS) { + std::cout << "create swap chain error: " << result << std::endl; + throw std::runtime_error("failed to create swap chain!"); + } + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + } + + void createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages[i]; + + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create image views!"); + } + } + } + + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& format : availableFormats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return format; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availableModes) { + for (const auto& mode : availableModes) { + if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return mode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } } void createLogicalDevice() { QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); - queueCreateInfo.queueCount = 1; + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()}; float queuePriority = 1.0f; - queueCreateInfo.pQueuePriorities = &queuePriority; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } VkPhysicalDeviceFeatures deviceFeatures{}; VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = &queueCreateInfo; - createInfo.queueCreateInfoCount = 1; + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pEnabledFeatures = &deviceFeatures; - /*createInfo.enabledExtensionCount = 1; - std::vector extensions(1); - extensions.emplace_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - createInfo.ppEnabledExtensionNames = extensions.data();*/ + createInfo.enabledExtensionCount = static_cast(requiredDeviceExtensions.size() + 1); + std::vector extensions(requiredDeviceExtensions.begin(), requiredDeviceExtensions.end()); + extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + createInfo.ppEnabledExtensionNames = extensions.data(); if (enableValidationLayers) { @@ -121,6 +275,10 @@ private: } } + const std::vector requiredDeviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + bool isDeviceSuitable(VkPhysicalDevice device) { /*VkPhysicalDeviceProperties props; vkGetPhysicalDeviceProperties(device, &props); @@ -129,16 +287,40 @@ private: vkGetPhysicalDeviceFeatures(device, &features); return props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && features.geometryShader;*/ + bool extensionsSupported = deviceHasRequiredExtensions(device); + + bool swapChainAdequate = false; + if (extensionsSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } QueueFamilyIndices indices = findQueueFamilies(device); - return indices.isComplete(); + return indices.isComplete() && extensionsSupported && swapChainAdequate; + } + + bool deviceHasRequiredExtensions(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(requiredDeviceExtensions.begin(), requiredDeviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); } struct QueueFamilyIndices { std::optional graphicsFamily; + std::optional presentFamily; bool isComplete() { - return graphicsFamily.has_value(); + return graphicsFamily.has_value() && presentFamily.has_value(); } }; @@ -157,6 +339,13 @@ private: indices.graphicsFamily = i; } + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (presentSupport) { + indices.presentFamily = i; + } + if (indices.isComplete()) { break; } @@ -167,6 +356,35 @@ private: return indices; } + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); @@ -273,7 +491,12 @@ private: } void cleanup() { + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window);