diff --git a/Vulkan/main.cpp b/Vulkan/main.cpp index c90e615..03aa598 100644 --- a/Vulkan/main.cpp +++ b/Vulkan/main.cpp @@ -5,13 +5,19 @@ #include #include #include +#include #include +#include #define GLFW_INCLUDE_VULKAN #include const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; +const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + #ifdef NDEBUG const bool enableValidationLayers = false; @@ -19,14 +25,22 @@ const bool enableValidationLayers = false; const bool enableValidationLayers = true; #endif +const int WIDTH = 800; +const int HEIGHT = 600; + struct QueueFamilyIndices { std::optional graphicsFamily; + std::optional presentFamily; bool isComplete() { - return graphicsFamily.has_value(); + return graphicsFamily.has_value() && presentFamily.has_value(); } }; - +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; +}; @@ -67,20 +81,34 @@ private: VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device; + VkQueue graphicsQueue; + VkQueue presentQueue; + + VkSurfaceKHR surface; + //Vulkan Swap Chain variables + VkSwapchainKHR swapChain; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + std::vector swapChainImages; + + std::vector swapChainImageViews; + void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } void initVulkan() { createInstance(); setupDebugMessenger(); + createSurface(); pickPhysicalDevice(); createLogicalDevice(); + createSwapChain(); } @@ -91,11 +119,19 @@ private: } void cleanup() { + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device,swapChain,nullptr); + vkDestroyDevice(device, nullptr); if (enableValidationLayers) { DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); } - vkDestroyDevice(device,nullptr); + + vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); + glfwDestroyWindow(window); glfwTerminate(); @@ -158,6 +194,7 @@ private: if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); + populateDebugMessengerCreateInfo(debugCreateInfo); createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo; } @@ -209,6 +246,14 @@ private: return true; } + + void createSurface() { + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { + throw std::runtime_error("Error creating windows surface!"); + } + } + + void pickPhysicalDevice() { uint32_t deviceCount = 0; @@ -244,51 +289,85 @@ private: score += deviceProperties.limits.maxImageDimension2D; QueueFamilyIndices indices = findQueueFamilies(device); + bool swapChainAdequate = false; + bool extensionsSupported = checkDeviceExtensionSupport(device); + if (extensionsSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } + + if (!swapChainAdequate) return 0; if( !indices.isComplete()) return 0; if (!deviceFeatures.geometryShader) return 0; return score; } + bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount = 0; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + std::vector availableExtensions(extensionCount); + + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + return requiredExtensions.empty(); + } QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector queueFamilyProperties(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilyProperties.data()); + int i = 0; for (const auto& queueFamily : queueFamilyProperties) { if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (queueFamily.queueCount > 0 && presentSupport) { + indices.presentFamily = i; + } if (indices.isComplete()) { break; } - - i++; + } return indices; } void createLogicalDevice() { QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; - VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); - queueCreateInfo.queueCount = 1; 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.enabledLayerCount = 0; + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); @@ -300,8 +379,138 @@ private: throw std::runtime_error("Error creating logical device!"); } vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); } + void createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSpwapExtent(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.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.imageExtent = extent; + createInfo.minImageCount = imageCount; + 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.oldSwapchain = VK_NULL_HANDLE; + if (vkCreateSwapchainKHR(device,&createInfo,nullptr,&swapChain) !=VK_SUCCESS) { + throw std::runtime_error("Error creating swap chain!"); + } + + vkGetSwapchainImagesKHR(device,swapChain,&imageCount,nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + swapChainExtent = extent; + swapChainImageFormat = surfaceFormat.format; + + } + + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + uint32_t formatCount = 0; + 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; + } + + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + 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 chooseSpwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } + else { + VkExtent2D actualExtent = { WIDTH, HEIGHT }; + + actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); + + return actualExtent; + } + } + + void createImageView() { + 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!"); + } + } + } + + + + }; int main() {