From 52a8ed8e6d6bbb1943be018d3b1bccf6e472308f Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 31 Oct 2020 16:22:15 +0300 Subject: [PATCH] [SPIR-V] Version, float controls --- src/xenia/gpu/shader_compiler_main.cc | 5 +- src/xenia/gpu/spirv_shader_translator.cc | 84 ++++++++++++++++------ src/xenia/gpu/spirv_shader_translator.h | 18 +++-- src/xenia/ui/vulkan/spirv_tools_context.cc | 14 +++- src/xenia/ui/vulkan/spirv_tools_context.h | 2 +- src/xenia/ui/vulkan/vulkan_provider.cc | 29 +++++++- src/xenia/ui/vulkan/vulkan_provider.h | 4 ++ 7 files changed, 122 insertions(+), 34 deletions(-) diff --git a/src/xenia/gpu/shader_compiler_main.cc b/src/xenia/gpu/shader_compiler_main.cc index f79e36df0..a9a2ed609 100644 --- a/src/xenia/gpu/shader_compiler_main.cc +++ b/src/xenia/gpu/shader_compiler_main.cc @@ -108,9 +108,10 @@ int shader_compiler_main(const std::vector& args) { shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size()); std::unique_ptr translator; + SpirvShaderTranslator::Features spirv_features(true); if (cvars::shader_output_type == "spirv" || cvars::shader_output_type == "spirvtext") { - translator = std::make_unique(); + translator = std::make_unique(spirv_features); } else if (cvars::shader_output_type == "dxbc" || cvars::shader_output_type == "dxbctext") { translator = std::make_unique( @@ -161,7 +162,7 @@ int shader_compiler_main(const std::vector& args) { spv::Disassemble(spirv_disasm_stream, spirv_source); spirv_disasm = std::move(spirv_disasm_stream.str()); ui::vulkan::SpirvToolsContext spirv_tools_context; - if (spirv_tools_context.Initialize()) { + if (spirv_tools_context.Initialize(spirv_features.spirv_version)) { std::string spirv_validation_error; spirv_tools_context.Validate( reinterpret_cast(spirv_source.data()), diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index e80a55444..3c5a1c71e 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -20,10 +20,34 @@ namespace xe { namespace gpu { -SpirvShaderTranslator::SpirvShaderTranslator(bool supports_clip_distance, - bool supports_cull_distance) - : supports_clip_distance_(supports_clip_distance), - supports_cull_distance_(supports_cull_distance) {} +SpirvShaderTranslator::Features::Features(bool all) + : spirv_version(all ? spv::Spv_1_5 : spv::Spv_1_0), + clip_distance(all), + cull_distance(all), + float_controls(all) {} + +SpirvShaderTranslator::Features::Features( + const ui::vulkan::VulkanProvider& provider) + : clip_distance(provider.device_features().shaderClipDistance), + cull_distance(provider.device_features().shaderCullDistance) { + uint32_t device_version = provider.device_properties().apiVersion; + const ui::vulkan::VulkanProvider::DeviceExtensions& device_extensions = + provider.device_extensions(); + if (device_version >= VK_MAKE_VERSION(1, 2, 0)) { + spirv_version = spv::Spv_1_5; + } else if (device_extensions.khr_spirv_1_4) { + spirv_version = spv::Spv_1_4; + } else if (device_version >= VK_MAKE_VERSION(1, 1, 0)) { + spirv_version = spv::Spv_1_3; + } else { + spirv_version = spv::Spv_1_0; + } + float_controls = spirv_version >= spv::Spv_1_4 || + device_extensions.khr_shader_float_controls; +} + +SpirvShaderTranslator::SpirvShaderTranslator(const Features& features) + : features_(features) {} void SpirvShaderTranslator::Reset() { ShaderTranslator::Reset(); @@ -32,6 +56,7 @@ void SpirvShaderTranslator::Reset() { uniform_float_constants_ = spv::NoResult; + main_interface_.clear(); var_main_registers_ = spv::NoResult; main_switch_op_.reset(); @@ -45,10 +70,16 @@ void SpirvShaderTranslator::StartTranslation() { // Tool ID 26 "Xenia Emulator Microcode Translator". // https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79 // TODO(Triang3l): Logger. - builder_ = std::make_unique(1 << 16, (26 << 16) | 1, nullptr); + builder_ = std::make_unique(features_.spirv_version, + (26 << 16) | 1, nullptr); builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation : spv::CapabilityShader); + if (features_.spirv_version < spv::Spv_1_4) { + if (features_.float_controls) { + builder_->addExtension("SPV_KHR_float_controls"); + } + } ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450"); builder_->setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); @@ -133,6 +164,9 @@ void SpirvShaderTranslator::StartTranslation() { : kDescriptorSetFloatConstantsVertex)); builder_->addDecoration(uniform_float_constants_, spv::DecorationBinding, 0); + if (features_.spirv_version >= spv::Spv_1_4) { + main_interface_.push_back(uniform_float_constants_); + } } // Common uniform buffer - bool and loop constants. @@ -168,6 +202,9 @@ void SpirvShaderTranslator::StartTranslation() { int(kDescriptorSetBoolLoopConstants)); builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding, 0); + if (features_.spirv_version >= spv::Spv_1_4) { + main_interface_.push_back(uniform_bool_loop_constants_); + } if (IsSpirvVertexOrTessEvalShader()) { StartVertexOrTessEvalShaderBeforeMain(); @@ -364,11 +401,23 @@ std::vector SpirvShaderTranslator::CompleteTranslation() { ? spv::ExecutionModelTessellationEvaluation : spv::ExecutionModelVertex; } + if (features_.float_controls) { + // 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); + // 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"); - - if (IsSpirvVertexOrTessEvalShader()) { - CompleteVertexOrTessEvalShaderAfterMain(entry_point); + for (spv::Id interface_id : main_interface_) { + entry_point->addIdOperand(interface_id); } // TODO(Triang3l): Avoid copy? @@ -721,11 +770,13 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() { spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID"); builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn, spv::BuiltInPrimitiveId); + main_interface_.push_back(input_primitive_id_); } else { input_vertex_index_ = builder_->createVariable( spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex"); builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn, spv::BuiltInVertexIndex); + main_interface_.push_back(input_vertex_index_); } // Create the entire GLSL 4.50 gl_PerVertex output similar to what glslang @@ -733,10 +784,10 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() { // ClipDistance and CullDistance may exist even if the device doesn't support // them, as long as the capabilities aren't enabled, and nothing is stored to // them. - if (supports_clip_distance_) { + if (features_.clip_distance) { builder_->addCapability(spv::CapabilityClipDistance); } - if (supports_cull_distance_) { + if (features_.cull_distance) { builder_->addCapability(spv::CapabilityCullDistance); } std::vector struct_per_vertex_members; @@ -746,7 +797,7 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() { // TODO(Triang3l): Specialization constant for ucp_cull_only_ena, for 6 + 1 // or 1 + 7 array sizes. struct_per_vertex_members.push_back(builder_->makeArrayType( - type_float_, builder_->makeUintConstant(supports_clip_distance_ ? 6 : 1), + type_float_, builder_->makeUintConstant(features_.clip_distance ? 6 : 1), 0)); struct_per_vertex_members.push_back( builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0)); @@ -777,6 +828,7 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() { output_per_vertex_ = builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput, type_struct_per_vertex, "xe_out_gl_PerVertex"); + main_interface_.push_back(output_per_vertex_); } void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() { @@ -787,16 +839,6 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() { void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {} -void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderAfterMain( - spv::Instruction* entry_point) { - if (IsSpirvTessEvalShader()) { - entry_point->addIdOperand(input_primitive_id_); - } else { - entry_point->addIdOperand(input_vertex_index_); - } - entry_point->addIdOperand(output_per_vertex_); -} - void SpirvShaderTranslator::UpdateExecConditionals( ParsedExecInstruction::Type type, uint32_t bool_constant_index, bool condition) { diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index 1350a4c39..07620f081 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -17,6 +17,7 @@ #include "third_party/glslang/SPIRV/SpvBuilder.h" #include "xenia/gpu/shader_translator.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { @@ -46,8 +47,16 @@ class SpirvShaderTranslator : public ShaderTranslator { kDescriptorSetSharedMemoryAndEdram, kDescriptorSetCount, }; - SpirvShaderTranslator(bool supports_clip_distance = true, - bool supports_cull_distance = true); + + struct Features { + explicit Features(const ui::vulkan::VulkanProvider& provider); + explicit Features(bool all = false); + unsigned int spirv_version; + bool clip_distance; + bool cull_distance; + bool float_controls; + }; + SpirvShaderTranslator(const Features& features); protected: void Reset() override; @@ -90,7 +99,6 @@ class SpirvShaderTranslator : public ShaderTranslator { void StartVertexOrTessEvalShaderBeforeMain(); void StartVertexOrTessEvalShaderInMain(); void CompleteVertexOrTessEvalShaderInMain(); - void CompleteVertexOrTessEvalShaderAfterMain(spv::Instruction* entry_point); // Updates the current flow control condition (to be called in the beginning // of exec and in jumps), closing the previous conditionals if needed. @@ -148,8 +156,7 @@ class SpirvShaderTranslator : public ShaderTranslator { spv::Id ProcessVectorAluOperation(const ParsedAluInstruction& instr, bool& predicate_written); - bool supports_clip_distance_; - bool supports_cull_distance_; + Features features_; std::unique_ptr builder_; @@ -223,6 +230,7 @@ class SpirvShaderTranslator : public ShaderTranslator { }; spv::Id output_per_vertex_; + std::vector main_interface_; spv::Function* function_main_; // bool. spv::Id var_main_predicate_; diff --git a/src/xenia/ui/vulkan/spirv_tools_context.cc b/src/xenia/ui/vulkan/spirv_tools_context.cc index 01078ca08..0565e1f60 100644 --- a/src/xenia/ui/vulkan/spirv_tools_context.cc +++ b/src/xenia/ui/vulkan/spirv_tools_context.cc @@ -26,7 +26,7 @@ namespace xe { namespace ui { namespace vulkan { -bool SpirvToolsContext::Initialize() { +bool SpirvToolsContext::Initialize(unsigned int spirv_version) { const char* vulkan_sdk_env = std::getenv("VULKAN_SDK"); if (!vulkan_sdk_env) { XELOGE("SPIRV-Tools: Failed to get the VULKAN_SDK environment variable"); @@ -63,7 +63,17 @@ bool SpirvToolsContext::Initialize() { Shutdown(); return false; } - context_ = fn_spvContextCreate_(SPV_ENV_VULKAN_1_0); + spv_target_env target_env; + if (spirv_version >= 0x10500) { + target_env = SPV_ENV_VULKAN_1_2; + } else if (spirv_version >= 0x10400) { + target_env = SPV_ENV_VULKAN_1_1_SPIRV_1_4; + } else if (spirv_version >= 0x10300) { + target_env = SPV_ENV_VULKAN_1_1; + } else { + target_env = SPV_ENV_VULKAN_1_0; + } + context_ = fn_spvContextCreate_(target_env); if (!context_) { XELOGE("SPIRV-Tools: Failed to create a Vulkan 1.0 context"); Shutdown(); diff --git a/src/xenia/ui/vulkan/spirv_tools_context.h b/src/xenia/ui/vulkan/spirv_tools_context.h index 87680c1a4..2ffea1ebd 100644 --- a/src/xenia/ui/vulkan/spirv_tools_context.h +++ b/src/xenia/ui/vulkan/spirv_tools_context.h @@ -32,7 +32,7 @@ class SpirvToolsContext { SpirvToolsContext(const SpirvToolsContext& context) = delete; SpirvToolsContext& operator=(const SpirvToolsContext& context) = delete; ~SpirvToolsContext() { Shutdown(); } - bool Initialize(); + bool Initialize(unsigned int spirv_version); void Shutdown(); spv_result_t Validate(const uint32_t* words, size_t num_words, diff --git a/src/xenia/ui/vulkan/vulkan_provider.cc b/src/xenia/ui/vulkan/vulkan_provider.cc index 179d8f40f..672b17162 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.cc +++ b/src/xenia/ui/vulkan/vulkan_provider.cc @@ -392,6 +392,10 @@ bool VulkanProvider::Initialize() { std::memset(&device_extensions_, 0, sizeof(device_extensions_)); if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) { device_extensions_.khr_dedicated_allocation = true; + if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 2, 0)) { + device_extensions_.khr_shader_float_controls = true; + device_extensions_.khr_spirv_1_4 = true; + } } bool device_supports_swapchain = false; for (const VkExtensionProperties& device_extension : @@ -405,6 +409,13 @@ bool VulkanProvider::Initialize() { !std::strcmp(device_extension_name, "VK_KHR_dedicated_allocation")) { device_extensions_.khr_dedicated_allocation = true; + } else if (!device_extensions_.khr_shader_float_controls && + !std::strcmp(device_extension_name, + "VK_KHR_shader_float_controls")) { + device_extensions_.khr_shader_float_controls = true; + } else if (!device_extensions_.khr_spirv_1_4 && + !std::strcmp(device_extension_name, "VK_KHR_spirv_1_4")) { + device_extensions_.khr_spirv_1_4 = true; } else if (!device_supports_swapchain && !std::strcmp(device_extension_name, "VK_KHR_swapchain")) { device_supports_swapchain = true; @@ -466,6 +477,10 @@ bool VulkanProvider::Initialize() { device_extensions_.ext_fragment_shader_interlock ? "yes" : "no"); XELOGVK("* VK_KHR_dedicated_allocation: {}", device_extensions_.khr_dedicated_allocation ? "yes" : "no"); + XELOGVK("* VK_KHR_shader_float_controls: {}", + device_extensions_.khr_shader_float_controls ? "yes" : "no"); + XELOGVK("* VK_KHR_spirv_1_4: {}", + device_extensions_.khr_spirv_1_4 ? "yes" : "no"); // TODO(Triang3l): Report properties, features. // Create the device. @@ -493,9 +508,17 @@ bool VulkanProvider::Initialize() { if (device_extensions_.ext_fragment_shader_interlock) { device_extensions_enabled.push_back("VK_EXT_fragment_shader_interlock"); } - if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { - if (device_extensions_.khr_dedicated_allocation) { - device_extensions_enabled.push_back("VK_KHR_dedicated_allocation"); + if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 2, 0)) { + if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + if (device_extensions_.khr_dedicated_allocation) { + device_extensions_enabled.push_back("VK_KHR_dedicated_allocation"); + } + } + if (device_extensions_.khr_shader_float_controls) { + device_extensions_enabled.push_back("VK_KHR_shader_float_controls"); + } + if (device_extensions_.khr_spirv_1_4) { + device_extensions_enabled.push_back("VK_KHR_spirv_1_4"); } } VkDeviceCreateInfo device_create_info; diff --git a/src/xenia/ui/vulkan/vulkan_provider.h b/src/xenia/ui/vulkan/vulkan_provider.h index 9fc117a50..1345dea61 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.h +++ b/src/xenia/ui/vulkan/vulkan_provider.h @@ -104,6 +104,10 @@ class VulkanProvider : public GraphicsProvider { bool ext_fragment_shader_interlock; // Core since 1.1.0. bool khr_dedicated_allocation; + // Core since 1.2.0. + bool khr_shader_float_controls; + // Core since 1.2.0. + bool khr_spirv_1_4; }; const DeviceExtensions& device_extensions() const { return device_extensions_;