diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index f13418355..69d1c04ac 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -28,7 +28,8 @@ SpirvShaderTranslator::Features::Features(bool all) max_storage_buffer_range(all ? UINT32_MAX : (128 * 1024 * 1024)), clip_distance(all), cull_distance(all), - float_controls(all) {} + signed_zero_inf_nan_preserve_float32(all), + denorm_flush_to_zero_float32(all) {} SpirvShaderTranslator::Features::Features( const ui::vulkan::VulkanProvider& provider) @@ -48,8 +49,18 @@ SpirvShaderTranslator::Features::Features( } else { spirv_version = spv::Spv_1_0; } - float_controls = spirv_version >= spv::Spv_1_4 || - device_extensions.khr_shader_float_controls; + if (spirv_version >= spv::Spv_1_4 || + device_extensions.khr_shader_float_controls) { + const VkPhysicalDeviceFloatControlsPropertiesKHR& + float_controls_properties = provider.device_float_controls_properties(); + signed_zero_inf_nan_preserve_float32 = + bool(float_controls_properties.shaderSignedZeroInfNanPreserveFloat32); + denorm_flush_to_zero_float32 = + bool(float_controls_properties.shaderDenormFlushToZeroFloat32); + } else { + signed_zero_inf_nan_preserve_float32 = false; + denorm_flush_to_zero_float32 = false; + } } SpirvShaderTranslator::SpirvShaderTranslator(const Features& features) @@ -82,7 +93,8 @@ void SpirvShaderTranslator::StartTranslation() { builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation : spv::CapabilityShader); if (features_.spirv_version < spv::Spv_1_4) { - if (features_.float_controls) { + if (features_.signed_zero_inf_nan_preserve_float32 || + features_.denorm_flush_to_zero_float32) { builder_->addExtension("SPV_KHR_float_controls"); } } @@ -511,21 +523,21 @@ std::vector SpirvShaderTranslator::CompleteTranslation() { ? spv::ExecutionModelTessellationEvaluation : spv::ExecutionModelVertex; } - // TODO(Triang3l): Re-enable float controls when - // VkPhysicalDeviceFloatControlsPropertiesKHR are handled. - /* if (features_.float_controls) { + if (features_.denorm_flush_to_zero_float32) { // Flush to zero, similar to the real hardware, also for things like Shader // Model 3 multiplication emulation. builder_->addCapability(spv::CapabilityDenormFlushToZero); builder_->addExecutionMode(function_main_, spv::ExecutionModeDenormFlushToZero, 32); + } + if (features_.signed_zero_inf_nan_preserve_float32) { // Signed zero used to get VFACE from ps_param_gen, also special behavior // for infinity in certain instructions (such as logarithm, reciprocal, // muls_prev2). builder_->addCapability(spv::CapabilitySignedZeroInfNanPreserve); builder_->addExecutionMode(function_main_, spv::ExecutionModeSignedZeroInfNanPreserve, 32); - } */ + } spv::Instruction* entry_point = builder_->addEntryPoint(execution_model, function_main_, "main"); for (spv::Id interface_id : main_interface_) { diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index 978ad8789..d4c32dda2 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -92,7 +92,8 @@ class SpirvShaderTranslator : public ShaderTranslator { uint32_t max_storage_buffer_range; bool clip_distance; bool cull_distance; - bool float_controls; + bool signed_zero_inf_nan_preserve_float32; + bool denorm_flush_to_zero_float32; }; SpirvShaderTranslator(const Features& features); diff --git a/src/xenia/ui/vulkan/vulkan_provider.cc b/src/xenia/ui/vulkan/vulkan_provider.cc index ba43f43d0..974e543dc 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.cc +++ b/src/xenia/ui/vulkan/vulkan_provider.cc @@ -125,6 +125,12 @@ bool VulkanProvider::Initialize() { 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; if (!library_functions_loaded) { XELOGE( "Failed to get Vulkan library function pointers via " @@ -144,11 +150,58 @@ bool VulkanProvider::Initialize() { VK_SUCCESS) { instance_api_version = VK_API_VERSION_1_0; } - XELOGVK("Vulkan instance version {}.{}.{}", + 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. + std::vector instance_extension_properties; + VkResult instance_extensions_enumerate_result; + for (;;) { + uint32_t instance_extension_count = + uint32_t(instance_extension_properties.size()); + bool instance_extensions_was_empty = !instance_extension_count; + instance_extensions_enumerate_result = + lfn_.vkEnumerateInstanceExtensionProperties( + nullptr, &instance_extension_count, + instance_extensions_was_empty + ? nullptr + : instance_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_extension_properties.resize(instance_extension_count); + if (instance_extensions_enumerate_result == VK_SUCCESS && + (!instance_extensions_was_empty || !instance_extension_count)) { + break; + } + } else { + break; + } + } + if (instance_extensions_enumerate_result != VK_SUCCESS) { + instance_extension_properties.clear(); + } + std::memset(&instance_extensions_, 0, sizeof(instance_extensions_)); + if (instance_api_version >= VK_MAKE_VERSION(1, 1, 0)) { + instance_extensions_.khr_get_physical_device_properties2 = true; + } + for (const VkExtensionProperties& instance_extension : + instance_extension_properties) { + const char* instance_extension_name = instance_extension.extensionName; + if (!instance_extensions_.khr_get_physical_device_properties2 && + !std::strcmp(instance_extension_name, + "VK_KHR_get_physical_device_properties2")) { + instance_extensions_.khr_get_physical_device_properties2 = true; + } + } + XELOGVK("Vulkan instance extensions:"); + XELOGVK( + "* VK_KHR_get_physical_device_properties2: {}", + instance_extensions_.khr_get_physical_device_properties2 ? "yes" : "no"); + // Create the instance. std::vector instance_extensions_enabled; instance_extensions_enabled.push_back("VK_KHR_surface"); @@ -157,6 +210,12 @@ bool VulkanProvider::Initialize() { #elif XE_PLATFORM_WIN32 instance_extensions_enabled.push_back("VK_KHR_win32_surface"); #endif + if (instance_api_version < VK_MAKE_VERSION(1, 1, 0)) { + if (instance_extensions_.khr_get_physical_device_properties2) { + instance_extensions_enabled.push_back( + "VK_KHR_get_physical_device_properties2"); + } + } VkApplicationInfo application_info; application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; application_info.pNext = nullptr; @@ -205,11 +264,16 @@ bool VulkanProvider::Initialize() { } // Get instance functions. + std::memset(&ifn_, 0, sizeof(ifn_)); bool instance_functions_loaded = true; #define XE_VULKAN_LOAD_IFN(name) \ instance_functions_loaded &= \ (ifn_.name = PFN_##name( \ lfn_.vkGetInstanceProcAddr(instance_, #name))) != nullptr; +#define XE_VULKAN_LOAD_IFN_SYMBOL(name, symbol) \ + instance_functions_loaded &= \ + (ifn_.name = PFN_##name( \ + lfn_.vkGetInstanceProcAddr(instance_, symbol))) != nullptr; XE_VULKAN_LOAD_IFN(vkCreateDevice); XE_VULKAN_LOAD_IFN(vkDestroyDevice); XE_VULKAN_LOAD_IFN(vkDestroySurfaceKHR); @@ -229,6 +293,13 @@ bool VulkanProvider::Initialize() { #elif XE_PLATFORM_WIN32 XE_VULKAN_LOAD_IFN(vkCreateWin32SurfaceKHR); #endif + if (instance_extensions_.khr_get_physical_device_properties2) { + XE_VULKAN_LOAD_IFN_SYMBOL(vkGetPhysicalDeviceProperties2KHR, + (instance_api_version >= VK_MAKE_VERSION(1, 1, 0)) + ? "vkGetPhysicalDeviceProperties2" + : "vkGetPhysicalDeviceProperties2KHR"); + } +#undef XE_VULKAN_LOAD_IFN_SYMBOL #undef XE_VULKAN_LOAD_IFN if (!instance_functions_loaded) { XELOGE("Failed to get Vulkan instance function pointers"); @@ -470,6 +541,32 @@ bool VulkanProvider::Initialize() { "support"); return false; } + + // Get additional device properties. + std::memset(&device_float_controls_properties_, 0, + sizeof(device_float_controls_properties_)); + 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_.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR; + device_float_controls_properties_.pNext = nullptr; + device_properties_2_last->pNext = &device_float_controls_properties_; + device_properties_2_last = + reinterpret_cast( + &device_float_controls_properties_); + } + if (device_properties_2_last != &device_properties_2) { + ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_, + &device_properties_2); + } + } + XELOGVK( "Vulkan device: {} (vendor {:04X}, device {:04X}, driver {:08X}, API " "{}.{}.{})", @@ -487,6 +584,17 @@ bool VulkanProvider::Initialize() { device_extensions_.khr_shader_float_controls ? "yes" : "no"); XELOGVK("* VK_KHR_spirv_1_4: {}", device_extensions_.khr_spirv_1_4 ? "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"); + } // TODO(Triang3l): Report properties, features. // Create the device. diff --git a/src/xenia/ui/vulkan/vulkan_provider.h b/src/xenia/ui/vulkan/vulkan_provider.h index 31753472c..b8318fd00 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.h +++ b/src/xenia/ui/vulkan/vulkan_provider.h @@ -57,12 +57,21 @@ class VulkanProvider : public GraphicsProvider { PFN_vkDestroyInstance vkDestroyInstance; // From vkGetInstanceProcAddr. PFN_vkCreateInstance vkCreateInstance; + PFN_vkEnumerateInstanceExtensionProperties + vkEnumerateInstanceExtensionProperties; struct { PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion; } v_1_1; }; const LibraryFunctions& lfn() const { return lfn_; } + struct InstanceExtensions { + // Core since 1.1.0. + bool khr_get_physical_device_properties2; + }; + const InstanceExtensions& instance_extensions() const { + return instance_extensions_; + } VkInstance instance() const { return instance_; } struct InstanceFunctions { PFN_vkCreateDevice vkCreateDevice; @@ -75,6 +84,8 @@ class VulkanProvider : public GraphicsProvider { PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + // VK_KHR_get_physical_device_properties2 or 1.1.0. + PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR @@ -129,6 +140,10 @@ class VulkanProvider : public GraphicsProvider { uint32_t queue_family_graphics_compute() const { return queue_family_graphics_compute_; } + const VkPhysicalDeviceFloatControlsPropertiesKHR& + device_float_controls_properties() const { + return device_float_controls_properties_; + } VkDevice device() const { return device_; } struct DeviceFunctions { @@ -262,8 +277,9 @@ class VulkanProvider : public GraphicsProvider { LibraryFunctions lfn_ = {}; + InstanceExtensions instance_extensions_; VkInstance instance_ = VK_NULL_HANDLE; - InstanceFunctions ifn_ = {}; + InstanceFunctions ifn_; VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; VkPhysicalDeviceProperties device_properties_; @@ -274,6 +290,7 @@ class VulkanProvider : public GraphicsProvider { uint32_t memory_types_host_coherent_; uint32_t memory_types_host_cached_; uint32_t queue_family_graphics_compute_; + VkPhysicalDeviceFloatControlsPropertiesKHR device_float_controls_properties_; VkDevice device_ = VK_NULL_HANDLE; DeviceFunctions dfn_ = {};