From 21c83950a5f6ee7b25743356ae47335061769155 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sat, 9 Mar 2019 10:20:14 +0100 Subject: [PATCH] - add vk_device and vk_listdevices that will allow selecting a different device on systems where this is desirable - clean up the VulkanDevice class --- .../vulkan/renderer/vk_renderbuffers.cpp | 2 +- src/rendering/vulkan/system/vk_builders.h | 2 +- src/rendering/vulkan/system/vk_device.cpp | 544 ++++++++++-------- src/rendering/vulkan/system/vk_device.h | 100 ++-- .../vulkan/system/vk_framebuffer.cpp | 30 +- src/rendering/vulkan/system/vk_swapchain.cpp | 10 +- 6 files changed, 387 insertions(+), 301 deletions(-) diff --git a/src/rendering/vulkan/renderer/vk_renderbuffers.cpp b/src/rendering/vulkan/renderer/vk_renderbuffers.cpp index 21235c617..36e355e51 100644 --- a/src/rendering/vulkan/renderer/vk_renderbuffers.cpp +++ b/src/rendering/vulkan/renderer/vk_renderbuffers.cpp @@ -18,7 +18,7 @@ VkRenderBuffers::~VkRenderBuffers() VkSampleCountFlagBits VkRenderBuffers::GetBestSampleCount() { auto fb = GetVulkanFrameBuffer(); - const auto &limits = fb->device->deviceProperties.limits; + const auto &limits = fb->device->PhysicalDevice.Properties.limits; VkSampleCountFlags deviceSampleCounts = limits.sampledImageColorSampleCounts & limits.sampledImageDepthSampleCounts & limits.sampledImageStencilSampleCounts; int requestedSamples = clamp((int)gl_multisample, 0, 64); diff --git a/src/rendering/vulkan/system/vk_builders.h b/src/rendering/vulkan/system/vk_builders.h index 5b9906723..9f2c55eac 100644 --- a/src/rendering/vulkan/system/vk_builders.h +++ b/src/rendering/vulkan/system/vk_builders.h @@ -370,7 +370,7 @@ inline void ImageBuilder::setUsage(VkImageUsageFlags usage, VmaMemoryUsage memor inline bool ImageBuilder::isFormatSupported(VulkanDevice *device) { VkImageFormatProperties properties = { }; - VkResult result = vkGetPhysicalDeviceImageFormatProperties(device->physicalDevice, imageInfo.format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags, &properties); + VkResult result = vkGetPhysicalDeviceImageFormatProperties(device->PhysicalDevice.Device, imageInfo.format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags, &properties); if (result != VK_SUCCESS) return false; if (imageInfo.extent.width > properties.maxExtent.width) return false; if (imageInfo.extent.height > properties.maxExtent.height) return false; diff --git a/src/rendering/vulkan/system/vk_device.cpp b/src/rendering/vulkan/system/vk_device.cpp index 0253c023a..1b0e6c5b9 100644 --- a/src/rendering/vulkan/system/vk_device.cpp +++ b/src/rendering/vulkan/system/vk_device.cpp @@ -37,6 +37,7 @@ #include "vk_swapchain.h" #include "vk_objects.h" #include "c_cvars.h" +#include "c_dispatch.h" #include "i_system.h" #include "version.h" #include "doomerrors.h" @@ -46,6 +47,10 @@ void I_GetVulkanDrawableSize(int *width, int *height); bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names); bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface); +// Physical device info +static std::vector AvailableDevices; +static std::vector SupportedDevices; + EXTERN_CVAR(Bool, vid_vsync); CUSTOM_CVAR(Bool, vk_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) @@ -53,45 +58,139 @@ CUSTOM_CVAR(Bool, vk_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINI Printf("This won't take effect until " GAMENAME " is restarted.\n"); } +CUSTOM_CVAR(Int, vk_device, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} + +CCMD(vk_listdevices) +{ + for (size_t i = 0; i < SupportedDevices.size(); i++) + { + Printf("#%d - %s\n", (int)i, SupportedDevices[i].device->Properties.deviceName); + } +} + VulkanDevice::VulkanDevice() { - if (volkInitialize() != VK_SUCCESS) - { - throw std::runtime_error("Unable to find Vulkan"); - } - auto iver = volkGetInstanceVersion(); - if (iver == 0) - { - throw std::runtime_error("Vulkan not supported"); - } - try { - createInstance(); - createSurface(); - selectPhysicalDevice(); - createDevice(); - createAllocator(); + InitVolk(); + CreateInstance(); + CreateSurface(); + + UsedDeviceFeatures.samplerAnisotropy = VK_TRUE; + UsedDeviceFeatures.shaderClipDistance = VK_TRUE; + UsedDeviceFeatures.fragmentStoresAndAtomics = VK_TRUE; + UsedDeviceFeatures.depthClamp = VK_TRUE; + UsedDeviceFeatures.shaderClipDistance = VK_TRUE; + + SelectPhysicalDevice(); + CreateDevice(); + CreateAllocator(); int width, height; I_GetVulkanDrawableSize(&width, &height); swapChain = std::make_unique(this, width, height, vid_vsync); - createSemaphores(); + CreateSemaphores(); } catch (...) { - releaseResources(); + ReleaseResources(); throw; } } VulkanDevice::~VulkanDevice() { - releaseResources(); + ReleaseResources(); } -void VulkanDevice::windowResized() +bool VulkanDevice::CheckFeatures(const VkPhysicalDeviceFeatures &f) +{ + return + f.samplerAnisotropy == VK_TRUE && + f.shaderClipDistance == VK_TRUE && + f.fragmentStoresAndAtomics == VK_TRUE && + f.depthClamp == VK_TRUE && + f.shaderClipDistance == VK_TRUE; +} + +void VulkanDevice::SelectPhysicalDevice() +{ + AvailableDevices = GetPhysicalDevices(instance); + + for (size_t idx = 0; idx < AvailableDevices.size(); idx++) + { + const auto &info = AvailableDevices[idx]; + + if (!CheckFeatures(info.Features)) + continue; + + VulkanCompatibleDevice dev; + dev.device = &AvailableDevices[idx]; + + int i = 0; + for (const auto& queueFamily : info.QueueFamilies) + { + // Only accept a decent GPU for now.. + VkQueueFlags gpuFlags = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT); + if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & gpuFlags) == gpuFlags) + { + dev.graphicsFamily = i; + dev.transferFamily = i; + } + + VkBool32 presentSupport = false; + VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.Device, i, surface, &presentSupport); + if (result == VK_SUCCESS && queueFamily.queueCount > 0 && presentSupport) + dev.presentFamily = i; + + i++; + } + + std::set requiredExtensionSearch(EnabledDeviceExtensions.begin(), EnabledDeviceExtensions.end()); + for (const auto &ext : info.Extensions) + requiredExtensionSearch.erase(ext.extensionName); + if (!requiredExtensionSearch.empty()) + continue; + + if (dev.graphicsFamily != -1 && dev.presentFamily != -1 && dev.transferFamily != -1) + { + SupportedDevices.push_back(dev); + } + } + + if (SupportedDevices.empty()) + throw std::runtime_error("No Vulkan device supports the minimum requirements of this application"); + + // The device order returned by Vulkan can be anything. Prefer discrete > integrated > virtual gpu > cpu > other + std::stable_sort(SupportedDevices.begin(), SupportedDevices.end(), [&](const auto &a, const auto b) { + + // Sort by GPU type first. This will ensure the "best" device is most likely to map to vk_device 0 + static const int typeSort[] = { 4, 1, 0, 2, 3 }; + int sortA = a.device->Properties.deviceType < 5 ? typeSort[a.device->Properties.deviceType] : (int)a.device->Properties.deviceType; + int sortB = b.device->Properties.deviceType < 5 ? typeSort[b.device->Properties.deviceType] : (int)b.device->Properties.deviceType; + if (sortA < sortB) + return true; + + // Then sort by the device's unique ID so that vk_device uses a consistent order + int sortUUID = memcmp(a.device->Properties.pipelineCacheUUID, b.device->Properties.pipelineCacheUUID, VK_UUID_SIZE); + return sortUUID < 0; + }); + + size_t selected = vk_device; + if (selected >= SupportedDevices.size()) + selected = 0; + + PhysicalDevice = *SupportedDevices[selected].device; + graphicsFamily = SupportedDevices[selected].graphicsFamily; + presentFamily = SupportedDevices[selected].presentFamily; + transferFamily = SupportedDevices[selected].transferFamily; +} + +void VulkanDevice::WindowResized() { int width, height; I_GetVulkanDrawableSize(&width, &height); @@ -100,20 +199,20 @@ void VulkanDevice::windowResized() swapChain = std::make_unique(this, width, height, vid_vsync); } -void VulkanDevice::waitPresent() +void VulkanDevice::WaitPresent() { vkWaitForFences(device, 1, &renderFinishedFence->fence, VK_TRUE, std::numeric_limits::max()); vkResetFences(device, 1, &renderFinishedFence->fence); } -void VulkanDevice::beginFrame() +void VulkanDevice::BeginFrame() { VkResult result = vkAcquireNextImageKHR(device, swapChain->swapChain, std::numeric_limits::max(), imageAvailableSemaphore->semaphore, VK_NULL_HANDLE, &presentImageIndex); if (result != VK_SUCCESS) throw std::runtime_error("Failed to acquire next image!"); } -void VulkanDevice::presentFrame() +void VulkanDevice::PresentFrame() { VkSemaphore waitSemaphores[] = { renderFinishedSemaphore->semaphore }; VkSwapchainKHR swapChains[] = { swapChain->swapChain }; @@ -129,7 +228,135 @@ void VulkanDevice::presentFrame() vkQueuePresentKHR(presentQueue, &presentInfo); } -VkBool32 VulkanDevice::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) +void VulkanDevice::CreateAllocator() +{ + VmaAllocatorCreateInfo allocinfo = {}; + // allocinfo.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; // To do: enable this for better performance + allocinfo.physicalDevice = PhysicalDevice.Device; + allocinfo.device = device; + allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024; + if (vmaCreateAllocator(&allocinfo, &allocator) != VK_SUCCESS) + throw std::runtime_error("Unable to create allocator"); +} + +void VulkanDevice::CreateSemaphores() +{ + imageAvailableSemaphore.reset(new VulkanSemaphore(this)); + renderFinishedSemaphore.reset(new VulkanSemaphore(this)); + renderFinishedFence.reset(new VulkanFence(this)); +} + +void VulkanDevice::CreateDevice() +{ + float queuePriority = 1.0f; + std::vector queueCreateInfos; + + std::set neededFamilies; + neededFamilies.insert(graphicsFamily); + neededFamilies.insert(presentFamily); + neededFamilies.insert(transferFamily); + + for (int index : neededFamilies) + { + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = index; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(); + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.pEnabledFeatures = &UsedDeviceFeatures; + deviceCreateInfo.enabledExtensionCount = (uint32_t)EnabledDeviceExtensions.size(); + deviceCreateInfo.ppEnabledExtensionNames = EnabledDeviceExtensions.data(); + deviceCreateInfo.enabledLayerCount = 0; + + VkResult result = vkCreateDevice(PhysicalDevice.Device, &deviceCreateInfo, nullptr, &device); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan device"); + + volkLoadDevice(device); + + vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue); + vkGetDeviceQueue(device, presentFamily, 0, &presentQueue); + vkGetDeviceQueue(device, transferFamily, 0, &transferQueue); +} + +void VulkanDevice::CreateSurface() +{ + if (!I_CreateVulkanSurface(instance, &surface)) + { + throw std::runtime_error("Could not create vulkan surface"); + } +} + +void VulkanDevice::CreateInstance() +{ + AvailableLayers = GetAvailableLayers(); + Extensions = GetExtensions(); + EnabledExtensions = GetPlatformExtensions(); + + std::string debugLayer = "VK_LAYER_LUNARG_standard_validation"; + bool wantDebugLayer = vk_debug; + bool debugLayerFound = false; + for (const VkLayerProperties &layer : AvailableLayers) + { + if (layer.layerName == debugLayer && wantDebugLayer) + { + EnabledValidationLayers.push_back(debugLayer.c_str()); + EnabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + debugLayerFound = true; + } + } + + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "GZDoom"; + appInfo.applicationVersion = VK_MAKE_VERSION(VER_MAJOR, VER_MINOR, VER_REVISION); + appInfo.pEngineName = "GZDoom"; + appInfo.engineVersion = VK_MAKE_VERSION(ENG_MAJOR, ENG_MINOR, ENG_REVISION); + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.enabledExtensionCount = (uint32_t)EnabledExtensions.size(); + createInfo.enabledLayerCount = (uint32_t)EnabledValidationLayers.size(); + createInfo.ppEnabledLayerNames = EnabledValidationLayers.data(); + createInfo.ppEnabledExtensionNames = EnabledExtensions.data(); + + VkResult result = vkCreateInstance(&createInfo, nullptr, &instance); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan instance"); + + volkLoadInstance(instance); + + if (debugLayerFound) + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = + //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + //VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = DebugCallback; + createInfo.pUserData = this; + result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateDebugUtilsMessengerEXT failed"); + } +} + +VkBool32 VulkanDevice::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) { VulkanDevice *device = (VulkanDevice*)userData; @@ -179,249 +406,89 @@ VkBool32 VulkanDevice::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT mess return VK_FALSE; } -void VulkanDevice::createInstance() +std::vector VulkanDevice::GetAvailableLayers() { - VkResult result; - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - availableLayers.resize(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + VkResult result = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + std::vector availableLayers(layerCount); + result = vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + return availableLayers; +} + +std::vector VulkanDevice::GetExtensions() +{ uint32_t extensionCount = 0; - result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - extensions.resize(extensionCount); - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - VkApplicationInfo appInfo = {}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "GZDoom"; - appInfo.applicationVersion = VK_MAKE_VERSION(VER_MAJOR, VER_MINOR, VER_REVISION); - appInfo.pEngineName = "GZDoom"; - appInfo.engineVersion = VK_MAKE_VERSION(ENG_MAJOR, ENG_MINOR, ENG_REVISION); - appInfo.apiVersion = VK_API_VERSION_1_0; - - std::vector enabledExtensions; - - if (!I_GetVulkanPlatformExtensions(&extensionCount, nullptr)) - { - throw std::runtime_error("Cannot obtain number of Vulkan extensions"); - } - - enabledExtensions.resize(extensionCount); - - if (!I_GetVulkanPlatformExtensions(&extensionCount, &enabledExtensions[0])) - { - throw std::runtime_error("Cannot obtain list of Vulkan extensions"); - } - - std::vector validationLayers; - std::string debugLayer = "VK_LAYER_LUNARG_standard_validation"; - bool wantDebugLayer = vk_debug; - bool debugLayerFound = false; - for (const VkLayerProperties &layer : availableLayers) - { - if (layer.layerName == debugLayer && wantDebugLayer) - { - validationLayers.push_back(debugLayer.c_str()); - enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - debugLayerFound = true; - } - } - - VkInstanceCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - createInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); - createInfo.enabledLayerCount = (uint32_t)validationLayers.size(); - createInfo.ppEnabledLayerNames = validationLayers.data(); - createInfo.ppEnabledExtensionNames = enabledExtensions.data(); - - result = vkCreateInstance(&createInfo, nullptr, &instance); - if (result != VK_SUCCESS) - throw std::runtime_error("Could not create vulkan instance"); - - volkLoadInstance(instance); - - if (debugLayerFound) - { - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - createInfo.messageSeverity = - //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - //VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - createInfo.messageType = - VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - createInfo.pfnUserCallback = debugCallback; - createInfo.pUserData = this; - result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger); - if (result != VK_SUCCESS) - throw std::runtime_error("vkCreateDebugUtilsMessengerEXT failed"); - } + std::vector extensions(extensionCount); + result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + return extensions; } -void VulkanDevice::createSurface() +std::vector VulkanDevice::GetPhysicalDevices(VkInstance instance) { - if (!I_CreateVulkanSurface(instance, &surface)) - { - throw std::runtime_error("Could not create vulkan surface"); - } -} - -bool VulkanDevice::checkFeatures(const VkPhysicalDeviceFeatures &f) -{ - return - f.samplerAnisotropy == VK_TRUE && - f.shaderClipDistance == VK_TRUE && - f.fragmentStoresAndAtomics == VK_TRUE && - f.depthClamp == VK_TRUE && - f.shaderClipDistance == VK_TRUE; -} - -void VulkanDevice::selectPhysicalDevice() -{ - VkResult result; - uint32_t deviceCount = 0; - result = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + VkResult result = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (result != VK_SUCCESS) throw std::runtime_error("vkEnumeratePhysicalDevices failed"); - else if (deviceCount == 0) - throw std::runtime_error("Could not find any vulkan devices"); + if (deviceCount == 0) + return {}; std::vector devices(deviceCount); result = vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); if (result != VK_SUCCESS) throw std::runtime_error("vkEnumeratePhysicalDevices failed (2)"); - for (const auto &device : devices) + std::vector devinfo(deviceCount); + for (size_t i = 0; i < devices.size(); i++) { - vkGetPhysicalDeviceProperties(device, &deviceProperties); - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + auto &dev = devinfo[i]; + dev.Device = devices[i]; - bool isUsableDevice = /*deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&*/ checkFeatures(deviceFeatures); - if (!isUsableDevice) - continue; + vkGetPhysicalDeviceMemoryProperties(dev.Device, &dev.MemoryProperties); + vkGetPhysicalDeviceProperties(dev.Device, &dev.Properties); + vkGetPhysicalDeviceFeatures(dev.Device, &dev.Features); uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + vkGetPhysicalDeviceQueueFamilyProperties(dev.Device, &queueFamilyCount, nullptr); + dev.QueueFamilies.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(dev.Device, &queueFamilyCount, dev.QueueFamilies.data()); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - graphicsFamily = -1; - computeFamily = -1; - transferFamily = -1; - sparseBindingFamily = -1; - presentFamily = -1; - - int i = 0; - for (const auto& queueFamily : queueFamilies) - { - // Only accept a decent GPU for now.. - VkQueueFlags gpuFlags = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT); - if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & gpuFlags) == gpuFlags) - { - graphicsFamily = i; - computeFamily = i; - transferFamily = i; - sparseBindingFamily = i; - } - - VkBool32 presentSupport = false; - result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (result == VK_SUCCESS && queueFamily.queueCount > 0 && presentSupport) presentFamily = i; - - i++; - } - - uint32_t deviceExtensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &deviceExtensionCount, nullptr); - availableDeviceExtensions.resize(deviceExtensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &deviceExtensionCount, availableDeviceExtensions.data()); - - std::set requiredExtensionSearch(requiredExtensions.begin(), requiredExtensions.end()); - for (const auto &ext : availableDeviceExtensions) - requiredExtensionSearch.erase(ext.extensionName); - if (!requiredExtensionSearch.empty()) - continue; - - physicalDevice = device; - return; + uint32_t deviceExtensionCount = 0; + vkEnumerateDeviceExtensionProperties(dev.Device, nullptr, &deviceExtensionCount, nullptr); + dev.Extensions.resize(deviceExtensionCount); + vkEnumerateDeviceExtensionProperties(dev.Device, nullptr, &deviceExtensionCount, dev.Extensions.data()); } - - throw std::runtime_error("No Vulkan device supports the minimum requirements of this application"); + return devinfo; } -void VulkanDevice::createDevice() +std::vector VulkanDevice::GetPlatformExtensions() { - float queuePriority = 1.0f; - std::vector queueCreateInfos; + uint32_t extensionCount = 0; + if (!I_GetVulkanPlatformExtensions(&extensionCount, nullptr)) + throw std::runtime_error("Cannot obtain number of Vulkan extensions"); - std::set neededFamilies; - neededFamilies.insert(graphicsFamily); - neededFamilies.insert(presentFamily); - neededFamilies.insert(computeFamily); + std::vector extensions(extensionCount); + if (!I_GetVulkanPlatformExtensions(&extensionCount, extensions.data())) + throw std::runtime_error("Cannot obtain list of Vulkan extensions"); + return extensions; +} - for (int index : neededFamilies) +void VulkanDevice::InitVolk() +{ + if (volkInitialize() != VK_SUCCESS) { - VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = index; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.push_back(queueCreateInfo); + throw std::runtime_error("Unable to find Vulkan"); + } + auto iver = volkGetInstanceVersion(); + if (iver == 0) + { + throw std::runtime_error("Vulkan not supported"); } - - VkPhysicalDeviceFeatures usedDeviceFeatures = {}; - usedDeviceFeatures.samplerAnisotropy = VK_TRUE; - usedDeviceFeatures.shaderClipDistance = VK_TRUE; - usedDeviceFeatures.fragmentStoresAndAtomics = VK_TRUE; - usedDeviceFeatures.depthClamp = VK_TRUE; - usedDeviceFeatures.shaderClipDistance = VK_TRUE; - - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(); - deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); - deviceCreateInfo.pEnabledFeatures = &usedDeviceFeatures; - deviceCreateInfo.enabledExtensionCount = (uint32_t)requiredExtensions.size(); - deviceCreateInfo.ppEnabledExtensionNames = requiredExtensions.data(); - deviceCreateInfo.enabledLayerCount = 0; - - VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); - if (result != VK_SUCCESS) - throw std::runtime_error("Could not create vulkan device"); - - volkLoadDevice(device); - - vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue); - vkGetDeviceQueue(device, presentFamily, 0, &presentQueue); } -void VulkanDevice::createAllocator() -{ - VmaAllocatorCreateInfo allocinfo = {}; - // allocinfo.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; // To do: enable this for better performance - allocinfo.physicalDevice = physicalDevice; - allocinfo.device = device; - allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024; - if (vmaCreateAllocator(&allocinfo, &allocator) != VK_SUCCESS) - throw std::runtime_error("Unable to create allocator"); -} - -void VulkanDevice::createSemaphores() -{ - imageAvailableSemaphore.reset(new VulkanSemaphore(this)); - renderFinishedSemaphore.reset(new VulkanSemaphore(this)); - renderFinishedFence.reset(new VulkanFence(this)); -} - -void VulkanDevice::releaseResources() +void VulkanDevice::ReleaseResources() { if (device) vkDeviceWaitIdle(device); @@ -450,14 +517,11 @@ void VulkanDevice::releaseResources() instance = nullptr; } -uint32_t VulkanDevice::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) +uint32_t VulkanDevice::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) + for (uint32_t i = 0; i < PhysicalDevice.MemoryProperties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + if ((typeFilter & (1 << i)) && (PhysicalDevice.MemoryProperties.memoryTypes[i].propertyFlags & properties) == properties) return i; } diff --git a/src/rendering/vulkan/system/vk_device.h b/src/rendering/vulkan/system/vk_device.h index db3ed7567..559343bf6 100644 --- a/src/rendering/vulkan/system/vk_device.h +++ b/src/rendering/vulkan/system/vk_device.h @@ -11,70 +11,90 @@ class VulkanSwapChain; class VulkanSemaphore; class VulkanFence; +class VulkanPhysicalDevice +{ +public: + VkPhysicalDevice Device = VK_NULL_HANDLE; + + std::vector Extensions; + std::vector QueueFamilies; + VkPhysicalDeviceProperties Properties = {}; + VkPhysicalDeviceFeatures Features = {}; + VkPhysicalDeviceMemoryProperties MemoryProperties = {}; +}; + +class VulkanCompatibleDevice +{ +public: + VulkanPhysicalDevice *device = nullptr; + int graphicsFamily = -1; + int transferFamily = -1; + int presentFamily = -1; +}; + class VulkanDevice { public: VulkanDevice(); ~VulkanDevice(); - void windowResized(); - void waitPresent(); + void WindowResized(); + void WaitPresent(); - void beginFrame(); - void presentFrame(); + void BeginFrame(); + void PresentFrame(); - VkDevice device = nullptr; + uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + + // Instance setup + std::vector AvailableLayers; + std::vector Extensions; + std::vector EnabledExtensions; + std::vector EnabledValidationLayers; + + // Device setup + VkPhysicalDeviceFeatures UsedDeviceFeatures = {}; + std::vector EnabledDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + VulkanPhysicalDevice PhysicalDevice; + + VkInstance instance = VK_NULL_HANDLE; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VmaAllocator allocator = VK_NULL_HANDLE; + + VkQueue graphicsQueue = VK_NULL_HANDLE; + VkQueue presentQueue = VK_NULL_HANDLE; + VkQueue transferQueue = VK_NULL_HANDLE; int graphicsFamily = -1; - int computeFamily = -1; int transferFamily = -1; - int sparseBindingFamily = -1; int presentFamily = -1; std::unique_ptr swapChain; uint32_t presentImageIndex = 0; - VkQueue graphicsQueue = nullptr; - std::unique_ptr imageAvailableSemaphore; std::unique_ptr renderFinishedSemaphore; std::unique_ptr renderFinishedFence; - VmaAllocator allocator = VK_NULL_HANDLE; - - std::vector availableLayers; - std::vector extensions; - std::vector availableDeviceExtensions; - VkPhysicalDeviceProperties deviceProperties; - VkPhysicalDeviceFeatures deviceFeatures; - - VkPhysicalDevice physicalDevice = {}; - - uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); - private: - void createInstance(); - void createSurface(); - void selectPhysicalDevice(); - void createDevice(); - void createAllocator(); - void createSemaphores(); - void releaseResources(); + void CreateInstance(); + void CreateSurface(); + void SelectPhysicalDevice(); + void CreateDevice(); + void CreateAllocator(); + void CreateSemaphores(); + void ReleaseResources(); - static bool checkFeatures(const VkPhysicalDeviceFeatures &f); + static bool CheckFeatures(const VkPhysicalDeviceFeatures &f); VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; - static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); - std::vector requiredExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); - VkInstance instance = nullptr; - VkSurfaceKHR surface = 0; - - VkQueue presentQueue = nullptr; - - VulkanDevice(const VulkanDevice &) = delete; - VulkanDevice &operator=(const VulkanDevice &) = delete; - - friend class VulkanSwapChain; + static void InitVolk(); + static std::vector GetAvailableLayers(); + static std::vector GetExtensions(); + static std::vector GetPlatformExtensions(); + static std::vector GetPhysicalDevices(VkInstance instance); }; diff --git a/src/rendering/vulkan/system/vk_framebuffer.cpp b/src/rendering/vulkan/system/vk_framebuffer.cpp index 199ede7f5..c60c8b35e 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/rendering/vulkan/system/vk_framebuffer.cpp @@ -102,8 +102,8 @@ void VulkanFrameBuffer::InitializeState() gl_vendorstring = "Vulkan"; hwcaps = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; glslversion = 4.50f; - uniformblockalignment = (unsigned int)device->deviceProperties.limits.minUniformBufferOffsetAlignment; - maxuniformblock = device->deviceProperties.limits.maxUniformBufferRange; + uniformblockalignment = (unsigned int)device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment; + maxuniformblock = device->PhysicalDevice.Properties.limits.maxUniformBufferRange; mUploadSemaphore.reset(new VulkanSemaphore(device)); mGraphicsCommandPool.reset(new VulkanCommandPool(device, device->graphicsFamily)); @@ -149,12 +149,12 @@ void VulkanFrameBuffer::Update() int newHeight = GetClientHeight(); if (lastSwapWidth != newWidth || lastSwapHeight != newHeight) { - device->windowResized(); + device->WindowResized(); lastSwapWidth = newWidth; lastSwapHeight = newHeight; } - device->beginFrame(); + device->BeginFrame(); GetPostprocess()->SetActiveRenderTarget(); @@ -220,8 +220,8 @@ void VulkanFrameBuffer::Update() Finish.Reset(); Finish.Clock(); - device->presentFrame(); - device->waitPresent(); + device->PresentFrame(); + device->WaitPresent(); mDrawCommands.reset(); mUploadCommands.reset(); @@ -478,7 +478,7 @@ void VulkanFrameBuffer::SetVSync(bool vsync) { if (device->swapChain->vsync != vsync) { - device->windowResized(); + device->WindowResized(); } } @@ -596,33 +596,35 @@ unsigned int VulkanFrameBuffer::GetLightBufferBlockSize() const void VulkanFrameBuffer::PrintStartupLog() { + const auto props = device->PhysicalDevice.Properties; + FString deviceType; - switch (device->deviceProperties.deviceType) + switch (props.deviceType) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break; - default: deviceType.Format("%d", (int)device->deviceProperties.deviceType); break; + default: deviceType.Format("%d", (int)props.deviceType); break; } FString apiVersion, driverVersion; - apiVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(device->deviceProperties.apiVersion), VK_VERSION_MINOR(device->deviceProperties.apiVersion), VK_VERSION_PATCH(device->deviceProperties.apiVersion)); - driverVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(device->deviceProperties.driverVersion), VK_VERSION_MINOR(device->deviceProperties.driverVersion), VK_VERSION_PATCH(device->deviceProperties.driverVersion)); + apiVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion)); + driverVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(props.driverVersion), VK_VERSION_MINOR(props.driverVersion), VK_VERSION_PATCH(props.driverVersion)); - Printf("Vulkan device: " TEXTCOLOR_ORANGE "%s\n", device->deviceProperties.deviceName); + Printf("Vulkan device: " TEXTCOLOR_ORANGE "%s\n", props.deviceName); Printf("Vulkan device type: %s\n", deviceType.GetChars()); Printf("Vulkan version: %s (api) %s (driver)\n", apiVersion.GetChars(), driverVersion.GetChars()); Printf(PRINT_LOG, "Vulkan extensions:"); - for (const VkExtensionProperties &p : device->availableDeviceExtensions) + for (const VkExtensionProperties &p : device->PhysicalDevice.Extensions) { Printf(PRINT_LOG, " %s", p.extensionName); } Printf(PRINT_LOG, "\n"); - const auto &limits = device->deviceProperties.limits; + const auto &limits = props.limits; Printf("Max. texture size: %d\n", limits.maxImageDimension2D); Printf("Max. uniform buffer range: %d\n", limits.maxUniformBufferRange); Printf("Min. uniform buffer offset alignment: %d\n", limits.minUniformBufferOffsetAlignment); diff --git a/src/rendering/vulkan/system/vk_swapchain.cpp b/src/rendering/vulkan/system/vk_swapchain.cpp index 968a4a782..372689cf0 100644 --- a/src/rendering/vulkan/system/vk_swapchain.cpp +++ b/src/rendering/vulkan/system/vk_swapchain.cpp @@ -4,31 +4,31 @@ VulkanSwapChain::VulkanSwapChain(VulkanDevice *device, int width, int height, bool vsync) : vsync(vsync), device(device) { VkSurfaceCapabilitiesKHR surfaceCapabilities; - VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device->physicalDevice, device->surface, &surfaceCapabilities); + VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device->PhysicalDevice.Device, device->surface, &surfaceCapabilities); if (result != VK_SUCCESS) throw std::runtime_error("vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed"); uint32_t surfaceFormatCount = 0; - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device->physicalDevice, device->surface, &surfaceFormatCount, nullptr); + result = vkGetPhysicalDeviceSurfaceFormatsKHR(device->PhysicalDevice.Device, device->surface, &surfaceFormatCount, nullptr); if (result != VK_SUCCESS) throw std::runtime_error("vkGetPhysicalDeviceSurfaceFormatsKHR failed"); else if (surfaceFormatCount == 0) throw std::runtime_error("No surface formats supported"); std::vector surfaceFormats(surfaceFormatCount); - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device->physicalDevice, device->surface, &surfaceFormatCount, surfaceFormats.data()); + result = vkGetPhysicalDeviceSurfaceFormatsKHR(device->PhysicalDevice.Device, device->surface, &surfaceFormatCount, surfaceFormats.data()); if (result != VK_SUCCESS) throw std::runtime_error("vkGetPhysicalDeviceSurfaceFormatsKHR failed"); uint32_t presentModeCount = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(device->physicalDevice, device->surface, &presentModeCount, nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(device->PhysicalDevice.Device, device->surface, &presentModeCount, nullptr); if (result != VK_SUCCESS) throw std::runtime_error("vkGetPhysicalDeviceSurfacePresentModesKHR failed"); else if (presentModeCount == 0) throw std::runtime_error("No surface present modes supported"); std::vector presentModes(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device->physicalDevice, device->surface, &presentModeCount, presentModes.data()); + vkGetPhysicalDeviceSurfacePresentModesKHR(device->PhysicalDevice.Device, device->surface, &presentModeCount, presentModes.data()); if (result != VK_SUCCESS) throw std::runtime_error("vkGetPhysicalDeviceSurfacePresentModesKHR failed");