/** ****************************************************************************** * 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. * ****************************************************************************** */ #ifndef XENIA_UI_VULKAN_VULKAN_PROVIDER_H_ #define XENIA_UI_VULKAN_VULKAN_PROVIDER_H_ #include #include #include #include #include #include #include "xenia/base/assert.h" #include "xenia/base/platform.h" #include "xenia/ui/graphics_provider.h" #include "xenia/ui/renderdoc_api.h" #if XE_PLATFORM_ANDROID #ifndef VK_USE_PLATFORM_ANDROID_KHR #define VK_USE_PLATFORM_ANDROID_KHR 1 #endif #elif XE_PLATFORM_GNU_LINUX #ifndef VK_USE_PLATFORM_XCB_KHR #define VK_USE_PLATFORM_XCB_KHR 1 #endif #elif XE_PLATFORM_WIN32 // Must be included before vulkan.h with VK_USE_PLATFORM_WIN32_KHR because it // includes Windows.h too. #include "xenia/base/platform_win.h" #ifndef VK_USE_PLATFORM_WIN32_KHR #define VK_USE_PLATFORM_WIN32_KHR 1 #endif #endif #ifndef VK_ENABLE_BETA_EXTENSIONS #define VK_ENABLE_BETA_EXTENSIONS 1 #endif #ifndef VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES 1 #endif #include "third_party/Vulkan-Headers/include/vulkan/vulkan.h" #define XELOGVK XELOGI #define XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES 1 namespace xe { namespace ui { namespace vulkan { class VulkanProvider : public GraphicsProvider { public: ~VulkanProvider(); static std::unique_ptr Create(bool is_surface_required); std::unique_ptr CreatePresenter( Presenter::HostGpuLossCallback host_gpu_loss_callback = Presenter::FatalErrorHostGpuLossCallback) override; std::unique_ptr CreateImmediateDrawer() override; const RenderdocApi& renderdoc_api() const { return renderdoc_api_; } struct LibraryFunctions { // From the module. PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; PFN_vkDestroyInstance vkDestroyInstance; // From vkGetInstanceProcAddr. PFN_vkCreateInstance vkCreateInstance; PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; struct { PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion; } v_1_1; }; const LibraryFunctions& lfn() const { return lfn_; } struct InstanceExtensions { bool ext_debug_utils; // Core since 1.1.0. bool khr_get_physical_device_properties2; // Surface extensions. bool khr_surface; #if XE_PLATFORM_ANDROID bool khr_android_surface; #elif XE_PLATFORM_GNU_LINUX bool khr_xcb_surface; #elif XE_PLATFORM_WIN32 bool khr_win32_surface; #endif }; const InstanceExtensions& instance_extensions() const { return instance_extensions_; } VkInstance instance() const { return instance_; } struct InstanceFunctions { #define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; #define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \ PFN_##extension_name extension_name; #include "xenia/ui/vulkan/functions/instance_1_0.inc" #include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc" #include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc" #include "xenia/ui/vulkan/functions/instance_khr_surface.inc" #if XE_PLATFORM_ANDROID #include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc" #elif XE_PLATFORM_GNU_LINUX #include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc" #elif XE_PLATFORM_WIN32 #include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc" #endif #undef XE_UI_VULKAN_FUNCTION_PROMOTED #undef XE_UI_VULKAN_FUNCTION }; const InstanceFunctions& ifn() const { return ifn_; } VkPhysicalDevice physical_device() const { return physical_device_; } const VkPhysicalDeviceProperties& device_properties() const { return device_properties_; } const VkPhysicalDeviceFeatures& device_features() const { return device_features_; } struct DeviceExtensions { bool ext_fragment_shader_interlock; // Core since 1.1.0. bool khr_dedicated_allocation; // Core since 1.2.0. bool khr_image_format_list; // Requires the VK_KHR_get_physical_device_properties2 instance extension. bool khr_portability_subset; // Core since 1.2.0. bool khr_shader_float_controls; // Core since 1.2.0. bool khr_spirv_1_4; bool khr_swapchain; }; const DeviceExtensions& device_extensions() const { return device_extensions_; } // Returns nullptr if the device is fully compliant with Vulkan 1.0. const VkPhysicalDevicePortabilitySubsetFeaturesKHR* device_portability_subset_features() const { if (!device_extensions_.khr_portability_subset) { return nullptr; } return &device_portability_subset_features_; } uint32_t memory_types_device_local() const { return memory_types_device_local_; } uint32_t memory_types_host_visible() const { return memory_types_host_visible_; } uint32_t memory_types_host_coherent() const { return memory_types_host_coherent_; } uint32_t memory_types_host_cached() const { return memory_types_host_cached_; } struct QueueFamily { uint32_t queue_first_index = 0; uint32_t queue_count = 0; bool potentially_supports_present = false; }; const std::vector& queue_families() const { return queue_families_; } // Required. uint32_t queue_family_graphics_compute() const { return queue_family_graphics_compute_; } // Optional, if sparse binding is supported (UINT32_MAX otherwise). May be the // same as queue_family_graphics_compute_. uint32_t queue_family_sparse_binding() const { return queue_family_sparse_binding_; } const VkPhysicalDeviceFloatControlsPropertiesKHR& device_float_controls_properties() const { return device_float_controls_properties_; } struct Queue { VkQueue queue = VK_NULL_HANDLE; std::recursive_mutex mutex; }; struct QueueAcquisition { QueueAcquisition(std::unique_lock&& lock, VkQueue queue) : lock(std::move(lock)), queue(queue) {} std::unique_lock lock; VkQueue queue; }; QueueAcquisition AcquireQueue(uint32_t index) { Queue& queue = queues_[index]; return QueueAcquisition(std::unique_lock(queue.mutex), queue.queue); } QueueAcquisition AcquireQueue(uint32_t family_index, uint32_t index) { assert_true(family_index != UINT32_MAX); return AcquireQueue(queue_families_[family_index].queue_first_index + index); } VkDevice device() const { return device_; } struct DeviceFunctions { #define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; #include "xenia/ui/vulkan/functions/device_1_0.inc" #include "xenia/ui/vulkan/functions/device_khr_swapchain.inc" #undef XE_UI_VULKAN_FUNCTION }; const DeviceFunctions& dfn() const { return dfn_; } void SetDeviceObjectName(VkObjectType type, uint64_t handle, const char* name) const; bool IsSparseBindingSupported() const { return queue_family_sparse_binding_ != UINT32_MAX; } // Samplers that may be useful for host needs. Only these samplers should be // used in host, non-emulation contexts, because the total number of samplers // is heavily limited (4000) on Nvidia GPUs - the rest of samplers are // allocated for emulation. enum class HostSampler { kNearestClamp, kLinearClamp, kNearestRepeat, kLinearRepeat, kCount, }; VkSampler GetHostSampler(HostSampler sampler) const { return host_samplers_[size_t(sampler)]; } private: explicit VulkanProvider(bool is_surface_required) : is_surface_required_(is_surface_required) {} bool Initialize(); static void AccumulateInstanceExtensions( size_t properties_count, const VkExtensionProperties* properties, bool request_debug_utils, InstanceExtensions& instance_extensions, std::vector& instance_extensions_enabled); static VkBool32 VKAPI_CALL DebugMessengerCallback( VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data); bool is_surface_required_; RenderdocApi renderdoc_api_; #if XE_PLATFORM_LINUX void* library_ = nullptr; #elif XE_PLATFORM_WIN32 HMODULE library_ = nullptr; #endif LibraryFunctions lfn_ = {}; InstanceExtensions instance_extensions_; VkInstance instance_ = VK_NULL_HANDLE; InstanceFunctions ifn_; VkDebugUtilsMessengerEXT debug_messenger_ = VK_NULL_HANDLE; bool debug_names_used_ = false; VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; VkPhysicalDeviceProperties device_properties_; VkPhysicalDeviceFeatures device_features_; DeviceExtensions device_extensions_; VkPhysicalDevicePortabilitySubsetFeaturesKHR device_portability_subset_features_; uint32_t memory_types_device_local_; uint32_t memory_types_host_visible_; uint32_t memory_types_host_coherent_; uint32_t memory_types_host_cached_; std::vector queue_families_; uint32_t queue_family_graphics_compute_; uint32_t queue_family_sparse_binding_; VkPhysicalDeviceFloatControlsPropertiesKHR device_float_controls_properties_; VkDevice device_ = VK_NULL_HANDLE; DeviceFunctions dfn_ = {}; // Queues contain a mutex, can't use std::vector. std::unique_ptr queues_; VkSampler host_samplers_[size_t(HostSampler::kCount)] = {}; }; } // namespace vulkan } // namespace ui } // namespace xe #endif // XENIA_UI_VULKAN_VULKAN_PROVIDER_H_