diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs index fca2dca..bce01f0 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs @@ -442,6 +442,15 @@ public static partial class Gen5SpirvTranslator break; } case "VRcpF32": + result = EmitFloatResult( + instruction, + _module.AddInstruction( + SpirvOp.FDiv, + _floatType, + Float(1), + EmitFlushF32DenormToSignedZero( + GetFloatSource(instruction, 0)))); + break; case "VRcpIflagF32": result = EmitFloatResult( instruction, @@ -454,7 +463,11 @@ public static partial class Gen5SpirvTranslator case "VLogF32": result = EmitFloatResult( instruction, - Ext(30, _floatType, GetFloatSource(instruction, 0))); + Ext( + 30, + _floatType, + EmitFlushF32DenormToSignedZero( + GetFloatSource(instruction, 0)))); break; case "VLdexpF32": result = EmitFloatResult( @@ -468,12 +481,20 @@ public static partial class Gen5SpirvTranslator case "VExpF32": result = EmitFloatResult( instruction, - Ext(29, _floatType, GetFloatSource(instruction, 0))); + Ext( + 29, + _floatType, + EmitFlushF32DenormToSignedZero( + GetFloatSource(instruction, 0)))); break; case "VRsqF32": result = EmitFloatResult( instruction, - Ext(32, _floatType, GetFloatSource(instruction, 0))); + Ext( + 32, + _floatType, + EmitFlushF32DenormToSignedZero( + GetFloatSource(instruction, 0)))); break; case "VRcpF16": result = EmitFloat16Result(instruction, destination, _module.AddInstruction(SpirvOp.FDiv, _floatType, Float(1), GetFloat16Source(instruction, 0))); @@ -542,7 +563,11 @@ public static partial class Gen5SpirvTranslator case "VSqrtF32": result = EmitFloatResult( instruction, - Ext(31, _floatType, GetFloatSource(instruction, 0))); + Ext( + 31, + _floatType, + EmitFlushF32DenormToSignedZero( + GetFloatSource(instruction, 0)))); break; case "VSinF32": result = EmitFloatResult( @@ -553,7 +578,9 @@ public static partial class Gen5SpirvTranslator _module.AddInstruction( SpirvOp.FMul, _floatType, - GetFloatSource(instruction, 0), + EmitTrigCycleF32( + GetFloatSource(instruction, 0), + preserveSignedZero: true), Float(MathF.Tau)))); break; case "VCosF32": @@ -565,7 +592,9 @@ public static partial class Gen5SpirvTranslator _module.AddInstruction( SpirvOp.FMul, _floatType, - GetFloatSource(instruction, 0), + EmitTrigCycleF32( + GetFloatSource(instruction, 0), + preserveSignedZero: false), Float(MathF.Tau)))); break; case "VAddF32": diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.FloatSemantics.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.FloatSemantics.cs new file mode 100644 index 0000000..754044b --- /dev/null +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.FloatSemantics.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +namespace SharpEmu.ShaderCompiler.Vulkan; + +public static partial class Gen5SpirvTranslator +{ + private sealed partial class CompilationContext + { + private uint EmitFlushF32DenormToSignedZero(uint value) + { + var bits = Bitcast(_uintType, value); + var absolute = _module.AddInstruction( + SpirvOp.BitwiseAnd, + _uintType, + bits, + UInt(0x7FFF_FFFF)); + var sign = _module.AddInstruction( + SpirvOp.BitwiseAnd, + _uintType, + bits, + UInt(0x8000_0000)); + var nonZero = IsNotZero(absolute); + var subnormal = _module.AddInstruction( + SpirvOp.ULessThan, + _boolType, + absolute, + UInt(0x0080_0000)); + var flush = _module.AddInstruction( + SpirvOp.LogicalAnd, + _boolType, + nonZero, + subnormal); + var selected = _module.AddInstruction( + SpirvOp.Select, + _uintType, + flush, + sign, + bits); + return Bitcast(_floatType, selected); + } + + private uint EmitTrigCycleF32(uint source, bool preserveSignedZero) + { + var fraction = Ext(10, _floatType, source); + var bits = Bitcast(_uintType, source); + var absolute = _module.AddInstruction( + SpirvOp.BitwiseAnd, + _uintType, + bits, + UInt(0x7FFF_FFFF)); + var large = _module.AddInstruction( + SpirvOp.UGreaterThanEqual, + _boolType, + absolute, + UInt(0x4B00_0000)); + var finite = _module.AddInstruction( + SpirvOp.ULessThan, + _boolType, + absolute, + UInt(0x7F80_0000)); + var largeFinite = _module.AddInstruction( + SpirvOp.LogicalAnd, + _boolType, + large, + finite); + var reduced = _module.AddInstruction( + SpirvOp.Select, + _floatType, + largeFinite, + Float(0), + fraction); + if (!preserveSignedZero) + { + return reduced; + } + + var zero = _module.AddInstruction( + SpirvOp.IEqual, + _boolType, + absolute, + UInt(0)); + return _module.AddInstruction( + SpirvOp.Select, + _floatType, + zero, + source, + reduced); + } + } +}