diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 9386864e5e52..6595300adc8e 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -529,6 +529,7 @@ struct VulkanDevice::State { bool viewportIndexLayer = false; bool imageFormatList = false; bool fmaFloat16 = false; + bool fmaFloat32 = false; bool primitiveListRestart = false; bool depthClipControl = false; bool graphicsPipelineLibrary = false; @@ -1711,15 +1712,18 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) : state->float32DenormPreserve = floatControls.shaderDenormPreserveFloat32 == VK_TRUE && floatControls.denormBehaviorIndependence != VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE && std::getenv("APS5_NO_HW_F32_PRESERVE") == nullptr; if ((state->float16DenormPreserve || state->float32DenormPreserve) && std::find(state->capabilities.begin(), state->capabilities.end(), static_cast(spv::CapabilityDenormPreserve)) == state->capabilities.end()) state->capabilities.push_back(spv::CapabilityDenormPreserve); VkPhysicalDeviceShaderFmaFeaturesKHR fmaFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FMA_FEATURES_KHR}; - if (float16Rte && hasExtension(VK_KHR_SHADER_FMA_EXTENSION_NAME)) { + if (hasExtension(VK_KHR_SHADER_FMA_EXTENSION_NAME)) { VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &fmaFeatures}; state->InstanceFunction("vkGetPhysicalDeviceFeatures2")(selected, &features); } const bool float16Fma = float16Rte && fmaFeatures.shaderFmaFloat16 == VK_TRUE; + const bool float32Fma = hasExtension(VK_KHR_SHADER_FMA_EXTENSION_NAME) && fmaFeatures.shaderFmaFloat32 == VK_TRUE; fmaFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FMA_FEATURES_KHR}; - fmaFeatures.shaderFmaFloat16 = VK_TRUE; + fmaFeatures.shaderFmaFloat16 = float16Fma ? VK_TRUE : VK_FALSE; + fmaFeatures.shaderFmaFloat32 = float32Fma ? VK_TRUE : VK_FALSE; state->fmaFloat16 = float16Fma; - if (float16Fma) { + state->fmaFloat32 = float32Fma; + if (float16Fma || float32Fma) { deviceExtensions.push_back(VK_KHR_SHADER_FMA_EXTENSION_NAME); state->capabilities.push_back(spv::CapabilityFMAKHR); state->spirvExtensions.push_back("SPV_KHR_fma"); @@ -2209,7 +2213,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) : float16Features.pNext = byteFeatures.pNext; byteFeatures.pNext = &float16Features; } - if (float16Fma) { + if (float16Fma || float32Fma) { fmaFeatures.pNext = byteFeatures.pNext; byteFeatures.pNext = &fmaFeatures; } @@ -4585,6 +4589,7 @@ Graphics::Context VulkanDevice::buildContext() const { context.demoteToHelperInvocation = state->demoteToHelperInvocation; context.earlyFragmentSampleMaskTestBeforeSampleCounting = state->earlyFragmentSampleMaskTestBeforeSampleCounting; context.fmaFloat16 = state->fmaFloat16; + context.fmaFloat32 = state->fmaFloat32; context.primitiveListRestart = state->primitiveListRestart; context.imageViewMinLod = state->imageViewMinLod; context.shaderResourceMinLod = state->shaderResourceMinLod; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp index 1f9f7e982b65..002749c91423 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp @@ -262,6 +262,7 @@ struct Context { bool demoteToHelperInvocation = false; bool earlyFragmentSampleMaskTestBeforeSampleCounting = false; bool fmaFloat16 = false; + bool fmaFloat32 = false; bool primitiveListRestart = false; bool imageViewMinLod = false; bool shaderResourceMinLod = false; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp index 9977db556e6d..1ab672b1a94d 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp @@ -157,7 +157,7 @@ bool ShadersWriteMemory(std::span shaders); bool DeclaresEarlyFragmentTests(const ShaderRecompiler::RecompileResult& program); void ValidateSampleMask(const Context& context, const State& state, std::span shaders); // Returns the color attachment locations the pixel shader writes. -std::set ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric, bool descriptorIndexing = false, bool imageInt64Atomics = false, bool geometryShader = false, bool sampleRateShading = false, bool bufferInt64Atomics = false, bool shaderClipDistance = false, bool shaderCullDistance = false, bool viewportIndexLayer = false, bool runtimeDescriptorArray = false, bool demoteToHelperInvocation = false, bool shaderTerminateInvocation = false, bool meshShader = true, bool shaderResourceMinLod = false, bool fmaFloat16 = false); +std::set ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric, bool descriptorIndexing = false, bool imageInt64Atomics = false, bool geometryShader = false, bool sampleRateShading = false, bool bufferInt64Atomics = false, bool shaderClipDistance = false, bool shaderCullDistance = false, bool viewportIndexLayer = false, bool runtimeDescriptorArray = false, bool demoteToHelperInvocation = false, bool shaderTerminateInvocation = false, bool meshShader = true, bool shaderResourceMinLod = false, bool fmaFloat16 = false, bool fmaFloat32 = false); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 502fa6dda05a..928f9644148b 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -1139,7 +1139,7 @@ std::uint32_t CachedFragmentOutputs(const Context& context, std::span primitiveLocations = {}) { +Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric, bool descriptorIndexing, bool imageInt64Atomics, bool geometryShader, bool sampleRateShading, bool bufferInt64Atomics, bool shaderClipDistance, bool shaderCullDistance, bool viewportIndexLayer, bool runtimeDescriptorArray, bool demoteToHelperInvocation, bool shaderTerminateInvocation, bool shaderResourceMinLod, bool fmaFloat16, bool fmaFloat32, std::span primitiveLocations = {}) { using Stage = ShaderRecompiler::ShaderStage; Require(compiled.program != nullptr, "missing compiled shader"); const auto& shader = *compiled.program; @@ -299,7 +299,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi capability == spv::CapabilityRoundingModeRTE || capability == spv::CapabilityDenormPreserve || capability == spv::CapabilityDenormFlushToZero || - (fmaFloat16 && capability == spv::CapabilityFMAKHR) || + ((fmaFloat16 || fmaFloat32) && capability == spv::CapabilityFMAKHR) || capability == spv::CapabilityFloat64 || capability == spv::CapabilityStorageBuffer8BitAccess || capability == spv::CapabilityPhysicalStorageBufferAddresses || @@ -360,7 +360,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi terminateExtension = true; break; } - Require(extension == "SPV_KHR_float_controls" || (fmaFloat16 && extension == "SPV_KHR_fma") || (imageInt64Atomics && extension == "SPV_EXT_shader_image_int64") || (viewportIndexLayer && extension == "SPV_EXT_shader_viewport_index_layer") || (fragment && demoteToHelperInvocation && extension == "SPV_EXT_demote_to_helper_invocation") || (mesh && (extension == "SPV_EXT_mesh_shader" || extension == "SPV_KHR_physical_storage_buffer")) || (fragment && !primitiveLocations.empty() && extension == "SPV_EXT_mesh_shader") || (geometry && state.tessellationCapture && extension == "SPV_KHR_physical_storage_buffer") || ((descriptorIndexing || runtimeDescriptorArray) && extension == "SPV_EXT_descriptor_indexing") || (shader.bdaAbiVersion == ShaderRecompiler::BdaAbi::Version && (extension == "SPV_KHR_physical_storage_buffer" || extension == "SPV_KHR_8bit_storage")), "unsupported SPIR-V extension"); + Require(extension == "SPV_KHR_float_controls" || ((fmaFloat16 || fmaFloat32) && extension == "SPV_KHR_fma") || (imageInt64Atomics && extension == "SPV_EXT_shader_image_int64") || (viewportIndexLayer && extension == "SPV_EXT_shader_viewport_index_layer") || (fragment && demoteToHelperInvocation && extension == "SPV_EXT_demote_to_helper_invocation") || (mesh && (extension == "SPV_EXT_mesh_shader" || extension == "SPV_KHR_physical_storage_buffer")) || (fragment && !primitiveLocations.empty() && extension == "SPV_EXT_mesh_shader") || (geometry && state.tessellationCapture && extension == "SPV_KHR_physical_storage_buffer") || ((descriptorIndexing || runtimeDescriptorArray) && extension == "SPV_EXT_descriptor_indexing") || (shader.bdaAbiVersion == ShaderRecompiler::BdaAbi::Version && (extension == "SPV_KHR_physical_storage_buffer" || extension == "SPV_KHR_8bit_storage")), "unsupported SPIR-V extension"); break; } case spv::OpTerminateInvocation: @@ -670,7 +670,7 @@ bool DeclaresEarlyFragmentTests(const ShaderRecompiler::RecompileResult& program return false; } -std::set ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric, bool descriptorIndexing, bool imageInt64Atomics, bool geometryShader, bool sampleRateShading, bool bufferInt64Atomics, bool shaderClipDistance, bool shaderCullDistance, bool viewportIndexLayer, bool runtimeDescriptorArray, bool demoteToHelperInvocation, bool shaderTerminateInvocation, bool meshShader, bool shaderResourceMinLod, bool fmaFloat16) { +std::set ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric, bool descriptorIndexing, bool imageInt64Atomics, bool geometryShader, bool sampleRateShading, bool bufferInt64Atomics, bool shaderClipDistance, bool shaderCullDistance, bool viewportIndexLayer, bool runtimeDescriptorArray, bool demoteToHelperInvocation, bool shaderTerminateInvocation, bool meshShader, bool shaderResourceMinLod, bool fmaFloat16, bool fmaFloat32) { using Stage = ShaderRecompiler::ShaderStage; const bool tessellation = state.stages.path == ShaderPath::Tessellation; const bool mesh = state.stages.path == ShaderPath::Geometry; @@ -708,7 +708,7 @@ std::set ValidateShaders(std::span shaders, Require(shaders[i].program != nullptr, "missing compiled shader"); Require(shaders[i].stage == expected, "graphics stage order disagrees"); for (const auto& binding : shaders[i].program->bindings) Require(binding.descriptorSet == ShaderRecompiler::RuntimeAbi::DescriptorSet || (shaders[i].stage == ShaderRecompiler::ShaderStage::Fragment && binding.descriptorSet == ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet), "graphics resource uses a descriptor set other than its stage's"); - const auto current = Inspect(shaders[i], state, subgroup, fragmentShaderBarycentric, descriptorIndexing, imageInt64Atomics, geometryShader, sampleRateShading, bufferInt64Atomics, shaderClipDistance, shaderCullDistance, viewportIndexLayer, runtimeDescriptorArray, demoteToHelperInvocation, shaderTerminateInvocation, shaderResourceMinLod, fmaFloat16, primitiveLocations); + const auto current = Inspect(shaders[i], state, subgroup, fragmentShaderBarycentric, descriptorIndexing, imageInt64Atomics, geometryShader, sampleRateShading, bufferInt64Atomics, shaderClipDistance, shaderCullDistance, viewportIndexLayer, runtimeDescriptorArray, demoteToHelperInvocation, shaderTerminateInvocation, shaderResourceMinLod, fmaFloat16, fmaFloat32, primitiveLocations); if (i != 0) { for (const auto& [location, signature] : current.inputs) { const auto output = previous.outputs.find(location); diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp index a823f9b4b765..6cf3cb9e7afa 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp @@ -1943,6 +1943,13 @@ std::uint32_t EmitPackHalf2x16Value(SpirvEmitterState& state, const IrValue& ins } std::uint32_t EmitFPFma32(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1, std::uint32_t arg2) { + if (std::find(state.supportedCapabilities.begin(), state.supportedCapabilities.end(), static_cast(spv::CapabilityFMAKHR)) != state.supportedCapabilities.end()) { + state.module.EmitCapability(spv::CapabilityFMAKHR); + state.module.EmitExtension("SPV_KHR_fma"); + const auto result = state.module.AllocateId(); + state.module.AddFunction(spv::OpFmaKHR, TypeF32(state), result, arg0, arg1, arg2); + return Exact(state, result); + } return Exact(state, EmitGlsl(state, arg0, arg1, arg2)); }