summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-08-13 22:57:50 +1000
committerStenzek <stenzek@gmail.com>2016-10-01 02:40:01 +1000
commit77a128ab8707659a531029a21a2e874108eedf38 (patch)
tree7b4a52ee61208eec2bf44a7745f3dac3bed1bafb /Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
parentfdd954e7e757b4601eb6d2636e12623aef63c9b4 (diff)
Implement experimental Vulkan backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VulkanContext.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/VulkanContext.cpp719
1 files changed, 719 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
new file mode 100644
index 0000000000..ef2e6e1fb3
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
@@ -0,0 +1,719 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "Common/Assert.h"
+#include "Common/CommonFuncs.h"
+#include "Common/Logging/Log.h"
+#include "Common/MsgHandler.h"
+#include "Common/StringUtil.h"
+
+#include "VideoBackends/Vulkan/VulkanContext.h"
+#include "VideoCommon/DriverDetails.h"
+
+namespace Vulkan
+{
+std::unique_ptr<VulkanContext> g_vulkan_context;
+
+VulkanContext::VulkanContext(VkInstance instance, VkPhysicalDevice physical_device)
+ : m_instance(instance), m_physical_device(physical_device)
+{
+ // Read device physical memory properties, we need it for allocating buffers
+ vkGetPhysicalDeviceProperties(physical_device, &m_device_properties);
+ vkGetPhysicalDeviceMemoryProperties(physical_device, &m_device_memory_properties);
+
+ // Would any drivers be this silly? I hope not...
+ m_device_properties.limits.minUniformBufferOffsetAlignment = std::max(
+ m_device_properties.limits.minUniformBufferOffsetAlignment, static_cast<VkDeviceSize>(1));
+ m_device_properties.limits.minTexelBufferOffsetAlignment = std::max(
+ m_device_properties.limits.minTexelBufferOffsetAlignment, static_cast<VkDeviceSize>(1));
+ m_device_properties.limits.optimalBufferCopyOffsetAlignment = std::max(
+ m_device_properties.limits.optimalBufferCopyOffsetAlignment, static_cast<VkDeviceSize>(1));
+ m_device_properties.limits.optimalBufferCopyRowPitchAlignment = std::max(
+ m_device_properties.limits.optimalBufferCopyRowPitchAlignment, static_cast<VkDeviceSize>(1));
+}
+
+VulkanContext::~VulkanContext()
+{
+ if (m_device != VK_NULL_HANDLE)
+ vkDestroyDevice(m_device, nullptr);
+
+ if (m_debug_report_callback != VK_NULL_HANDLE)
+ DisableDebugReports();
+
+ vkDestroyInstance(m_instance, nullptr);
+}
+
+bool VulkanContext::CheckValidationLayerAvailablility()
+{
+ u32 extension_count = 0;
+ VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
+ return false;
+ }
+
+ std::vector<VkExtensionProperties> extension_list(extension_count);
+ res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_list.data());
+ _assert_(res == VK_SUCCESS);
+
+ u32 layer_count = 0;
+ res = vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
+ return false;
+ }
+
+ std::vector<VkLayerProperties> layer_list(layer_count);
+ res = vkEnumerateInstanceLayerProperties(&layer_count, layer_list.data());
+ _assert_(res == VK_SUCCESS);
+
+ // Check for both VK_EXT_debug_report and VK_LAYER_LUNARG_standard_validation
+ return (std::find_if(extension_list.begin(), extension_list.end(),
+ [](const auto& it) {
+ return strcmp(it.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0;
+ }) != extension_list.end() &&
+ std::find_if(layer_list.begin(), layer_list.end(), [](const auto& it) {
+ return strcmp(it.layerName, "VK_LAYER_LUNARG_standard_validation") == 0;
+ }) != layer_list.end());
+}
+
+VkInstance VulkanContext::CreateVulkanInstance(bool enable_surface, bool enable_validation_layer)
+{
+ ExtensionList enabled_extensions;
+ if (!SelectInstanceExtensions(&enabled_extensions, enable_surface, enable_validation_layer))
+ return VK_NULL_HANDLE;
+
+ VkApplicationInfo app_info = {};
+ app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ app_info.pNext = nullptr;
+ app_info.pApplicationName = "Dolphin Emulator";
+ app_info.applicationVersion = VK_MAKE_VERSION(5, 0, 0);
+ app_info.pEngineName = "Dolphin Emulator";
+ app_info.engineVersion = VK_MAKE_VERSION(5, 0, 0);
+ app_info.apiVersion = VK_MAKE_VERSION(1, 0, 0);
+
+ VkInstanceCreateInfo instance_create_info = {};
+ instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instance_create_info.pNext = nullptr;
+ instance_create_info.flags = 0;
+ instance_create_info.pApplicationInfo = &app_info;
+ instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
+ instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
+ instance_create_info.enabledLayerCount = 0;
+ instance_create_info.ppEnabledLayerNames = nullptr;
+
+ // Enable debug layer on debug builds
+ if (enable_validation_layer)
+ {
+ static const char* layer_names[] = {"VK_LAYER_LUNARG_standard_validation"};
+ instance_create_info.enabledLayerCount = 1;
+ instance_create_info.ppEnabledLayerNames = layer_names;
+ }
+
+ VkInstance instance;
+ VkResult res = vkCreateInstance(&instance_create_info, nullptr, &instance);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateInstance failed: ");
+ return nullptr;
+ }
+
+ return instance;
+}
+
+bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface,
+ bool enable_validation_layer)
+{
+ u32 extension_count = 0;
+ VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
+ return false;
+ }
+
+ if (extension_count == 0)
+ {
+ ERROR_LOG(VIDEO, "Vulkan: No extensions supported by instance.");
+ return false;
+ }
+
+ std::vector<VkExtensionProperties> available_extension_list(extension_count);
+ res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count,
+ available_extension_list.data());
+ _assert_(res == VK_SUCCESS);
+
+ for (const auto& extension_properties : available_extension_list)
+ INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
+
+ auto CheckForExtension = [&](const char* name, bool required) -> bool {
+ if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
+ [&](const VkExtensionProperties& properties) {
+ return !strcmp(name, properties.extensionName);
+ }) != available_extension_list.end())
+ {
+ INFO_LOG(VIDEO, "Enabling extension: %s", name);
+ extension_list->push_back(name);
+ return true;
+ }
+
+ if (required)
+ {
+ ERROR_LOG(VIDEO, "Vulkan: Missing required extension %s.", name);
+ return false;
+ }
+
+ return true;
+ };
+
+ // Common extensions
+ if (enable_surface && !CheckForExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
+ {
+ return false;
+ }
+
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+ if (enable_surface && !CheckForExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
+ return false;
+#elif defined(VK_USE_PLATFORM_XLIB_KHR)
+ if (enable_surface && !CheckForExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
+ return false;
+#elif defined(VK_USE_PLATFORM_XCB_KHR)
+ if (enable_surface && !CheckForExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, true))
+ return false;
+#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
+ if (enable_surface && !CheckForExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
+ return false;
+#endif
+
+ // VK_EXT_debug_report
+ if (enable_validation_layer && !CheckForExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, true))
+ return false;
+
+ return true;
+}
+
+VulkanContext::GPUList VulkanContext::EnumerateGPUs(VkInstance instance)
+{
+ u32 gpu_count = 0;
+ VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
+ return {};
+ }
+
+ GPUList gpus;
+ gpus.resize(gpu_count);
+
+ res = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
+ return {};
+ }
+
+ return gpus;
+}
+
+void VulkanContext::PopulateBackendInfo(VideoConfig* config)
+{
+ config->backend_info.api_type = APIType::Vulkan;
+ config->backend_info.bSupportsExclusiveFullscreen = false; // Currently WSI does not allow this.
+ config->backend_info.bSupports3DVision = false; // D3D-exclusive.
+ config->backend_info.bSupportsOversizedViewports = true; // Assumed support.
+ config->backend_info.bSupportsEarlyZ = true; // Assumed support.
+ config->backend_info.bSupportsPrimitiveRestart = true; // Assumed support.
+ config->backend_info.bSupportsBindingLayout = false; // Assumed support.
+ config->backend_info.bSupportsPaletteConversion = true; // Assumed support.
+ config->backend_info.bSupportsClipControl = true; // Assumed support.
+ config->backend_info.bSupportsMultithreading = true; // Assumed support.
+ config->backend_info.bSupportsPostProcessing = false; // No support yet.
+ config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
+ config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
+ config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
+ config->backend_info.bSupportsBBox = false; // Dependent on features.
+ config->backend_info.bSupportsSSAA = false; // Dependent on features.
+ config->backend_info.bSupportsDepthClamp = false; // Dependent on features.
+ config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs.
+}
+
+void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list)
+{
+ config->backend_info.Adapters.clear();
+ for (VkPhysicalDevice physical_device : gpu_list)
+ {
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(physical_device, &properties);
+ config->backend_info.Adapters.push_back(properties.deviceName);
+ }
+}
+
+void VulkanContext::PopulateBackendInfoFeatures(VideoConfig* config, VkPhysicalDevice gpu,
+ const VkPhysicalDeviceFeatures& features)
+{
+ config->backend_info.bSupportsDualSourceBlend = (features.dualSrcBlend == VK_TRUE);
+ config->backend_info.bSupportsGeometryShaders = (features.geometryShader == VK_TRUE);
+ config->backend_info.bSupportsGSInstancing = (features.geometryShader == VK_TRUE);
+ config->backend_info.bSupportsBBox = (features.fragmentStoresAndAtomics == VK_TRUE);
+ config->backend_info.bSupportsSSAA = (features.sampleRateShading == VK_TRUE);
+
+ // Disable geometry shader when shaderTessellationAndGeometryPointSize is not supported.
+ // Seems this is needed for gl_Layer.
+ if (!features.shaderTessellationAndGeometryPointSize)
+ config->backend_info.bSupportsGeometryShaders = VK_FALSE;
+
+ // TODO: Investigate if there's a feature we can enable for GS instancing.
+ config->backend_info.bSupportsGSInstancing = VK_FALSE;
+
+ // Depth clamping implies shaderClipDistance and depthClamp
+ config->backend_info.bSupportsDepthClamp =
+ (features.depthClamp == VK_TRUE && features.shaderClipDistance == VK_TRUE);
+}
+
+void VulkanContext::PopulateBackendInfoMultisampleModes(
+ VideoConfig* config, VkPhysicalDevice gpu, const VkPhysicalDeviceProperties& properties)
+{
+ // Query image support for the EFB texture formats.
+ VkImageFormatProperties efb_color_properties = {};
+ vkGetPhysicalDeviceImageFormatProperties(
+ gpu, EFB_COLOR_TEXTURE_FORMAT, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, &efb_color_properties);
+ VkImageFormatProperties efb_depth_properties = {};
+ vkGetPhysicalDeviceImageFormatProperties(
+ gpu, EFB_DEPTH_TEXTURE_FORMAT, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, &efb_depth_properties);
+
+ // We can only support MSAA if it's supported on our render target formats.
+ VkSampleCountFlags supported_sample_counts = properties.limits.framebufferColorSampleCounts &
+ properties.limits.framebufferDepthSampleCounts &
+ efb_color_properties.sampleCounts &
+ efb_color_properties.sampleCounts;
+
+ // No AA
+ config->backend_info.AAModes.clear();
+ config->backend_info.AAModes.emplace_back(1);
+
+ // 2xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_2_BIT)
+ config->backend_info.AAModes.emplace_back(2);
+
+ // 4xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_4_BIT)
+ config->backend_info.AAModes.emplace_back(4);
+
+ // 8xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_8_BIT)
+ config->backend_info.AAModes.emplace_back(8);
+
+ // 16xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_16_BIT)
+ config->backend_info.AAModes.emplace_back(16);
+
+ // 32xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_32_BIT)
+ config->backend_info.AAModes.emplace_back(32);
+
+ // 64xMSAA/SSAA
+ if (supported_sample_counts & VK_SAMPLE_COUNT_64_BIT)
+ config->backend_info.AAModes.emplace_back(64);
+}
+
+std::unique_ptr<VulkanContext> VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu,
+ VkSurfaceKHR surface, VideoConfig* config,
+ bool enable_validation_layer)
+{
+ std::unique_ptr<VulkanContext> context = std::make_unique<VulkanContext>(instance, gpu);
+
+ // Initialize DriverDetails so that we can check for bugs to disable features if needed.
+ DriverDetails::Init(DriverDetails::API_VULKAN,
+ DriverDetails::TranslatePCIVendorID(context->m_device_properties.vendorID),
+ DriverDetails::DRIVER_UNKNOWN,
+ static_cast<double>(context->m_device_properties.driverVersion),
+ DriverDetails::Family::UNKNOWN);
+
+ // Enable debug reports if validation layer is enabled.
+ if (enable_validation_layer)
+ context->EnableDebugReports();
+
+ // Attempt to create the device.
+ if (!context->CreateDevice(surface, enable_validation_layer))
+ {
+ // Since we are destroying the instance, we're also responsible for destroying the surface.
+ if (surface != VK_NULL_HANDLE)
+ vkDestroySurfaceKHR(instance, surface, nullptr);
+
+ return nullptr;
+ }
+
+ // Update video config with features.
+ PopulateBackendInfoFeatures(config, gpu, context->m_device_features);
+ PopulateBackendInfoMultisampleModes(config, gpu, context->m_device_properties);
+ return context;
+}
+
+bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface,
+ bool enable_validation_layer)
+{
+ u32 extension_count = 0;
+ VkResult res =
+ vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, nullptr);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: ");
+ return false;
+ }
+
+ if (extension_count == 0)
+ {
+ ERROR_LOG(VIDEO, "Vulkan: No extensions supported by device.");
+ return false;
+ }
+
+ std::vector<VkExtensionProperties> available_extension_list(extension_count);
+ res = vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count,
+ available_extension_list.data());
+ _assert_(res == VK_SUCCESS);
+
+ for (const auto& extension_properties : available_extension_list)
+ INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName);
+
+ auto CheckForExtension = [&](const char* name, bool required) -> bool {
+ if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
+ [&](const VkExtensionProperties& properties) {
+ return !strcmp(name, properties.extensionName);
+ }) != available_extension_list.end())
+ {
+ INFO_LOG(VIDEO, "Enabling extension: %s", name);
+ extension_list->push_back(name);
+ return true;
+ }
+
+ if (required)
+ {
+ ERROR_LOG(VIDEO, "Vulkan: Missing required extension %s.", name);
+ return false;
+ }
+
+ return true;
+ };
+
+ if (enable_surface && !CheckForExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool VulkanContext::SelectDeviceFeatures()
+{
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(m_physical_device, &properties);
+
+ VkPhysicalDeviceFeatures available_features;
+ vkGetPhysicalDeviceFeatures(m_physical_device, &available_features);
+
+ // Not having geometry shaders or wide lines will cause issues with rendering.
+ if (!available_features.geometryShader && !available_features.wideLines)
+ WARN_LOG(VIDEO, "Vulkan: Missing both geometryShader and wideLines features.");
+ if (!available_features.largePoints)
+ WARN_LOG(VIDEO, "Vulkan: Missing large points feature. CPU EFB writes will be slower.");
+ if (!available_features.occlusionQueryPrecise)
+ WARN_LOG(VIDEO, "Vulkan: Missing precise occlusion queries. Perf queries will be inaccurate.");
+
+ // Check push constant size.
+ if (properties.limits.maxPushConstantsSize < static_cast<u32>(PUSH_CONSTANT_BUFFER_SIZE))
+ {
+ PanicAlert("Vulkan: Push contant buffer size %u is below minimum %u.",
+ properties.limits.maxPushConstantsSize, static_cast<u32>(PUSH_CONSTANT_BUFFER_SIZE));
+
+ return false;
+ }
+
+ // Enable the features we use.
+ m_device_features.dualSrcBlend = available_features.dualSrcBlend;
+ m_device_features.geometryShader = available_features.geometryShader;
+ m_device_features.samplerAnisotropy = available_features.samplerAnisotropy;
+ m_device_features.logicOp = available_features.logicOp;
+ m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
+ m_device_features.sampleRateShading = available_features.sampleRateShading;
+ m_device_features.largePoints = available_features.largePoints;
+ m_device_features.shaderStorageImageMultisample =
+ available_features.shaderStorageImageMultisample;
+ m_device_features.shaderTessellationAndGeometryPointSize =
+ available_features.shaderTessellationAndGeometryPointSize;
+ m_device_features.occlusionQueryPrecise = available_features.occlusionQueryPrecise;
+ m_device_features.shaderClipDistance = available_features.shaderClipDistance;
+ m_device_features.depthClamp = available_features.depthClamp;
+ return true;
+}
+
+bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
+{
+ u32 queue_family_count;
+ vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, nullptr);
+ if (queue_family_count == 0)
+ {
+ ERROR_LOG(VIDEO, "No queue families found on specified vulkan physical device.");
+ return false;
+ }
+
+ std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_count);
+ vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count,
+ queue_family_properties.data());
+ INFO_LOG(VIDEO, "%u vulkan queue families", queue_family_count);
+
+ // Find a graphics queue
+ // Currently we only use a single queue for both graphics and presenting.
+ // TODO: In the future we could do post-processing and presenting on a different queue.
+ m_graphics_queue_family_index = queue_family_count;
+ for (uint32_t i = 0; i < queue_family_count; i++)
+ {
+ if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+ {
+ // Check that it can present to our surface from this queue
+ if (surface)
+ {
+ VkBool32 present_supported;
+ VkResult res =
+ vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
+ return false;
+ }
+
+ if (present_supported)
+ {
+ m_graphics_queue_family_index = i;
+ break;
+ }
+ }
+ else
+ {
+ // We don't need present, so any graphics queue will do.
+ m_graphics_queue_family_index = i;
+ break;
+ }
+ }
+ }
+ if (m_graphics_queue_family_index == queue_family_count)
+ {
+ ERROR_LOG(VIDEO, "Vulkan: Failed to find an acceptable graphics queue.");
+ return false;
+ }
+
+ VkDeviceCreateInfo device_info = {};
+ device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ device_info.pNext = nullptr;
+ device_info.flags = 0;
+
+ static constexpr float queue_priorities[] = {1.0f};
+ VkDeviceQueueCreateInfo queue_info = {};
+ queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_info.pNext = nullptr;
+ queue_info.flags = 0;
+ queue_info.queueFamilyIndex = m_graphics_queue_family_index;
+ queue_info.queueCount = 1;
+ queue_info.pQueuePriorities = queue_priorities;
+ device_info.queueCreateInfoCount = 1;
+ device_info.pQueueCreateInfos = &queue_info;
+
+ ExtensionList enabled_extensions;
+ if (!SelectDeviceExtensions(&enabled_extensions, (surface != nullptr), enable_validation_layer))
+ return false;
+
+ device_info.enabledLayerCount = 0;
+ device_info.ppEnabledLayerNames = nullptr;
+ device_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
+ device_info.ppEnabledExtensionNames = enabled_extensions.data();
+
+ // Check for required features before creating.
+ if (!SelectDeviceFeatures())
+ return false;
+
+ device_info.pEnabledFeatures = &m_device_features;
+
+ // Enable debug layer on debug builds
+ if (enable_validation_layer)
+ {
+ static const char* layer_names[] = {"VK_LAYER_LUNARG_standard_validation"};
+ device_info.enabledLayerCount = 1;
+ device_info.ppEnabledLayerNames = layer_names;
+ }
+
+ VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateDevice failed: ");
+ return false;
+ }
+
+ // With the device created, we can fill the remaining entry points.
+ if (!LoadVulkanDeviceFunctions(m_device))
+ return false;
+
+ // Grab the graphics queue (only one we're using at this point).
+ vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
+ return true;
+}
+
+static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objectType,
+ uint64_t object, size_t location,
+ int32_t messageCode,
+ const char* pLayerPrefix,
+ const char* pMessage, void* pUserData)
+{
+ std::string log_message =
+ StringFromFormat("Vulkan debug report: (%s) %s", pLayerPrefix ? pLayerPrefix : "", pMessage);
+ if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
+ GENERIC_LOG(LogTypes::HOST_GPU, LogTypes::LERROR, "%s", log_message.c_str())
+ else if (flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT))
+ GENERIC_LOG(LogTypes::HOST_GPU, LogTypes::LWARNING, "%s", log_message.c_str())
+ else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
+ GENERIC_LOG(LogTypes::HOST_GPU, LogTypes::LINFO, "%s", log_message.c_str())
+ else
+ GENERIC_LOG(LogTypes::HOST_GPU, LogTypes::LDEBUG, "%s", log_message.c_str())
+
+ return VK_FALSE;
+}
+
+bool VulkanContext::EnableDebugReports()
+{
+ // Already enabled?
+ if (m_debug_report_callback != VK_NULL_HANDLE)
+ return true;
+
+ // Check for presence of the functions before calling
+ if (!vkCreateDebugReportCallbackEXT || !vkDestroyDebugReportCallbackEXT ||
+ !vkDebugReportMessageEXT)
+ {
+ return false;
+ }
+
+ VkDebugReportCallbackCreateInfoEXT callback_info = {
+ VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, nullptr,
+ VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
+ VK_DEBUG_REPORT_DEBUG_BIT_EXT,
+ DebugReportCallback, nullptr};
+
+ VkResult res =
+ vkCreateDebugReportCallbackEXT(m_instance, &callback_info, nullptr, &m_debug_report_callback);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateDebugReportCallbackEXT failed: ");
+ return false;
+ }
+
+ return true;
+}
+
+void VulkanContext::DisableDebugReports()
+{
+ if (m_debug_report_callback != VK_NULL_HANDLE)
+ {
+ vkDestroyDebugReportCallbackEXT(m_instance, m_debug_report_callback, nullptr);
+ m_debug_report_callback = VK_NULL_HANDLE;
+ }
+}
+
+bool VulkanContext::GetMemoryType(u32 bits, VkMemoryPropertyFlags properties, u32* out_type_index)
+{
+ for (u32 i = 0; i < VK_MAX_MEMORY_TYPES; i++)
+ {
+ if ((bits & (1 << i)) != 0)
+ {
+ u32 supported = m_device_memory_properties.memoryTypes[i].propertyFlags & properties;
+ if (supported == properties)
+ {
+ *out_type_index = i;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+u32 VulkanContext::GetMemoryType(u32 bits, VkMemoryPropertyFlags properties)
+{
+ u32 type_index = VK_MAX_MEMORY_TYPES;
+ if (!GetMemoryType(bits, properties, &type_index))
+ PanicAlert("Unable to find memory type for %x:%x", bits, properties);
+
+ return type_index;
+}
+
+u32 VulkanContext::GetUploadMemoryType(u32 bits, bool* is_coherent)
+{
+ // Try for coherent memory first.
+ VkMemoryPropertyFlags flags =
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+ u32 type_index;
+ if (!GetMemoryType(bits, flags, &type_index))
+ {
+ WARN_LOG(
+ VIDEO,
+ "Vulkan: Failed to find a coherent memory type for uploads, this will affect performance.");
+
+ // Try non-coherent memory.
+ flags &= ~VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ if (!GetMemoryType(bits, flags, &type_index))
+ {
+ // We shouldn't have any memory types that aren't host-visible.
+ PanicAlert("Unable to get memory type for upload.");
+ type_index = 0;
+ }
+ }
+
+ if (is_coherent)
+ *is_coherent = ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0);
+
+ return type_index;
+}
+
+u32 VulkanContext::GetReadbackMemoryType(u32 bits, bool* is_coherent, bool* is_cached)
+{
+ // Try for cached and coherent memory first.
+ VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+ u32 type_index;
+ if (!GetMemoryType(bits, flags, &type_index))
+ {
+ // For readbacks, caching is more important than coherency.
+ flags &= ~VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ if (!GetMemoryType(bits, flags, &type_index))
+ {
+ WARN_LOG(VIDEO, "Vulkan: Failed to find a cached memory type for readbacks, this will affect "
+ "performance.");
+
+ // Remove the cached bit as well.
+ flags &= ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ if (!GetMemoryType(bits, flags, &type_index))
+ {
+ // We shouldn't have any memory types that aren't host-visible.
+ PanicAlert("Unable to get memory type for upload.");
+ type_index = 0;
+ }
+ }
+ }
+
+ if (is_coherent)
+ *is_coherent = ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0);
+ if (is_cached)
+ *is_cached = ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0);
+
+ return type_index;
+}
+}