diff --git a/Hello_triangle/main.cpp b/Hello_triangle/main.cpp index b81a8a9..aca40e7 100644 --- a/Hello_triangle/main.cpp +++ b/Hello_triangle/main.cpp @@ -1,6 +1,6 @@ /* Following the vulkan tutorial on : - https://vulkan-tutorial.com/en/Drawing_a_triangle/Presentation/Window_surface + https://vulkan-tutorial.com/en/Drawing_a_triangle/Presentation/Swap_chain */ #define GLFW_INCLUDE_VULKAN @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -21,6 +24,10 @@ const std::vector validationLayers ={ "VK_LAYER_KHRONOS_validation" }; +const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + #ifdef NDEBUG const bool enableValidationLayers = false; # else @@ -29,9 +36,9 @@ const std::vector validationLayers ={ -class HelloTriangleApplication{ - public : - void run (){ + class HelloTriangleApplication { + public: + void run() { initWindow(); initVulkan(); mainLoop(); @@ -47,8 +54,19 @@ class HelloTriangleApplication{ VkDevice device; VkQueue graphicsQueue; VkQueue presentQueue; + VkSwapchainKHR swapChain; + std::vector swapChainImages; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; - void initWindow(){ + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + + void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -57,12 +75,13 @@ class HelloTriangleApplication{ window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } - void initVulkan(){ + void initVulkan() { createInstance(); setupDebugMessenger(); createSurface(); pickPhysicalDevice(); createLogicalDevice(); + createSwapChain(); } @@ -73,7 +92,7 @@ class HelloTriangleApplication{ } - void createLogicalDevice(){ + void createLogicalDevice() { QueueFamilyIndices indices = findQueueFamilies(physicalDevice); @@ -83,89 +102,162 @@ class HelloTriangleApplication{ float queuePriority = 1.0f; for (uint32_t queueFamily : uniqueQueueFamilies) { VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + 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.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = 0; - if(enableValidationLayers){ + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); - } else{ - createInfo.enabledLayerCount = 0 ; + } + else { + createInfo.enabledLayerCount = 0; } - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS){ + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device"); } - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0 , &graphicsQueue); + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); } - void pickPhysicalDevice(){ + + void createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + 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; + createInfo.pQueueFamilyIndices = nullptr; + } + + + 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; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain)!= VK_SUCCESS ) { + throw std::runtime_error("failed to create swap chain!"); + } + + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; + + } + + void pickPhysicalDevice() { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - - if(deviceCount == 0 ){ + + if (deviceCount == 0) { throw std::runtime_error("failed to find GPU's with Vulkan support!"); } - + std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - for(const auto& device : devices){ - if(isDeviceSuitable(device)){ + for (const auto& device : devices) { + if (isDeviceSuitable(device)) { physicalDevice = device; break; } } - if(physicalDevice == VK_NULL_HANDLE){ + if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("failed to find a suitable GPU"); } } - bool isDeviceSuitable(VkPhysicalDevice device){ + bool isDeviceSuitable(VkPhysicalDevice device) { + + QueueFamilyIndices indices = findQueueFamilies(device); + bool extensionSupported = checkDeviceExtensionSupport(device); + VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(device, &deviceProperties); VkPhysicalDeviceFeatures deviceFeatures; vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + bool swapChainAdequate = false; + if (extensionSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } - QueueFamilyIndices indices = findQueueFamilies(device); - return indices.isComplete(); + return indices.isComplete() && extensionSupported && swapChainAdequate; } struct QueueFamilyIndices { std::optional graphicsFamily; std::optional presentFamily; - bool isComplete(){ + bool isComplete() { return graphicsFamily.has_value() && presentFamily.has_value(); } }; - QueueFamilyIndices findQueueFamilies (VkPhysicalDevice device ){ + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; @@ -177,7 +269,7 @@ class HelloTriangleApplication{ int i = 0; - for (const auto& queueFamily : queueFamilies){ + for (const auto& queueFamily : queueFamilies) { VkBool32 presentSupport = false; @@ -188,11 +280,11 @@ class HelloTriangleApplication{ } - if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT){ + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } - if(indices.isComplete()){ + if (indices.isComplete()) { break; } @@ -202,17 +294,43 @@ class HelloTriangleApplication{ return indices; } + 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; + } + + VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { - auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - } else { + } + else { return VK_ERROR_EXTENSION_NOT_PRESENT; } } - void setupDebugMessenger(){ - if(!enableValidationLayers) return; + void setupDebugMessenger() { + if (!enableValidationLayers) return; VkDebugUtilsMessengerCreateInfoEXT createinfo; populateDebugMessengerCreateInfo(createinfo); @@ -222,48 +340,65 @@ class HelloTriangleApplication{ } } - bool checkValidationLayerSupport(){ - uint32_t layerCount ; + bool checkValidationLayerSupport() { + uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for( const char* layerName: validationLayers) { - bool layerFound = false; - for (const auto & layerProperties : availableLayers){ - if( strcmp(layerName, layerProperties.layerName) == 0){ - layerFound = true; - break; - } - } + for (const char* layerName : validationLayers) { + bool layerFound = false; - if(!layerFound ){ + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { return false; } - } + } - return true; + return true; } - std::vector getRequiredExtensions(){ + bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set < std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + + std::vector getRequiredExtensions() { uint32_t glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - std::vector extensions (glfwExtensions, glfwExtensions + glfwExtensionCount); + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); - if(enableValidationLayers){ + if (enableValidationLayers) { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } return extensions; - } + } - void createInstance(){ - if ( enableValidationLayers && !checkValidationLayerSupport()){ + void createInstance() { + if (enableValidationLayers && !checkValidationLayerSupport()) { throw std::runtime_error("Validation layer requested, but not available!"); } @@ -280,14 +415,15 @@ class HelloTriangleApplication{ createInfo.pApplicationInfo = &appInfo; VkDebugUtilsMessengerCreateInfoEXT debugcreateinfo{}; - if(enableValidationLayers){ + if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); populateDebugMessengerCreateInfo(debugcreateinfo); - createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugcreateinfo; + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugcreateinfo; - } else{ + } + else { createInfo.enabledLayerCount = 0; createInfo.pNext = nullptr; } @@ -296,18 +432,19 @@ class HelloTriangleApplication{ auto extensions = getRequiredExtensions(); createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); - - if(enableValidationLayers){ + + if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast (validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); - } else{ + } + else { createInfo.enabledLayerCount = 0; } - - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS ){ + + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("Failed to create instance!"); } @@ -315,6 +452,45 @@ class HelloTriangleApplication{ } + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes) { + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + 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.width, capabilities.minImageExtent.height, capabilities.minImageExtent.height); + + return actualExtent; + } + } + void mainLoop(){ while (!glfwWindowShouldClose(window)){ glfwPollEvents(); @@ -322,6 +498,8 @@ class HelloTriangleApplication{ } void cleanup(){ + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); if(enableValidationLayers){ DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);