From 4b89348c0046e69c3419a587d2bc19e1c411e39c Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Mon, 4 Jun 2018 18:05:12 -0500
Subject: [PATCH] GPU: Implemented the F2I_R shader instruction.

---
 src/video_core/engines/shader_bytecode.h      | 24 ++++++++--
 .../renderer_opengl/gl_shader_decompiler.cpp  | 47 +++++++++++++++++--
 2 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 22c122fcc5..93654eb662 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -173,6 +173,13 @@ enum class SubOp : u64 {
     Min = 0x8,
 };
 
+enum class FloatRoundingOp : u64 {
+    None = 0,
+    Floor = 1,
+    Ceil = 2,
+    Trunc = 3,
+};
+
 union Instruction {
     Instruction& operator=(const Instruction& instr) {
         value = instr.value;
@@ -277,11 +284,20 @@ union Instruction {
 
     union {
         BitField<10, 2, Register::Size> size;
-        BitField<13, 1, u64> is_signed;
+        BitField<12, 1, u64> is_output_signed;
+        BitField<13, 1, u64> is_input_signed;
         BitField<41, 2, u64> selector;
         BitField<45, 1, u64> negate_a;
         BitField<49, 1, u64> abs_a;
         BitField<50, 1, u64> saturate_a;
+
+        union {
+            BitField<39, 2, FloatRoundingOp> rounding;
+        } f2i;
+
+        union {
+            BitField<39, 4, u64> rounding;
+        } f2f;
     } conversion;
 
     union {
@@ -535,9 +551,9 @@ private:
             INST("0100110010101---", Id::F2F_C, Type::Conversion, "F2F_C"),
             INST("0101110010101---", Id::F2F_R, Type::Conversion, "F2F_R"),
             INST("0011100-10101---", Id::F2F_IMM, Type::Conversion, "F2F_IMM"),
-            INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"),
-            INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"),
-            INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"),
+            INST("0100110010110---", Id::F2I_C, Type::Conversion, "F2I_C"),
+            INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"),
+            INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_IMM"),
             INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
             INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
             INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 9943394c69..4b14bb47ec 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -929,18 +929,20 @@ private:
                 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
 
                 std::string op_a =
-                    regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
+                    regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed);
 
                 if (instr.conversion.abs_a) {
                     op_a = "abs(" + op_a + ')';
                 }
 
-                regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1);
+                regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
+                                          1);
                 break;
             }
             case OpCode::Id::I2F_R: {
+                ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
                 std::string op_a =
-                    regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
+                    regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed);
 
                 if (instr.conversion.abs_a) {
                     op_a = "abs(" + op_a + ')';
@@ -950,6 +952,8 @@ private:
                 break;
             }
             case OpCode::Id::F2F_R: {
+                // TODO(Subv): Implement rounding operations.
+                ASSERT_MSG(instr.conversion.f2f.rounding == 0, "Unimplemented rounding operation");
                 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
 
                 if (instr.conversion.abs_a) {
@@ -959,6 +963,43 @@ private:
                 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
                 break;
             }
+            case OpCode::Id::F2I_R: {
+                std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
+
+                if (instr.conversion.abs_a) {
+                    op_a = "abs(" + op_a + ')';
+                }
+
+                using Tegra::Shader::FloatRoundingOp;
+                switch (instr.conversion.f2i.rounding) {
+                case FloatRoundingOp::None:
+                    break;
+                case FloatRoundingOp::Floor:
+                    op_a = "floor(" + op_a + ')';
+                    break;
+                case FloatRoundingOp::Ceil:
+                    op_a = "ceil(" + op_a + ')';
+                    break;
+                case FloatRoundingOp::Trunc:
+                    op_a = "trunc(" + op_a + ')';
+                    break;
+                default:
+                    NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}",
+                                   static_cast<u32>(instr.conversion.f2i.rounding.Value()));
+                    UNREACHABLE();
+                    break;
+                }
+
+                if (instr.conversion.is_output_signed) {
+                    op_a = "int(" + op_a + ')';
+                } else {
+                    op_a = "uint(" + op_a + ')';
+                }
+
+                regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
+                                          1);
+                break;
+            }
             default: {
                 NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName());
                 UNREACHABLE();