From 54587d3bbb9ab661b2c327d04ccb021266e109ae Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 19 Sep 2022 18:33:56 +0100 Subject: [PATCH] More vulkan work --- include/gfx_device.hpp | 3 + src/gfx_device_vulkan.cpp | 244 ++++++++++++++++++++++++++++++-------- src/window.cpp | 14 +-- 3 files changed, 203 insertions(+), 58 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a24eb47..d64daea 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -13,6 +13,9 @@ namespace engine::gfx { struct ENGINE_API Device { Device(AppInfo appInfo, SDL_Window* window); + + Device(const Device&) = delete; + Device& operator=(const Device&) = delete; ~Device(); private: diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2de047e..6ba100c 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace engine::gfx { @@ -37,15 +38,151 @@ namespace engine::gfx { Impl(AppInfo appInfo, SDL_Window* window) { #ifdef NDEBUG - findAvailableLayers(m_layerInfo, false); + // release mode; don't use validation layer + m_layerInfo = std::make_unique(false); #else - findAvailableLayers(m_layerInfo, true); + // debug mode; use validation layer + m_layerInfo = std::make_unique(true); #endif - m_instance = std::make_shared(appInfo, m_layerInfo, getRequiredVulkanExtensions(window)); + m_instance = std::make_shared(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window)); m_debugMessenger = std::make_unique(m_instance); - } - ~Impl() - { + + // enumerate physical devices + uint32_t physDeviceCount = 0; + VkResult res; + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr); + assert(res == VK_SUCCESS); + if (physDeviceCount == 0) { + throw std::runtime_error("No GPU found with vulkan support!"); + } + std::vector physicalDevices(physDeviceCount); + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); + + // find suitable device + const std::vector requiredDeviceExtensions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + for (const auto& dev : physicalDevices) { + + uint32_t extensionCount; + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); + assert(res == VK_SUCCESS); + std::vector availableExtensions(extensionCount); + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); + assert(res == VK_SUCCESS); + + bool suitable = true; + + for (const auto& extToFind : requiredDeviceExtensions) { + + bool extFound = false; + + for (const auto& ext : availableExtensions) { + if (strcmp(extToFind, ext.extensionName) == 0) { + extFound = true; + } + } + + if (!extFound) { + suitable = false; + } + } + + if (suitable) { + physicalDevice = dev; + break; + } + + } + + if (physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("No suitable Vulkan physical device found"); + } + + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(physicalDevice, &devProps); + TRACE("Physical device to use: {}", devProps.deviceName); + + // queue families + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + + std::optional graphicsFamilyIndex; + std::optional transferFamilyIndex; + std::optional computeFamilyIndex; + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkQueueFamilyProperties family = queueFamilies[i]; + if (family.queueCount > 0) { + if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + TRACE("GRAPHICS:"); + graphicsFamilyIndex = i; + } + if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { + TRACE("TRANSFER:"); + transferFamilyIndex = i; + } + if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { + TRACE("COMPUTE:"); + computeFamilyIndex = i; + } + TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); + } + } + if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { + throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag"); + } + + std::vector queueCreateInfos{}; + + // use a set to filter out duplicate indices + std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; + float queuePriority = 1.0f; + for (uint32_t family : uniqueQueueFamilies) { + // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + TRACE("Creating queue from family {}", family); + VkDeviceQueueCreateInfo queueCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = family, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), + .pQueueCreateInfos = queueCreateInfos.data(), + // IGNORED: .enabledLayerCount + // IGNORED: .ppEnabledLayerNames + .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), + .ppEnabledExtensionNames = requiredDeviceExtensions.data(), + .pEnabledFeatures = nullptr, + }; + + VkDevice device; + + res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); + if (res != VK_SUCCESS) { + throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); + } + + volkLoadDevice(device); + + vkDestroyDevice(device, nullptr); + } private: @@ -54,37 +191,36 @@ namespace engine::gfx { struct LayerInfo { + LayerInfo(bool useValidation) + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data()); + assert(res == VK_SUCCESS); + + if (useValidation == true) { + // find validation layer and print all layers to log + for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) { + if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { + validationLayer = it; + } + } + if (validationLayer.has_value() == false) { + throw std::runtime_error("The validation layer was not found. Quitting."); + } + } + } + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; std::vector layersAvailable{}; std::optional::iterator> validationLayer; - } m_layerInfo; - - static void findAvailableLayers(LayerInfo& layerInfo, bool useValidation) - { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - layerInfo.layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, layerInfo.layersAvailable.data()); - assert(res == VK_SUCCESS); - - if (useValidation == true) { - // find validation layer and print all layers to log - for (auto it = layerInfo.layersAvailable.begin(); it != layerInfo.layersAvailable.end(); it++) { - if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { - layerInfo.validationLayer = it; - } - } - if (layerInfo.validationLayer.has_value() == false) { - CRITICAL("The validation layer was not found. Quitting."); - throw std::runtime_error("Validation layer not found"); - } - } - } + }; class Instance { @@ -93,9 +229,9 @@ namespace engine::gfx { { VkResult res; - int appVersionMajor, appVersionMinor, appVersionPatch; + int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor, engineVersionMinor, engineVersionPatch; + int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); VkApplicationInfo applicationInfo{ @@ -112,11 +248,13 @@ namespace engine::gfx { std::vector extensions{}; extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + // also use debug utils extension + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + std::vector layers{}; if (layerInfo.validationLayer.has_value()) { layers.push_back(layerInfo.validationLayer.value()->layerName); - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } VkInstanceCreateInfo instanceInfo{ @@ -144,20 +282,19 @@ namespace engine::gfx { res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - CRITICAL("The graphics driver is incompatible with vulkan"); - throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + throw std::runtime_error("The graphics driver is incompatible with vulkan"); } assert(res == VK_SUCCESS); - volkLoadInstance(m_handle); + volkLoadInstanceOnly(m_handle); } + Instance(const Instance&) = delete; + Instance& operator=(const Instance&) = delete; ~Instance() { - INFO("DESTROYING INSTANCE..."); vkDestroyInstance(m_handle, nullptr); - INFO("DESTROYED INSTANCE."); } VkInstance getHandle() @@ -166,7 +303,6 @@ namespace engine::gfx { } private: - VkInstance m_handle; }; @@ -182,15 +318,13 @@ namespace engine::gfx { assert(res == VK_SUCCESS); } + DebugMessenger(const DebugMessenger&) = delete; + DebugMessenger& operator=(const DebugMessenger&) = delete; ~DebugMessenger() { - INFO("DESTROYING MESSENGER..."); vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); - INFO("DESTROYED MESSENGER."); } - static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() { VkDebugUtilsMessengerCreateInfoEXT createInfo{ @@ -207,16 +341,16 @@ namespace engine::gfx { }; switch (MESSAGE_LEVEL) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + case Severity::VERBOSE: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + case Severity::INFO: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + case Severity::WARNING: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + case Severity::ERROR: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; // fall-through default: @@ -229,6 +363,14 @@ namespace engine::gfx { private: VkDebugUtilsMessengerEXT m_messengerHandle; std::shared_ptr m_instance; + + enum class Severity { + VERBOSE, + INFO, + WARNING, + ERROR + }; + static constexpr Severity MESSAGE_LEVEL = Severity::WARNING; static VkBool32 messengerCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -267,6 +409,7 @@ namespace engine::gfx { }; + std::unique_ptr m_layerInfo; std::shared_ptr m_instance; std::unique_ptr m_debugMessenger; @@ -288,7 +431,6 @@ namespace engine::gfx { VkResult res; res = volkInitialize(); if (res == VK_ERROR_INITIALIZATION_FAILED) { - CRITICAL("Unable to load vulkan, is it installed?"); throw std::runtime_error("Unable to load vulkan, is it installed?"); } assert(res == VK_SUCCESS); @@ -304,4 +446,4 @@ namespace engine::gfx { } -#endif +#endif \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index 84c2b77..cbf92b1 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -89,8 +89,8 @@ void Window::resetInputDeltas() m_keyboard.deltas.fill(ButtonDelta::SAME); m_mouse.deltas.fill(ButtonDelta::SAME); - m_mouse.dx = 0.0f; - m_mouse.dy = 0.0f; + m_mouse.dx = 0; + m_mouse.dy = 0; m_mouse.xscroll = 0.0f; m_mouse.yscroll = 0.0f; } @@ -143,14 +143,14 @@ void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e) button = inputs::MouseButton::M_X2; break; } - - bool buttonWasDown = m_mouse.buttons[static_cast(button)]; + int buttonIndex = static_cast(button); + bool buttonWasDown = m_mouse.buttons.at(buttonIndex); bool buttonIsDown = (e.state == SDL_PRESSED); - m_mouse.buttons[static_cast(button)] = buttonIsDown; + m_mouse.buttons.at(buttonIndex) = buttonIsDown; if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) // only sets delta if it hasn't already been set this frame (to detect very fast presses) - if (m_mouse.deltas[static_cast(button)] == ButtonDelta::SAME) { - m_mouse.deltas[static_cast(button)] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) { + m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; } } }