[Vulkan] Float controls properties

This commit is contained in:
Triang3l 2020-11-15 15:08:50 +03:00
parent 8febf02a39
commit 715d614f5e
4 changed files with 149 additions and 11 deletions

View File

@ -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<uint8_t> 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_) {

View File

@ -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);

View File

@ -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<VkExtensionProperties> 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<const char*> 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<VkPhysicalDeviceProperties2KHR*>(
&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.

View File

@ -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_ = {};