diff --git a/src/graphics/device.cpp b/src/graphics/device.cpp index 85682e2..6defaee 100644 --- a/src/graphics/device.cpp +++ b/src/graphics/device.cpp @@ -6,27 +6,10 @@ #include "yolo/yolo.hpp" #include -#include #include namespace inferno::graphics { -struct QueueFamilyIndices { - std::optional graphicsFamily; - std::optional presentFamily; - - bool isComplete() - { - return graphicsFamily.has_value() && presentFamily.has_value(); - } -}; - -struct SwapChainSupportDetails { - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; -}; - static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -112,6 +95,11 @@ std::vector getRequiredExtensions() extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } + yolo::info("Requested instance extensions:"); + for (const auto& extension : extensions) { + yolo::info("\t{}", extension); + } + return extensions; } @@ -149,39 +137,6 @@ QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice return indices; } -bool device_evaluate(GraphicsDevice* g, VkPhysicalDevice device) -{ - int score = 0; - - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - - VkPhysicalDeviceFeatures deviceFeatures; - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - - yolo::info("Found device {}", deviceProperties.deviceName); - // Discrete GPUs have a significant performance advantage - if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - yolo::info("Device {} is a discrete GPU", deviceProperties.deviceName); - score += 1000; - } - // We really want to favour ones with RayTracing support - - // Maximum possible size of textures affects graphics quality - score += deviceProperties.limits.maxImageDimension2D; - - // Application won't function without geometry shaders - if (!deviceFeatures.geometryShader) - return 0; - - // Ensure that the device can process the graphics commands that we need - QueueFamilyIndices indices = device_get_queue_families(g, device); - if (!indices.isComplete()) - return 0; - - return score; -} - bool device_evaluate_extensions(VkPhysicalDevice device, std::vector extensions) { uint32_t extensionCount; @@ -199,39 +154,54 @@ bool device_evaluate_extensions(VkPhysicalDevice device, std::vectorVulkanSurface, &details.capabilities); + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(device, &deviceProperties); - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr); + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - if (formatCount != 0) { - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, details.formats.data()); + yolo::info("Found device {}", deviceProperties.deviceName); + // Discrete GPUs have a significant performance advantage + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + yolo::info("Device {} is a discrete GPU", deviceProperties.deviceName); + score += 1000; } - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, nullptr); - - if (presentModeCount != 0) { - details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, details.presentModes.data()); + // does the device support the extensions we need? + if (!device_evaluate_extensions(device, DEVICE_EXTENSIONS)) { + return 0; } - return details; + // Maximum possible size of textures affects graphics quality + score += deviceProperties.limits.maxImageDimension2D; + + // Application won't function without geometry shaders + if (!deviceFeatures.geometryShader) + return 0; + + // Ensure that the device can process the graphics commands that we need + QueueFamilyIndices indices = device_get_queue_families(g, device); + if (!indices.isComplete()) + return 0; + + return score; } + GraphicsDevice* device_create() { GraphicsDevice* device = new GraphicsDevice(); device_create_vulkan_instance(device); device_vulkan_debugger(device); window_set_surface(device); + device->SurfaceSize = window_get_size(); device_create_vulkan_physical_device(device); device_create_vulkan_logical_device(device); + device_create_command_pool(device); return device; } @@ -371,7 +341,12 @@ void device_create_vulkan_logical_device(GraphicsDevice* device) createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = 0; + createInfo.enabledExtensionCount = static_cast(DEVICE_EXTENSIONS.size()); + createInfo.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(); + yolo::info("Requested device extensions:"); + for (const auto& extension : DEVICE_EXTENSIONS) { + yolo::info("\t{}", extension); + } #ifdef VALIDATION_LAYERS_ENABLED createInfo.enabledLayerCount = static_cast(VALIDATION_LAYERS.size()); @@ -389,4 +364,19 @@ void device_create_vulkan_logical_device(GraphicsDevice* device) vkGetDeviceQueue(device->VulkanDevice, indices.presentFamily.value(), 0, &device->VulkanPresentQueue); } +void device_create_command_pool(GraphicsDevice* device) +{ + QueueFamilyIndices queueFamilyIndices = device_get_queue_families(device, device->VulkanPhysicalDevice); + + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + if (vkCreateCommandPool(device->VulkanDevice, &poolInfo, nullptr, &device->VulkanCommandPool) != VK_SUCCESS) { + yolo::error("failed to create command pool!"); + } + yolo::info("Vulkan command pool created"); +} + } diff --git a/src/graphics/device.hpp b/src/graphics/device.hpp index 3e5cb73..082d9a5 100644 --- a/src/graphics/device.hpp +++ b/src/graphics/device.hpp @@ -2,6 +2,8 @@ #include "graphics.hpp" +#include + namespace inferno::graphics { #define VALIDATION_LAYERS_ENABLED true @@ -11,6 +13,10 @@ const std::vector VALIDATION_LAYERS = { }; #endif +const std::vector DEVICE_EXTENSIONS = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, +}; + typedef struct GraphicsDevice { VkInstance VulkanInstance; VkDebugUtilsMessengerEXT VulkanDebugMessenger; @@ -19,9 +25,21 @@ typedef struct GraphicsDevice { VkSurfaceKHR VulkanSurface; VkQueue VulkanGraphicsQueue; VkQueue VulkanPresentQueue; - VkSwapchainKHR VulkanSwapChain; + VkCommandPool VulkanCommandPool; + + glm::ivec2 SurfaceSize; } GraphicsDevice; +struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() + { + return graphicsFamily.has_value() && presentFamily.has_value(); + } +}; + // MUST ONLY BE CALLED AFTER GLFW INIT GraphicsDevice* device_create(); void device_cleanup(GraphicsDevice* device); @@ -30,4 +48,8 @@ void device_create_vulkan_instance(GraphicsDevice* device); void device_vulkan_debugger(GraphicsDevice* device); void device_create_vulkan_physical_device(GraphicsDevice* device); void device_create_vulkan_logical_device(GraphicsDevice* device); +void device_create_command_pool(GraphicsDevice* device); + +QueueFamilyIndices device_get_queue_families(GraphicsDevice* g, VkPhysicalDevice device); + } diff --git a/src/graphics/pipeline.cpp b/src/graphics/pipeline.cpp index 96ce64d..0ce4ea9 100644 --- a/src/graphics/pipeline.cpp +++ b/src/graphics/pipeline.cpp @@ -1,6 +1,23 @@ #include "pipeline.hpp" +#include "device.hpp" +#include "swapchain.hpp" + namespace inferno::graphics { +Pipeline* pipeline_create(GraphicsDevice* device) +{ + Pipeline* pipeline = new Pipeline(); + + pipeline->Device = device; + pipeline->Swap = swapchain_create(device, device->SurfaceSize); + + return pipeline; } +void pipeline_cleanup(Pipeline* pipeline) +{ + delete pipeline; +} + +} // namespace inferno::graphics diff --git a/src/graphics/pipeline.hpp b/src/graphics/pipeline.hpp index c4416b1..0d15eb3 100644 --- a/src/graphics/pipeline.hpp +++ b/src/graphics/pipeline.hpp @@ -5,12 +5,14 @@ namespace inferno::graphics { struct GraphicsDevice; +struct SwapChain; typedef struct Pipeline { - VkPipelineLayout Layout; - VkPipeline Pipeline; - GraphicsDevice* Device; + SwapChain* Swap; } Pipeline; +Pipeline* pipeline_create(GraphicsDevice* device); +void pipeline_cleanup(Pipeline* pipeline); + } diff --git a/src/graphics/swapchain.cpp b/src/graphics/swapchain.cpp index e69de29..bf92ba6 100644 --- a/src/graphics/swapchain.cpp +++ b/src/graphics/swapchain.cpp @@ -0,0 +1,150 @@ +#include "swapchain.hpp" + +#include "device.hpp" + +#include "yolo/yolo.hpp" + +namespace inferno::graphics { + +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR Capabilities; + std::vector Formats; + std::vector PresentModes; +}; + +SwapChainSupportDetails device_get_swapchain_support(GraphicsDevice* g, VkPhysicalDevice device) +{ + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, g->VulkanSurface, &details.Capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr); + + if (formatCount != 0) { + details.Formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, details.Formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.PresentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, details.PresentModes.data()); + } + + return details; +} + +VkSurfaceFormatKHR device_choose_swap_surface_format(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 device_choose_swap_present_mode(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 device_choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities, int width, int height) +{ + if (capabilities.currentExtent.width != UINT32_MAX) { + return capabilities.currentExtent; + } else { + VkExtent2D actualExtent = { (uint32_t)width, (uint32_t)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; + } +} + +SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size) +{ + SwapChain* swapchain = new SwapChain(); + + SwapChainSupportDetails swapChainSupport = device_get_swapchain_support(device, device->VulkanPhysicalDevice); + + VkSurfaceFormatKHR surfaceFormat = device_choose_swap_surface_format(swapChainSupport.Formats); + VkPresentModeKHR presentMode = device_choose_swap_present_mode(swapChainSupport.PresentModes); + VkExtent2D extent = device_choose_swap_extent(swapChainSupport.Capabilities, surface_size.x, surface_size.y); + 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 = device->VulkanSurface; + 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 = device_get_queue_families(device, device->VulkanPhysicalDevice); + 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; + + if (vkCreateSwapchainKHR(device->VulkanDevice, &createInfo, nullptr, &swapchain->Handle) != VK_SUCCESS) { + yolo::error("failed to create swap chain!"); + exit(1); + } + yolo::info("Swap chain created"); + + vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, nullptr); + swapchain->Images.resize(imageCount); + vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, swapchain->Images.data()); + + swapchain->ImageFormat = surfaceFormat.format; + swapchain->Extent = extent; + swapchain->Device = device; + + return swapchain; +} + +void swapchain_cleanup(SwapChain* swapchain) +{ + vkDestroySwapchainKHR(swapchain->Device->VulkanDevice, swapchain->Handle, nullptr); + delete swapchain; +} + +} diff --git a/src/graphics/swapchain.hpp b/src/graphics/swapchain.hpp index e69de29..1e8e9c7 100644 --- a/src/graphics/swapchain.hpp +++ b/src/graphics/swapchain.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "graphics.hpp" + +namespace inferno::graphics { + +struct GraphicsDevice; + +typedef struct SwapChain { + VkSwapchainKHR Handle; + VkFormat ImageFormat; + VkExtent2D Extent; + + std::vector Images; + + GraphicsDevice* Device; +} SwapChain; + +SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size); +void swapchain_cleanup(SwapChain* swapchain); + +} diff --git a/src/inferno.cpp b/src/inferno.cpp index 4b7815c..a3f2041 100644 --- a/src/inferno.cpp +++ b/src/inferno.cpp @@ -93,8 +93,8 @@ InfernoApp* inferno_create() // Create window graphics::window_create("Inferno v" INFERNO_VERSION, 1920, 1080); - graphics::GraphicsDevice* d = graphics::device_create(); - + graphics::GraphicsDevice* device = graphics::device_create(); + graphics::Pipeline* p = graphics::pipeline_create(device); // // setup the scene // scene::Material* basicMaterial = new scene::Material("basic");