From e2bc05b17d91854cbb9c0ce3647141bf7d33143e Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 20 Feb 2021 03:30:13 -0300
Subject: shader: Add denorm flush support

---
 src/shader_recompiler/frontend/ir/ir_emitter.cpp   | 32 +++++++++++-----------
 src/shader_recompiler/frontend/ir/ir_emitter.h     |  8 +++---
 src/shader_recompiler/frontend/ir/modifiers.h      | 23 ++++++++++------
 .../impl/floating_point_conversion_integer.cpp     | 19 ++++++++++---
 4 files changed, 49 insertions(+), 33 deletions(-)

(limited to 'src/shader_recompiler/frontend')

diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 559ab9cca6..8f120a2f67 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -558,53 +558,53 @@ F16F32F64 IREmitter::FPSaturate(const F16F32F64& value) {
     }
 }
 
-F16F32F64 IREmitter::FPRoundEven(const F16F32F64& value) {
+F16F32F64 IREmitter::FPRoundEven(const F16F32F64& value, FpControl control) {
     switch (value.Type()) {
     case Type::F16:
-        return Inst<F16>(Opcode::FPRoundEven16, value);
+        return Inst<F16>(Opcode::FPRoundEven16, Flags{control}, value);
     case Type::F32:
-        return Inst<F32>(Opcode::FPRoundEven32, value);
+        return Inst<F32>(Opcode::FPRoundEven32, Flags{control}, value);
     case Type::F64:
-        return Inst<F64>(Opcode::FPRoundEven64, value);
+        return Inst<F64>(Opcode::FPRoundEven64, Flags{control}, value);
     default:
         ThrowInvalidType(value.Type());
     }
 }
 
-F16F32F64 IREmitter::FPFloor(const F16F32F64& value) {
+F16F32F64 IREmitter::FPFloor(const F16F32F64& value, FpControl control) {
     switch (value.Type()) {
     case Type::F16:
-        return Inst<F16>(Opcode::FPFloor16, value);
+        return Inst<F16>(Opcode::FPFloor16, Flags{control}, value);
     case Type::F32:
-        return Inst<F32>(Opcode::FPFloor32, value);
+        return Inst<F32>(Opcode::FPFloor32, Flags{control}, value);
     case Type::F64:
-        return Inst<F64>(Opcode::FPFloor64, value);
+        return Inst<F64>(Opcode::FPFloor64, Flags{control}, value);
     default:
         ThrowInvalidType(value.Type());
     }
 }
 
-F16F32F64 IREmitter::FPCeil(const F16F32F64& value) {
+F16F32F64 IREmitter::FPCeil(const F16F32F64& value, FpControl control) {
     switch (value.Type()) {
     case Type::F16:
-        return Inst<F16>(Opcode::FPCeil16, value);
+        return Inst<F16>(Opcode::FPCeil16, Flags{control}, value);
     case Type::F32:
-        return Inst<F32>(Opcode::FPCeil32, value);
+        return Inst<F32>(Opcode::FPCeil32, Flags{control}, value);
     case Type::F64:
-        return Inst<F64>(Opcode::FPCeil64, value);
+        return Inst<F64>(Opcode::FPCeil64, Flags{control}, value);
     default:
         ThrowInvalidType(value.Type());
     }
 }
 
-F16F32F64 IREmitter::FPTrunc(const F16F32F64& value) {
+F16F32F64 IREmitter::FPTrunc(const F16F32F64& value, FpControl control) {
     switch (value.Type()) {
     case Type::F16:
-        return Inst<F16>(Opcode::FPTrunc16, value);
+        return Inst<F16>(Opcode::FPTrunc16, Flags{control}, value);
     case Type::F32:
-        return Inst<F32>(Opcode::FPTrunc32, value);
+        return Inst<F32>(Opcode::FPTrunc32, Flags{control}, value);
     case Type::F64:
-        return Inst<F64>(Opcode::FPTrunc64, value);
+        return Inst<F64>(Opcode::FPTrunc64, Flags{control}, value);
     default:
         ThrowInvalidType(value.Type());
     }
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 24b012a393..959f4f9dac 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -129,10 +129,10 @@ public:
     [[nodiscard]] F32 FPSinNotReduced(const F32& value);
     [[nodiscard]] F32 FPSqrt(const F32& value);
     [[nodiscard]] F16F32F64 FPSaturate(const F16F32F64& value);
-    [[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value);
-    [[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value);
-    [[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value);
-    [[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value);
+    [[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value, FpControl control = {});
+    [[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value, FpControl control = {});
+    [[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value, FpControl control = {});
+    [[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value, FpControl control = {});
 
     [[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
     [[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h
index c288eede03..44652eae7c 100644
--- a/src/shader_recompiler/frontend/ir/modifiers.h
+++ b/src/shader_recompiler/frontend/ir/modifiers.h
@@ -4,25 +4,30 @@
 
 #pragma once
 
+#include "common/common_types.h"
+
 namespace Shader::IR {
 
 enum class FmzMode : u8 {
-    None, // Denorms are not flushed, NAN is propagated (nouveau)
-    FTZ,  // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK)
-    FMZ,  // Flush denorms to zero, x * 0 == 0 (D3D9)
+    DontCare, // Not specified for this instruction
+    FTZ,      // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK)
+    FMZ,      // Flush denorms to zero, x * 0 == 0 (D3D9)
+    None,     // Denorms are not flushed, NAN is propagated (nouveau)
 };
 
 enum class FpRounding : u8 {
-    RN, // Round to nearest even,
-    RM, // Round towards negative infinity
-    RP, // Round towards positive infinity
-    RZ, // Round towards zero
+    DontCare, // Not specified for this instruction
+    RN,       // Round to nearest even,
+    RM,       // Round towards negative infinity
+    RP,       // Round towards positive infinity
+    RZ,       // Round towards zero
 };
 
 struct FpControl {
     bool no_contraction{false};
-    FpRounding rounding{FpRounding::RN};
-    FmzMode fmz_mode{FmzMode::FTZ};
+    FpRounding rounding{FpRounding::DontCare};
+    FmzMode fmz_mode{FmzMode::DontCare};
 };
 static_assert(sizeof(FpControl) <= sizeof(u32));
+
 } // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
index ae2d37405f..4d82a0009d 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
@@ -81,17 +81,28 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) {
     // F2I is used to convert from a floating point value to an integer
     const F2I f2i{insn};
 
+    const bool denorm_cares{f2i.src_format != SrcFormat::F16 && f2i.src_format != SrcFormat::F64 &&
+                            f2i.dest_format != DestFormat::I64};
+    IR::FmzMode fmz_mode{IR::FmzMode::DontCare};
+    if (denorm_cares) {
+        fmz_mode = f2i.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None;
+    }
+    const IR::FpControl fp_control{
+        .no_contraction{true},
+        .rounding{IR::FpRounding::DontCare},
+        .fmz_mode{fmz_mode},
+    };
     const IR::F16F32F64 op_a{v.ir.FPAbsNeg(src_a, f2i.abs != 0, f2i.neg != 0)};
     const IR::F16F32F64 rounded_value{[&] {
         switch (f2i.rounding) {
         case Rounding::Round:
-            return v.ir.FPRoundEven(op_a);
+            return v.ir.FPRoundEven(op_a, fp_control);
         case Rounding::Floor:
-            return v.ir.FPFloor(op_a);
+            return v.ir.FPFloor(op_a, fp_control);
         case Rounding::Ceil:
-            return v.ir.FPCeil(op_a);
+            return v.ir.FPCeil(op_a, fp_control);
         case Rounding::Trunc:
-            return v.ir.FPTrunc(op_a);
+            return v.ir.FPTrunc(op_a, fp_control);
         default:
             throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
         }
-- 
cgit v1.2.3-70-g09d2