Files
Xenia-Canary/src/xenia/ui/vulkan/vulkan_provider.cc
Triang3l 45050b2380 [GPU] Vulkan fragment shader interlock RB and related fixes/cleanup
Also fixes addressing of MSAA samples 2 and 3 for 64bpp color render targets in the ROV RB implementation on Direct3D 12.
Additionally, with FSI/ROV, alpha test and alpha to coverage are done only if the render target 0 was dynamically written to (according to the Direct3D 9 rules for writing to color render targets, though not sure if they actually apply to the alpha tests on Direct3D 9, but for safety).
There is also some code cleanup for things spotted during the development of the feature.
2022-10-09 22:06:41 +03:00

1316 lines
56 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_provider.h"
#include <cfloat>
#include <cstddef>
#include <cstring>
#include <utility>
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/platform.h"
#include "xenia/ui/vulkan/vulkan_immediate_drawer.h"
#include "xenia/ui/vulkan/vulkan_presenter.h"
#if XE_PLATFORM_LINUX
#include <dlfcn.h>
#elif XE_PLATFORM_WIN32
#include "xenia/base/platform_win.h"
#endif
DEFINE_bool(
vulkan_validation, false,
"Enable Vulkan validation (VK_LAYER_KHRONOS_validation). Messages will be "
"written to the OS debug log without vulkan_debug_messenger or to the "
"Xenia log with it.",
"Vulkan");
DEFINE_bool(
vulkan_debug_utils_messenger, false,
"Enable writing Vulkan debug messages via VK_EXT_debug_utils to the Xenia "
"log.",
"Vulkan");
DEFINE_uint32(
vulkan_debug_utils_messenger_severity, 2,
"Maximum severity of messages to log via the Vulkan debug messenger: 0 - "
"error, 1 - warning, 2 - info, 3 - verbose.",
"Vulkan");
DEFINE_bool(vulkan_debug_utils_names, false,
"Enable naming Vulkan objects via VK_EXT_debug_utils.", "Vulkan");
DEFINE_int32(
vulkan_device, -1,
"Index of the physical device to use, or -1 for any compatible device.",
"Vulkan");
namespace xe {
namespace ui {
namespace vulkan {
std::unique_ptr<VulkanProvider> VulkanProvider::Create(
bool is_surface_required) {
std::unique_ptr<VulkanProvider> provider(
new VulkanProvider(is_surface_required));
if (!provider->Initialize()) {
xe::FatalError(
"Unable to initialize Vulkan graphics subsystem.\n"
"\n"
"Ensure that you have the latest drivers for your GPU and it supports "
"Vulkan, and that you have the latest Vulkan runtime installed, which "
"can be downloaded at https://vulkan.lunarg.com/sdk/home.\n"
"\n"
"See https://xenia.jp/faq/ for more information and a list of "
"supported GPUs.");
return nullptr;
}
return provider;
}
VulkanProvider::~VulkanProvider() {
for (size_t i = 0; i < size_t(HostSampler::kCount); ++i) {
if (host_samplers_[i] != VK_NULL_HANDLE) {
dfn_.vkDestroySampler(device_, host_samplers_[i], nullptr);
}
}
if (device_ != VK_NULL_HANDLE) {
ifn_.vkDestroyDevice(device_, nullptr);
}
if (instance_ != VK_NULL_HANDLE) {
if (debug_messenger_ != VK_NULL_HANDLE) {
ifn_.vkDestroyDebugUtilsMessengerEXT(instance_, debug_messenger_,
nullptr);
}
lfn_.vkDestroyInstance(instance_, nullptr);
}
#if XE_PLATFORM_LINUX
if (library_) {
dlclose(library_);
}
#elif XE_PLATFORM_WIN32
if (library_) {
FreeLibrary(library_);
}
#endif
}
bool VulkanProvider::Initialize() {
renderdoc_api_.Initialize();
// Load the library.
bool library_functions_loaded = true;
#if XE_PLATFORM_LINUX
#if XE_PLATFORM_ANDROID
const char* libvulkan_name = "libvulkan.so";
#else
const char* libvulkan_name = "libvulkan.so.1";
#endif
// http://developer.download.nvidia.com/mobile/shield/assets/Vulkan/UsingtheVulkanAPI.pdf
library_ = dlopen(libvulkan_name, RTLD_NOW | RTLD_LOCAL);
if (!library_) {
XELOGE("Failed to load {}", libvulkan_name);
return false;
}
#define XE_VULKAN_LOAD_MODULE_LFN(name) \
library_functions_loaded &= \
(lfn_.name = PFN_##name(dlsym(library_, #name))) != nullptr;
#elif XE_PLATFORM_WIN32
library_ = LoadLibraryA("vulkan-1.dll");
if (!library_) {
XELOGE("Failed to load vulkan-1.dll");
return false;
}
#define XE_VULKAN_LOAD_MODULE_LFN(name) \
library_functions_loaded &= \
(lfn_.name = PFN_##name(GetProcAddress(library_, #name))) != nullptr;
#else
#error No Vulkan library loading provided for the target platform.
#endif
XE_VULKAN_LOAD_MODULE_LFN(vkGetInstanceProcAddr);
XE_VULKAN_LOAD_MODULE_LFN(vkDestroyInstance);
#undef XE_VULKAN_LOAD_MODULE_LFN
if (!library_functions_loaded) {
XELOGE("Failed to get Vulkan library function pointers");
return false;
}
library_functions_loaded &=
(lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr;
library_functions_loaded &=
(lfn_.vkEnumerateInstanceExtensionProperties =
PFN_vkEnumerateInstanceExtensionProperties(
lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE,
"vkEnumerateInstanceExtensionProperties"))) != nullptr;
library_functions_loaded &=
(lfn_.vkEnumerateInstanceLayerProperties =
PFN_vkEnumerateInstanceLayerProperties(lfn_.vkGetInstanceProcAddr(
VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"))) !=
nullptr;
if (!library_functions_loaded) {
XELOGE(
"Failed to get Vulkan library function pointers via "
"vkGetInstanceProcAddr");
return false;
}
lfn_.v_1_1.vkEnumerateInstanceVersion = PFN_vkEnumerateInstanceVersion(
lfn_.vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"));
// Get the API version.
uint32_t instance_api_version;
if (!lfn_.v_1_1.vkEnumerateInstanceVersion ||
lfn_.v_1_1.vkEnumerateInstanceVersion(&instance_api_version) !=
VK_SUCCESS) {
instance_api_version = VK_API_VERSION_1_0;
}
XELOGVK("Vulkan instance version: {}.{}.{}",
VK_VERSION_MAJOR(instance_api_version),
VK_VERSION_MINOR(instance_api_version),
VK_VERSION_PATCH(instance_api_version));
// Get the instance extensions without layers, as well as extensions promoted
// to the core.
bool debug_utils_messenger_requested = cvars::vulkan_debug_utils_messenger;
bool debug_utils_names_requested = cvars::vulkan_debug_utils_names;
bool debug_utils_requested =
debug_utils_messenger_requested || debug_utils_names_requested;
std::memset(&instance_extensions_, 0, sizeof(instance_extensions_));
if (instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
instance_extensions_.khr_get_physical_device_properties2 = true;
}
std::vector<const char*> instance_extensions_enabled;
std::vector<VkExtensionProperties> instance_or_layer_extension_properties;
VkResult instance_extensions_enumerate_result;
for (;;) {
uint32_t instance_extension_count =
uint32_t(instance_or_layer_extension_properties.size());
bool instance_extensions_were_empty = !instance_extension_count;
instance_extensions_enumerate_result =
lfn_.vkEnumerateInstanceExtensionProperties(
nullptr, &instance_extension_count,
instance_extensions_were_empty
? nullptr
: instance_or_layer_extension_properties.data());
// If the original extension count was 0 (first call), SUCCESS is returned,
// not INCOMPLETE.
if (instance_extensions_enumerate_result == VK_SUCCESS ||
instance_extensions_enumerate_result == VK_INCOMPLETE) {
instance_or_layer_extension_properties.resize(instance_extension_count);
if (instance_extensions_enumerate_result == VK_SUCCESS &&
(!instance_extensions_were_empty || !instance_extension_count)) {
break;
}
} else {
break;
}
}
if (instance_extensions_enumerate_result == VK_SUCCESS) {
AccumulateInstanceExtensions(instance_or_layer_extension_properties.size(),
instance_or_layer_extension_properties.data(),
debug_utils_requested, instance_extensions_,
instance_extensions_enabled);
}
size_t instance_extensions_enabled_count_without_layers =
instance_extensions_enabled.size();
InstanceExtensions instance_extensions_without_layers = instance_extensions_;
// Get the instance layers and their extensions.
std::vector<VkLayerProperties> layer_properties;
VkResult layers_enumerate_result;
for (;;) {
uint32_t layer_count = uint32_t(layer_properties.size());
bool layers_were_empty = !layer_count;
layers_enumerate_result = lfn_.vkEnumerateInstanceLayerProperties(
&layer_count, layers_were_empty ? nullptr : layer_properties.data());
// If the original layer count was 0 (first call), SUCCESS is returned, not
// INCOMPLETE.
if (layers_enumerate_result == VK_SUCCESS ||
layers_enumerate_result == VK_INCOMPLETE) {
layer_properties.resize(layer_count);
if (layers_enumerate_result == VK_SUCCESS &&
(!layers_were_empty || !layer_count)) {
break;
}
} else {
break;
}
}
if (layers_enumerate_result != VK_SUCCESS) {
layer_properties.clear();
}
struct {
bool khronos_validation;
} layer_enabled_flags = {};
std::vector<const char*> layers_enabled;
for (const VkLayerProperties& layer : layer_properties) {
// Check if the layer is needed.
// Checking if already enabled as an optimization to do fewer and fewer
// string comparisons. Adding literals to layers_enabled for the most C
// string lifetime safety.
if (!layer_enabled_flags.khronos_validation && cvars::vulkan_validation &&
!std::strcmp(layer.layerName, "VK_LAYER_KHRONOS_validation")) {
layers_enabled.push_back("VK_LAYER_KHRONOS_validation");
layer_enabled_flags.khronos_validation = true;
} else {
// Not enabling this layer, so don't need the extensions from it as well.
continue;
}
// Load extensions from the layer.
instance_or_layer_extension_properties.clear();
for (;;) {
uint32_t instance_extension_count =
uint32_t(instance_or_layer_extension_properties.size());
bool instance_extensions_were_empty = !instance_extension_count;
instance_extensions_enumerate_result =
lfn_.vkEnumerateInstanceExtensionProperties(
layer.layerName, &instance_extension_count,
instance_extensions_were_empty
? nullptr
: instance_or_layer_extension_properties.data());
// If the original extension count was 0 (first call), SUCCESS is
// returned, not INCOMPLETE.
if (instance_extensions_enumerate_result == VK_SUCCESS ||
instance_extensions_enumerate_result == VK_INCOMPLETE) {
instance_or_layer_extension_properties.resize(instance_extension_count);
if (instance_extensions_enumerate_result == VK_SUCCESS &&
(!instance_extensions_were_empty || !instance_extension_count)) {
break;
}
} else {
break;
}
}
if (instance_extensions_enumerate_result == VK_SUCCESS) {
AccumulateInstanceExtensions(
instance_or_layer_extension_properties.size(),
instance_or_layer_extension_properties.data(), debug_utils_requested,
instance_extensions_, instance_extensions_enabled);
}
}
// Create the instance.
VkApplicationInfo application_info;
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = nullptr;
application_info.pApplicationName = "Xenia";
application_info.applicationVersion = 1;
application_info.pEngineName = nullptr;
application_info.engineVersion = 0;
// "apiVersion must be the highest version of Vulkan that the application is
// designed to use"
// "Vulkan 1.0 implementations were required to return
// VK_ERROR_INCOMPATIBLE_DRIVER if apiVersion was larger than 1.0"
application_info.apiVersion =
instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)
? VK_HEADER_VERSION_COMPLETE
: instance_api_version;
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 = &application_info;
instance_create_info.enabledLayerCount = uint32_t(layers_enabled.size());
instance_create_info.ppEnabledLayerNames = layers_enabled.data();
instance_create_info.enabledExtensionCount =
uint32_t(instance_extensions_enabled.size());
instance_create_info.ppEnabledExtensionNames =
instance_extensions_enabled.data();
VkResult instance_create_result =
lfn_.vkCreateInstance(&instance_create_info, nullptr, &instance_);
if (instance_create_result != VK_SUCCESS) {
if ((instance_create_result == VK_ERROR_LAYER_NOT_PRESENT ||
instance_create_result == VK_ERROR_EXTENSION_NOT_PRESENT) &&
!layers_enabled.empty()) {
XELOGE("Failed to enable Vulkan layers");
// Try to create without layers and their extensions.
std::memset(&layer_enabled_flags, 0, sizeof(layer_enabled_flags));
instance_create_info.enabledLayerCount = 0;
instance_create_info.ppEnabledLayerNames = nullptr;
instance_create_info.enabledExtensionCount =
uint32_t(instance_extensions_enabled_count_without_layers);
instance_extensions_ = instance_extensions_without_layers;
instance_create_result =
lfn_.vkCreateInstance(&instance_create_info, nullptr, &instance_);
}
if (instance_create_result != VK_SUCCESS) {
XELOGE("Failed to create a Vulkan instance");
return false;
}
}
// Get instance functions.
std::memset(&ifn_, 0, sizeof(ifn_));
#define XE_UI_VULKAN_FUNCTION(name) \
functions_loaded &= (ifn_.name = PFN_##name(lfn_.vkGetInstanceProcAddr( \
instance_, #name))) != nullptr;
#define XE_UI_VULKAN_FUNCTION_DONT_PROMOTE(extension_name, core_name) \
functions_loaded &= \
(ifn_.extension_name = PFN_##extension_name(lfn_.vkGetInstanceProcAddr( \
instance_, #extension_name))) != nullptr;
#define XE_UI_VULKAN_FUNCTION_PROMOTE(extension_name, core_name) \
functions_loaded &= \
(ifn_.extension_name = PFN_##extension_name( \
lfn_.vkGetInstanceProcAddr(instance_, #core_name))) != nullptr;
// Core - require unconditionally.
{
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
if (!functions_loaded) {
XELOGE("Failed to get Vulkan instance function pointers");
return false;
}
}
// Extensions - disable the specific extension if failed to get its functions.
if (instance_extensions_.ext_debug_utils) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc"
instance_extensions_.ext_debug_utils = functions_loaded;
}
if (instance_extensions_.khr_get_physical_device_properties2) {
bool functions_loaded = true;
if (instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_PROMOTE
#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
} else {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
}
instance_extensions_.khr_get_physical_device_properties2 = functions_loaded;
}
if (instance_extensions_.khr_surface) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
instance_extensions_.khr_surface = functions_loaded;
}
#if XE_PLATFORM_ANDROID
if (instance_extensions_.khr_android_surface) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
instance_extensions_.khr_android_surface = functions_loaded;
}
#elif XE_PLATFORM_GNU_LINUX
if (instance_extensions_.khr_xcb_surface) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
instance_extensions_.khr_xcb_surface = functions_loaded;
}
#elif XE_PLATFORM_WIN32
if (instance_extensions_.khr_win32_surface) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
instance_extensions_.khr_win32_surface = functions_loaded;
}
#endif // XE_PLATFORM
#undef XE_UI_VULKAN_FUNCTION_PROMOTE
#undef XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#undef XE_UI_VULKAN_FUNCTION
// Check if surface is supported after verifying that surface extension
// function pointers could be obtained.
if (is_surface_required_ &&
!VulkanPresenter::GetSurfaceTypesSupportedByInstance(
instance_extensions_)) {
XELOGE(
"The Vulkan instance doesn't support the required surface extension "
"for the platform");
return false;
}
// Report instance information after verifying that extension function
// pointers could be obtained.
XELOGVK("Vulkan layers enabled by Xenia:");
XELOGVK("* VK_LAYER_KHRONOS_validation: {}",
layer_enabled_flags.khronos_validation ? "yes" : "no");
XELOGVK("Vulkan instance extensions:");
XELOGVK("* VK_EXT_debug_utils: {}",
instance_extensions_.ext_debug_utils
? "yes"
: (debug_utils_requested ? "no" : "not requested"));
XELOGVK(
"* VK_KHR_get_physical_device_properties2: {}",
instance_extensions_.khr_get_physical_device_properties2 ? "yes" : "no");
XELOGVK("* VK_KHR_surface: {}",
instance_extensions_.khr_surface ? "yes" : "no");
#if XE_PLATFORM_ANDROID
XELOGVK(" * VK_KHR_android_surface: {}",
instance_extensions_.khr_android_surface ? "yes" : "no");
#elif XE_PLATFORM_GNU_LINUX
XELOGVK(" * VK_KHR_xcb_surface: {}",
instance_extensions_.khr_xcb_surface ? "yes" : "no");
#elif XE_PLATFORM_WIN32
XELOGVK(" * VK_KHR_win32_surface: {}",
instance_extensions_.khr_win32_surface ? "yes" : "no");
#endif
// Enable the debug messenger.
if (debug_utils_messenger_requested) {
if (instance_extensions_.ext_debug_utils) {
VkDebugUtilsMessengerCreateInfoEXT debug_messenger_create_info;
debug_messenger_create_info.sType =
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debug_messenger_create_info.pNext = nullptr;
debug_messenger_create_info.flags = 0;
debug_messenger_create_info.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
if (cvars::vulkan_debug_utils_messenger_severity >= 1) {
debug_messenger_create_info.messageSeverity |=
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
if (cvars::vulkan_debug_utils_messenger_severity >= 2) {
debug_messenger_create_info.messageSeverity |=
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
if (cvars::vulkan_debug_utils_messenger_severity >= 3) {
debug_messenger_create_info.messageSeverity |=
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
}
}
}
debug_messenger_create_info.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;
debug_messenger_create_info.pfnUserCallback = DebugMessengerCallback;
debug_messenger_create_info.pUserData = this;
ifn_.vkCreateDebugUtilsMessengerEXT(
instance_, &debug_messenger_create_info, nullptr, &debug_messenger_);
}
if (debug_messenger_ != VK_NULL_HANDLE) {
XELOGVK("Vulkan debug messenger enabled");
} else {
XELOGE("Failed to enable the Vulkan debug messenger");
}
}
debug_names_used_ =
debug_utils_names_requested && instance_extensions_.ext_debug_utils;
// Get the compatible physical device.
std::vector<VkPhysicalDevice> physical_devices;
for (;;) {
uint32_t physical_device_count = uint32_t(physical_devices.size());
bool physical_devices_were_empty = !physical_device_count;
VkResult physical_device_enumerate_result = ifn_.vkEnumeratePhysicalDevices(
instance_, &physical_device_count,
physical_devices_were_empty ? nullptr : physical_devices.data());
// If the original device count was 0 (first call), SUCCESS is returned, not
// INCOMPLETE.
if (physical_device_enumerate_result == VK_SUCCESS ||
physical_device_enumerate_result == VK_INCOMPLETE) {
physical_devices.resize(physical_device_count);
if (physical_device_enumerate_result == VK_SUCCESS &&
(!physical_devices_were_empty || !physical_device_count)) {
break;
}
} else {
XELOGE("Failed to enumerate Vulkan physical devices");
return false;
}
}
if (physical_devices.empty()) {
XELOGE("No Vulkan physical devices are available");
return false;
}
size_t physical_device_index_first, physical_device_index_last;
if (cvars::vulkan_device >= 0) {
physical_device_index_first = uint32_t(cvars::vulkan_device);
physical_device_index_last = physical_device_index_first;
if (physical_device_index_first >= physical_devices.size()) {
XELOGE(
"vulkan_device config variable is out of range, {} devices are "
"available",
physical_devices.size());
return false;
}
} else {
physical_device_index_first = 0;
physical_device_index_last = physical_devices.size() - 1;
}
physical_device_ = VK_NULL_HANDLE;
std::vector<VkQueueFamilyProperties> queue_families_properties;
std::vector<VkExtensionProperties> device_extension_properties;
std::vector<const char*> device_extensions_enabled;
for (size_t i = physical_device_index_first; i <= physical_device_index_last;
++i) {
VkPhysicalDevice physical_device_current = physical_devices[i];
// Get physical device features. Need this before obtaining the queues as
// sparse binding is an optional feature.
ifn_.vkGetPhysicalDeviceFeatures(physical_device_current,
&device_features_);
// Get the needed queues:
// - Graphics and compute.
// - Sparse binding if used (preferably the same as the graphics and compute
// one for the lowest latency as Xenia submits sparse binding commands
// right before graphics commands anyway).
// - Additional queues for presentation as VulkanProvider may be used with
// different surfaces, and they may have varying support of presentation
// from different queue families.
uint32_t queue_family_count = 0;
ifn_.vkGetPhysicalDeviceQueueFamilyProperties(physical_device_current,
&queue_family_count, nullptr);
queue_families_properties.resize(queue_family_count);
ifn_.vkGetPhysicalDeviceQueueFamilyProperties(
physical_device_current, &queue_family_count,
queue_families_properties.data());
assert_true(queue_family_count == queue_families_properties.size());
// Initialize all queue families to unused.
queue_families_.clear();
queue_families_.resize(queue_family_count);
// First, try to obtain a graphics and compute queue. Preferably find a
// queue with sparse binding support as well.
// The family indices here are listed from the best to the worst.
uint32_t queue_family_graphics_compute_sparse_binding = UINT32_MAX;
uint32_t queue_family_graphics_compute_only = UINT32_MAX;
for (uint32_t j = 0; j < queue_family_count; ++j) {
const VkQueueFamilyProperties& queue_family_properties =
queue_families_properties[j];
if ((queue_family_properties.queueFlags &
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) !=
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) {
continue;
}
uint32_t* queue_family_ptr;
if (device_features_.sparseBinding &&
(queue_family_properties.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) {
queue_family_ptr = &queue_family_graphics_compute_sparse_binding;
} else {
queue_family_ptr = &queue_family_graphics_compute_only;
}
if (*queue_family_ptr == UINT32_MAX) {
*queue_family_ptr = j;
}
}
if (queue_family_graphics_compute_sparse_binding != UINT32_MAX) {
assert_true(device_features_.sparseBinding);
queue_family_graphics_compute_ =
queue_family_graphics_compute_sparse_binding;
} else if (queue_family_graphics_compute_only != UINT32_MAX) {
queue_family_graphics_compute_ = queue_family_graphics_compute_only;
} else {
// No graphics and compute queue family.
continue;
}
// Mark the graphics and compute queue as requested.
queue_families_[queue_family_graphics_compute_].queue_count =
std::max(queue_families_[queue_family_graphics_compute_].queue_count,
uint32_t(1));
// Request a separate sparse binding queue if needed.
queue_family_sparse_binding_ = UINT32_MAX;
if (device_features_.sparseBinding) {
if (queue_families_properties[queue_family_graphics_compute_].queueFlags &
VK_QUEUE_SPARSE_BINDING_BIT) {
queue_family_sparse_binding_ = queue_family_graphics_compute_;
} else {
for (uint32_t j = 0; j < queue_family_count; ++j) {
if (!(queue_families_properties[j].queueFlags &
VK_QUEUE_SPARSE_BINDING_BIT)) {
continue;
}
queue_family_sparse_binding_ = j;
queue_families_[j].queue_count =
std::max(queue_families_[j].queue_count, uint32_t(1));
break;
}
}
// Don't expose, and disable during logical device creature, the sparse
// binding feature if failed to obtain a queue supporting it.
if (queue_family_sparse_binding_ == UINT32_MAX) {
device_features_.sparseBinding = VK_FALSE;
}
}
bool any_queue_potentially_supports_present = false;
if (instance_extensions_.khr_surface) {
// Request possible presentation queues.
for (uint32_t j = 0; j < queue_family_count; ++j) {
#if XE_PLATFORM_WIN32
if (instance_extensions_.khr_win32_surface &&
!ifn_.vkGetPhysicalDeviceWin32PresentationSupportKHR(
physical_device_current, j)) {
continue;
}
#endif
any_queue_potentially_supports_present = true;
QueueFamily& queue_family = queue_families_[j];
queue_family.queue_count =
std::max(queue_families_[j].queue_count, uint32_t(1));
queue_family.potentially_supports_present = true;
}
}
if (!any_queue_potentially_supports_present && is_surface_required_) {
continue;
}
// Get device properties, will be needed to check if extensions have been
// promoted to core.
ifn_.vkGetPhysicalDeviceProperties(physical_device_current,
&device_properties_);
// Get the extensions, check if swapchain is supported.
device_extension_properties.clear();
VkResult device_extensions_enumerate_result;
for (;;) {
uint32_t device_extension_count =
uint32_t(device_extension_properties.size());
bool device_extensions_were_empty = !device_extension_count;
device_extensions_enumerate_result =
ifn_.vkEnumerateDeviceExtensionProperties(
physical_device_current, nullptr, &device_extension_count,
device_extensions_were_empty
? nullptr
: device_extension_properties.data());
// If the original extension count was 0 (first call), SUCCESS is
// returned, not INCOMPLETE.
if (device_extensions_enumerate_result == VK_SUCCESS ||
device_extensions_enumerate_result == VK_INCOMPLETE) {
device_extension_properties.resize(device_extension_count);
if (device_extensions_enumerate_result == VK_SUCCESS &&
(!device_extensions_were_empty || !device_extension_count)) {
break;
}
} else {
break;
}
}
if (device_extensions_enumerate_result != VK_SUCCESS) {
continue;
}
std::memset(&device_extensions_, 0, sizeof(device_extensions_));
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
device_extensions_.khr_bind_memory2 = true;
device_extensions_.khr_dedicated_allocation = true;
device_extensions_.khr_get_memory_requirements2 = true;
device_extensions_.khr_sampler_ycbcr_conversion = true;
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
device_extensions_.khr_image_format_list = true;
device_extensions_.khr_shader_float_controls = true;
device_extensions_.khr_spirv_1_4 = true;
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
device_extensions_.ext_shader_demote_to_helper_invocation = true;
device_extensions_.khr_maintenance4 = true;
}
}
}
device_extensions_enabled.clear();
// Checking if already enabled as an optimization to do fewer and fewer
// string comparisons, as well as to skip adding extensions promoted to the
// core to device_extensions_enabled. Adding literals to
// device_extensions_enabled for the most C string lifetime safety.
static const std::pair<const char*, size_t> kUsedDeviceExtensions[] = {
{"VK_EXT_fragment_shader_interlock",
offsetof(DeviceExtensions, ext_fragment_shader_interlock)},
{"VK_EXT_memory_budget", offsetof(DeviceExtensions, ext_memory_budget)},
{"VK_EXT_shader_demote_to_helper_invocation",
offsetof(DeviceExtensions, ext_shader_demote_to_helper_invocation)},
{"VK_EXT_shader_stencil_export",
offsetof(DeviceExtensions, ext_shader_stencil_export)},
{"VK_KHR_bind_memory2", offsetof(DeviceExtensions, khr_bind_memory2)},
{"VK_KHR_dedicated_allocation",
offsetof(DeviceExtensions, khr_dedicated_allocation)},
{"VK_KHR_get_memory_requirements2",
offsetof(DeviceExtensions, khr_get_memory_requirements2)},
{"VK_KHR_image_format_list",
offsetof(DeviceExtensions, khr_image_format_list)},
{"VK_KHR_maintenance4", offsetof(DeviceExtensions, khr_maintenance4)},
{"VK_KHR_portability_subset",
offsetof(DeviceExtensions, khr_portability_subset)},
// While vkGetPhysicalDeviceFormatProperties should be used to check the
// format support (device support for Y'CbCr formats is not required by
// this extension or by Vulkan 1.1), still adding
// VK_KHR_sampler_ycbcr_conversion to this list to enable this extension
// on the device on Vulkan 1.0.
{"VK_KHR_sampler_ycbcr_conversion",
offsetof(DeviceExtensions, khr_sampler_ycbcr_conversion)},
{"VK_KHR_shader_float_controls",
offsetof(DeviceExtensions, khr_shader_float_controls)},
{"VK_KHR_spirv_1_4", offsetof(DeviceExtensions, khr_spirv_1_4)},
{"VK_KHR_swapchain", offsetof(DeviceExtensions, khr_swapchain)},
};
for (const VkExtensionProperties& device_extension :
device_extension_properties) {
for (const std::pair<const char*, size_t>& used_device_extension :
kUsedDeviceExtensions) {
bool& device_extension_flag = *reinterpret_cast<bool*>(
reinterpret_cast<char*>(&device_extensions_) +
used_device_extension.second);
if (!device_extension_flag &&
!std::strcmp(device_extension.extensionName,
used_device_extension.first)) {
device_extensions_enabled.push_back(used_device_extension.first);
device_extension_flag = true;
}
}
}
if (is_surface_required_ && !device_extensions_.khr_swapchain) {
continue;
}
// Get portability subset features.
// VK_KHR_portability_subset reduces, not increases, the capabilities, skip
// the device completely if there's no way to retrieve what is actually
// unsupported. Though VK_KHR_portability_subset requires
// VK_KHR_get_physical_device_properties2, check just in case of an
// untrustworthy driver.
if (device_extensions_.khr_portability_subset) {
if (!instance_extensions_.khr_get_physical_device_properties2) {
continue;
}
device_portability_subset_features_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR;
device_portability_subset_features_.pNext = nullptr;
VkPhysicalDeviceProperties2KHR device_properties_2;
device_properties_2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
device_properties_2.pNext = &device_portability_subset_features_;
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
&device_properties_2);
}
// Get the memory types.
VkPhysicalDeviceMemoryProperties memory_properties;
ifn_.vkGetPhysicalDeviceMemoryProperties(physical_device_current,
&memory_properties);
memory_types_device_local_ = 0;
memory_types_host_visible_ = 0;
memory_types_host_coherent_ = 0;
memory_types_host_cached_ = 0;
for (uint32_t j = 0; j < memory_properties.memoryTypeCount; ++j) {
VkMemoryPropertyFlags memory_property_flags =
memory_properties.memoryTypes[j].propertyFlags;
uint32_t memory_type_bit = uint32_t(1) << j;
if (memory_property_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
memory_types_device_local_ |= memory_type_bit;
}
if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
memory_types_host_visible_ |= memory_type_bit;
}
if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
memory_types_host_coherent_ |= memory_type_bit;
}
if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
memory_types_host_cached_ |= memory_type_bit;
}
}
if (!memory_types_device_local_ && !memory_types_host_visible_) {
// Shouldn't happen according to the specification.
continue;
}
physical_device_ = physical_device_current;
break;
}
if (physical_device_ == VK_NULL_HANDLE) {
XELOGE(
"Failed to get a compatible Vulkan physical device with swapchain "
"support");
return false;
}
// Get additional device properties.
std::memset(&device_float_controls_properties_, 0,
sizeof(device_float_controls_properties_));
device_float_controls_properties_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
std::memset(&device_fragment_shader_interlock_features_, 0,
sizeof(device_fragment_shader_interlock_features_));
device_fragment_shader_interlock_features_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
std::memset(&device_shader_demote_to_helper_invocation_features_, 0,
sizeof(device_shader_demote_to_helper_invocation_features_));
device_shader_demote_to_helper_invocation_features_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
if (instance_extensions_.khr_get_physical_device_properties2) {
VkPhysicalDeviceProperties2KHR device_properties_2;
device_properties_2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
device_properties_2.pNext = nullptr;
VkPhysicalDeviceProperties2KHR* device_properties_2_last =
&device_properties_2;
if (device_extensions_.khr_shader_float_controls) {
device_float_controls_properties_.pNext = nullptr;
device_properties_2_last->pNext = &device_float_controls_properties_;
device_properties_2_last =
reinterpret_cast<VkPhysicalDeviceProperties2KHR*>(
&device_float_controls_properties_);
}
if (device_properties_2_last != &device_properties_2) {
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
&device_properties_2);
}
VkPhysicalDeviceFeatures2KHR device_features_2;
device_features_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
device_features_2.pNext = nullptr;
VkPhysicalDeviceFeatures2KHR* device_features_2_last = &device_features_2;
if (device_extensions_.ext_fragment_shader_interlock) {
device_fragment_shader_interlock_features_.pNext = nullptr;
device_features_2_last->pNext =
&device_fragment_shader_interlock_features_;
device_features_2_last = reinterpret_cast<VkPhysicalDeviceFeatures2KHR*>(
&device_fragment_shader_interlock_features_);
}
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
device_shader_demote_to_helper_invocation_features_.pNext = nullptr;
device_features_2_last->pNext =
&device_shader_demote_to_helper_invocation_features_;
device_features_2_last = reinterpret_cast<VkPhysicalDeviceFeatures2KHR*>(
&device_shader_demote_to_helper_invocation_features_);
}
if (device_features_2_last != &device_features_2) {
ifn_.vkGetPhysicalDeviceFeatures2KHR(physical_device_,
&device_features_2);
}
}
// Create the device.
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
queue_create_infos.reserve(queue_families_.size());
uint32_t used_queue_count = 0;
uint32_t max_queue_count_per_family = 0;
for (size_t i = 0; i < queue_families_.size(); ++i) {
QueueFamily& queue_family = queue_families_[i];
queue_family.queue_first_index = used_queue_count;
if (!queue_family.queue_count) {
continue;
}
VkDeviceQueueCreateInfo& queue_create_info =
queue_create_infos.emplace_back();
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_info.pNext = nullptr;
queue_create_info.flags = 0;
queue_create_info.queueFamilyIndex = uint32_t(i);
queue_create_info.queueCount = queue_family.queue_count;
// pQueuePriorities will be set later based on max_queue_count_per_family.
max_queue_count_per_family =
std::max(max_queue_count_per_family, queue_family.queue_count);
used_queue_count += queue_family.queue_count;
}
std::vector<float> queue_priorities;
queue_priorities.resize(max_queue_count_per_family, 1.0f);
for (VkDeviceQueueCreateInfo& queue_create_info : queue_create_infos) {
queue_create_info.pQueuePriorities = queue_priorities.data();
}
VkDeviceCreateInfo device_create_info;
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.pNext = nullptr;
VkDeviceCreateInfo* device_create_info_last = &device_create_info;
device_create_info.flags = 0;
device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size());
device_create_info.pQueueCreateInfos = queue_create_infos.data();
// Device layers are deprecated - using validation layer on the instance.
device_create_info.enabledLayerCount = 0;
device_create_info.ppEnabledLayerNames = nullptr;
device_create_info.enabledExtensionCount =
uint32_t(device_extensions_enabled.size());
device_create_info.ppEnabledExtensionNames = device_extensions_enabled.data();
// TODO(Triang3l): Enable only needed features.
device_create_info.pEnabledFeatures = &device_features_;
if (device_extensions_.khr_portability_subset) {
// TODO(Triang3l): Enable only needed portability subset features.
device_portability_subset_features_.pNext = nullptr;
device_create_info_last->pNext = &device_portability_subset_features_;
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
&device_portability_subset_features_);
}
if (device_extensions_.ext_fragment_shader_interlock) {
// TODO(Triang3l): Enable only needed fragment shader interlock features.
device_fragment_shader_interlock_features_.pNext = nullptr;
device_create_info_last->pNext =
&device_fragment_shader_interlock_features_;
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
&device_fragment_shader_interlock_features_);
}
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
device_shader_demote_to_helper_invocation_features_.pNext = nullptr;
device_create_info_last->pNext =
&device_shader_demote_to_helper_invocation_features_;
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
&device_shader_demote_to_helper_invocation_features_);
}
if (ifn_.vkCreateDevice(physical_device_, &device_create_info, nullptr,
&device_) != VK_SUCCESS) {
XELOGE("Failed to create a Vulkan device");
return false;
}
// Get device functions.
std::memset(&dfn_, 0, sizeof(ifn_));
bool device_functions_loaded = true;
#define XE_UI_VULKAN_FUNCTION(name) \
functions_loaded &= \
(dfn_.name = PFN_##name(ifn_.vkGetDeviceProcAddr(device_, #name))) != \
nullptr;
#define XE_UI_VULKAN_FUNCTION_DONT_PROMOTE(extension_name, core_name) \
functions_loaded &= \
(dfn_.extension_name = PFN_##extension_name( \
ifn_.vkGetDeviceProcAddr(device_, #extension_name))) != nullptr;
#define XE_UI_VULKAN_FUNCTION_PROMOTE(extension_name, core_name) \
functions_loaded &= \
(dfn_.extension_name = PFN_##extension_name( \
ifn_.vkGetDeviceProcAddr(device_, #core_name))) != nullptr;
// Core - require unconditionally.
{
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/device_1_0.inc"
if (!functions_loaded) {
XELOGE("Failed to get Vulkan device function pointers");
return false;
}
}
// Extensions - disable the specific extension if failed to get its functions.
if (device_extensions_.khr_bind_memory2) {
bool functions_loaded = true;
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_bind_memory2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
} else {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_bind_memory2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
}
device_extensions_.khr_bind_memory2 = functions_loaded;
}
if (device_extensions_.khr_get_memory_requirements2) {
bool functions_loaded = true;
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_get_memory_requirements2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
} else {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_get_memory_requirements2.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
}
device_extensions_.khr_get_memory_requirements2 = functions_loaded;
// VK_KHR_dedicated_allocation can still work without the dedicated
// allocation preference getter even though it requires
// VK_KHR_get_memory_requirements2 to be supported and enabled.
}
if (device_extensions_.khr_maintenance4) {
bool functions_loaded = true;
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_maintenance4.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
} else {
#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#include "xenia/ui/vulkan/functions/device_khr_maintenance4.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
}
device_extensions_.khr_maintenance4 = functions_loaded;
}
if (device_extensions_.khr_swapchain) {
bool functions_loaded = true;
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
if (!functions_loaded) {
// Outside the physical device selection loop, so can't just skip the
// device anymore, but this shouldn't really happen anyway.
XELOGE(
"Failed to get Vulkan swapchain function pointers while swapchain "
"support is required");
return false;
}
device_extensions_.khr_swapchain = functions_loaded;
}
#undef XE_UI_VULKAN_FUNCTION_PROMOTE
#undef XE_UI_VULKAN_FUNCTION_DONT_PROMOTE
#undef XE_UI_VULKAN_FUNCTION
if (!device_functions_loaded) {
XELOGE("Failed to get Vulkan device function pointers");
return false;
}
// Report device information after verifying that extension function pointers
// could be obtained.
XELOGVK(
"Vulkan device: {} (vendor {:04X}, device {:04X}, driver {:08X}, API "
"{}.{}.{})",
device_properties_.deviceName, device_properties_.vendorID,
device_properties_.deviceID, device_properties_.driverVersion,
VK_VERSION_MAJOR(device_properties_.apiVersion),
VK_VERSION_MINOR(device_properties_.apiVersion),
VK_VERSION_PATCH(device_properties_.apiVersion));
XELOGVK("Vulkan device extensions:");
XELOGVK("* VK_EXT_fragment_shader_interlock: {}",
device_extensions_.ext_fragment_shader_interlock ? "yes" : "no");
if (device_extensions_.ext_fragment_shader_interlock) {
XELOGVK(
" * Sample interlock: {}",
device_fragment_shader_interlock_features_.fragmentShaderSampleInterlock
? "yes"
: "no");
XELOGVK(
" * Pixel interlock: {}",
device_fragment_shader_interlock_features_.fragmentShaderPixelInterlock
? "yes"
: "no");
}
XELOGVK("* VK_EXT_memory_budget: {}",
device_extensions_.ext_memory_budget ? "yes" : "no");
XELOGVK(
"* VK_EXT_shader_demote_to_helper_invocation: {}",
device_extensions_.ext_shader_demote_to_helper_invocation ? "yes" : "no");
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
XELOGVK(" * Demote to helper invocation: {}",
device_shader_demote_to_helper_invocation_features_
.shaderDemoteToHelperInvocation
? "yes"
: "no");
}
XELOGVK("* VK_EXT_shader_stencil_export: {}",
device_extensions_.ext_shader_stencil_export ? "yes" : "no");
XELOGVK("* VK_KHR_bind_memory2: {}",
device_extensions_.khr_bind_memory2 ? "yes" : "no");
XELOGVK("* VK_KHR_dedicated_allocation: {}",
device_extensions_.khr_dedicated_allocation ? "yes" : "no");
XELOGVK("* VK_KHR_get_memory_requirements2: {}",
device_extensions_.khr_get_memory_requirements2 ? "yes" : "no");
XELOGVK("* VK_KHR_image_format_list: {}",
device_extensions_.khr_image_format_list ? "yes" : "no");
XELOGVK("* VK_KHR_maintenance4: {}",
device_extensions_.khr_maintenance4 ? "yes" : "no");
XELOGVK("* VK_KHR_portability_subset: {}",
device_extensions_.khr_portability_subset ? "yes" : "no");
if (device_extensions_.khr_portability_subset) {
XELOGVK(" * Constant alpha color blend factors: {}",
device_portability_subset_features_.constantAlphaColorBlendFactors
? "yes"
: "no");
XELOGVK(" * Image view format reinterpretation: {}",
device_portability_subset_features_.imageViewFormatReinterpretation
? "yes"
: "no");
XELOGVK(" * Image view format swizzle: {}",
device_portability_subset_features_.imageViewFormatSwizzle ? "yes"
: "no");
XELOGVK(" * Point polygons: {}",
device_portability_subset_features_.pointPolygons ? "yes" : "no");
XELOGVK(
" * Separate stencil front and back masks and reference values: {}",
device_portability_subset_features_.separateStencilMaskRef ? "yes"
: "no");
XELOGVK(" * Shader sample rate interpolation functions: {}",
device_portability_subset_features_
.shaderSampleRateInterpolationFunctions
? "yes"
: "no");
XELOGVK(" * Triangle fans: {}",
device_portability_subset_features_.triangleFans ? "yes" : "no");
}
XELOGVK("* VK_KHR_sampler_ycbcr_conversion: {}",
device_extensions_.khr_sampler_ycbcr_conversion ? "yes" : "no");
XELOGVK("* VK_KHR_shader_float_controls: {}",
device_extensions_.khr_shader_float_controls ? "yes" : "no");
if (device_extensions_.khr_shader_float_controls) {
XELOGVK(
" * Signed zero, inf, nan preserve for float32: {}",
device_float_controls_properties_.shaderSignedZeroInfNanPreserveFloat32
? "yes"
: "no");
XELOGVK(" * Denorm flush to zero for float32: {}",
device_float_controls_properties_.shaderDenormFlushToZeroFloat32
? "yes"
: "no");
XELOGVK("* VK_KHR_spirv_1_4: {}",
device_extensions_.khr_spirv_1_4 ? "yes" : "no");
XELOGVK("* VK_KHR_swapchain: {}",
device_extensions_.khr_swapchain ? "yes" : "no");
}
// TODO(Triang3l): Report properties, features.
// Get the queues.
queues_.reset();
queues_ = std::make_unique<Queue[]>(used_queue_count);
uint32_t queue_index = 0;
for (size_t i = 0; i < queue_families_.size(); ++i) {
const QueueFamily& queue_family = queue_families_[i];
if (!queue_family.queue_count) {
continue;
}
assert_true(queue_index == queue_family.queue_first_index);
for (uint32_t j = 0; j < queue_family.queue_count; ++j) {
VkQueue queue;
dfn_.vkGetDeviceQueue(device_, uint32_t(i), j, &queue);
queues_[queue_index++].queue = queue;
}
}
// Create host-side samplers.
VkSamplerCreateInfo sampler_create_info = {};
sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_create_info.magFilter = VK_FILTER_NEAREST;
sampler_create_info.minFilter = VK_FILTER_NEAREST;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_create_info.maxLod = FLT_MAX;
if (dfn_.vkCreateSampler(
device_, &sampler_create_info, nullptr,
&host_samplers_[size_t(HostSampler::kNearestClamp)]) != VK_SUCCESS) {
XELOGE("Failed to create the nearest-neighbor clamping Vulkan sampler");
return false;
}
sampler_create_info.magFilter = VK_FILTER_LINEAR;
sampler_create_info.minFilter = VK_FILTER_LINEAR;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
if (dfn_.vkCreateSampler(
device_, &sampler_create_info, nullptr,
&host_samplers_[size_t(HostSampler::kLinearClamp)]) != VK_SUCCESS) {
XELOGE("Failed to create the bilinear-filtering clamping Vulkan sampler");
return false;
}
sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
if (dfn_.vkCreateSampler(
device_, &sampler_create_info, nullptr,
&host_samplers_[size_t(HostSampler::kLinearRepeat)]) != VK_SUCCESS) {
XELOGE("Failed to create the bilinear-filtering repeating Vulkan sampler");
return false;
}
sampler_create_info.magFilter = VK_FILTER_NEAREST;
sampler_create_info.minFilter = VK_FILTER_NEAREST;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
if (dfn_.vkCreateSampler(
device_, &sampler_create_info, nullptr,
&host_samplers_[size_t(HostSampler::kNearestRepeat)]) != VK_SUCCESS) {
XELOGE("Failed to create the nearest-neighbor repeating Vulkan sampler");
return false;
}
return true;
}
std::unique_ptr<Presenter> VulkanProvider::CreatePresenter(
Presenter::HostGpuLossCallback host_gpu_loss_callback) {
return VulkanPresenter::Create(host_gpu_loss_callback, *this);
}
std::unique_ptr<ImmediateDrawer> VulkanProvider::CreateImmediateDrawer() {
return VulkanImmediateDrawer::Create(*this);
}
void VulkanProvider::AccumulateInstanceExtensions(
size_t properties_count, const VkExtensionProperties* properties,
bool request_debug_utils, InstanceExtensions& instance_extensions,
std::vector<const char*>& instance_extensions_enabled) {
for (size_t i = 0; i < properties_count; ++i) {
const char* instance_extension_name = properties[i].extensionName;
// Checking if already enabled as an optimization to do fewer and fewer
// string comparisons, as well as to skip adding extensions promoted to the
// core to instance_extensions_enabled. Adding literals to
// instance_extensions_enabled for the most C string lifetime safety.
if (request_debug_utils && !instance_extensions.ext_debug_utils &&
!std::strcmp(instance_extension_name, "VK_EXT_debug_utils")) {
// Debug utilities are only enabled when needed. Overhead in Xenia not
// profiled, but better to avoid unless enabled by the user.
instance_extensions_enabled.push_back("VK_EXT_debug_utils");
instance_extensions.ext_debug_utils = true;
} else if (!instance_extensions.khr_get_physical_device_properties2 &&
!std::strcmp(instance_extension_name,
"VK_KHR_get_physical_device_properties2")) {
instance_extensions_enabled.push_back(
"VK_KHR_get_physical_device_properties2");
instance_extensions.khr_get_physical_device_properties2 = true;
} else if (!instance_extensions.khr_surface &&
!std::strcmp(instance_extension_name, "VK_KHR_surface")) {
instance_extensions_enabled.push_back("VK_KHR_surface");
instance_extensions.khr_surface = true;
} else {
#if XE_PLATFORM_ANDROID
if (!instance_extensions.khr_android_surface &&
!std::strcmp(instance_extension_name, "VK_KHR_android_surface")) {
instance_extensions_enabled.push_back("VK_KHR_android_surface");
instance_extensions.khr_android_surface = true;
}
#elif XE_PLATFORM_GNU_LINUX
if (!instance_extensions.khr_xcb_surface &&
!std::strcmp(instance_extension_name, "VK_KHR_xcb_surface")) {
instance_extensions_enabled.push_back("VK_KHR_xcb_surface");
instance_extensions.khr_xcb_surface = true;
}
#elif XE_PLATFORM_WIN32
if (!instance_extensions.khr_win32_surface &&
!std::strcmp(instance_extension_name, "VK_KHR_win32_surface")) {
instance_extensions_enabled.push_back("VK_KHR_win32_surface");
instance_extensions.khr_win32_surface = true;
}
#endif
}
}
}
VkBool32 VKAPI_CALL VulkanProvider::DebugMessengerCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_types,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
void* user_data) {
const char* severity_string;
switch (message_severity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
severity_string = "verbose output";
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
severity_string = "info";
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
severity_string = "warning";
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
severity_string = "error";
break;
default:
switch (xe::bit_count(uint32_t(message_severity))) {
case 0:
severity_string = "no-severity";
break;
case 1:
severity_string = "unknown-severity";
break;
default:
severity_string = "multi-severity";
}
}
const char* type_string;
switch (message_types) {
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
type_string = "general";
break;
case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
type_string = "validation";
break;
case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
type_string = "performance";
break;
default:
switch (xe::bit_count(uint32_t(message_types))) {
case 0:
type_string = "no-type";
break;
case 1:
type_string = "unknown-type";
break;
default:
type_string = "multi-type";
}
}
XELOGVK("Vulkan {} {}: {}", type_string, severity_string,
callback_data->pMessage);
return VK_FALSE;
}
} // namespace vulkan
} // namespace ui
} // namespace xe