From 6adf15e479b684cad7a783e7a1a056be087fdc02 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 21 Jan 2023 12:18:05 -0300
Subject: [PATCH] Implement CSET and CSETP shader instructions (#4318)

* Implement CSET and CSETP shader instructions

* Shader cache version bump

* Fix CC.HI
---
 .../Shader/DiskCache/DiskCacheHostStorage.cs  |  2 +-
 .../Decoders/InstDecoders.cs                  |  1 +
 .../Instructions/InstEmit.cs                  |  7 --
 .../Instructions/InstEmitConditionCode.cs     | 87 +++++++++++++++++++
 .../Instructions/InstEmitFlowControl.cs       | 18 +---
 .../Instructions/InstEmitIntegerComparison.cs | 17 ----
 6 files changed, 90 insertions(+), 42 deletions(-)
 create mode 100644 Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs

diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 2622ea3e8f..9f436502f1 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         private const ushort FileFormatVersionMajor = 1;
         private const ushort FileFormatVersionMinor = 2;
         private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
-        private const uint CodeGenVersion = 3939;
+        private const uint CodeGenVersion = 4318;
 
         private const string SharedTocFileName = "shared.toc";
         private const string SharedDataFileName = "shared.data";
diff --git a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
index 98a4364070..0c22ddc051 100644
--- a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
@@ -91,6 +91,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
         Neu = 13,
         Geu = 14,
         T = 15,
+        Off = 16,
         Lo = 17,
         Sff = 18,
         Ls = 19,
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
index c242963a6d..3a9e658aa8 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
@@ -54,13 +54,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
             context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented.");
         }
 
-        public static void Cset(EmitterContext context)
-        {
-            InstCset op = context.GetOp<InstCset>();
-
-            context.Config.GpuAccessor.Log("Shader instruction Cset is not implemented.");
-        }
-
         public static void Cs2r(EmitterContext context)
         {
             InstCs2r op = context.GetOp<InstCs2r>();
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs
new file mode 100644
index 0000000000..74ac760298
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConditionCode.cs
@@ -0,0 +1,87 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+    static partial class InstEmit
+    {
+        public static void Cset(EmitterContext context)
+        {
+            InstCset op = context.GetOp<InstCset>();
+
+            Operand res = GetCondition(context, op.Ccc);
+            Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+            res = GetPredLogicalOp(context, op.Bop, res, srcPred);
+
+            Operand dest = GetDest(op.Dest);
+
+            if (op.BVal)
+            {
+                context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
+            }
+            else
+            {
+                context.Copy(dest, res);
+            }
+
+            // TODO: CC.
+        }
+
+        public static void Csetp(EmitterContext context)
+        {
+            InstCsetp op = context.GetOp<InstCsetp>();
+
+            Operand p0Res = GetCondition(context, op.Ccc);
+            Operand p1Res = context.BitwiseNot(p0Res);
+            Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+            p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred);
+            p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred);
+
+            context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
+            context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
+
+            // TODO: CC.
+        }
+
+        private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True)
+        {
+            return cond switch
+            {
+                Ccc.F => Const(IrConsts.False),
+                Ccc.Lt => context.BitwiseExclusiveOr(context.BitwiseAnd(GetNF(), context.BitwiseNot(GetZF())), GetVF()),
+                Ccc.Eq => context.BitwiseAnd(context.BitwiseNot(GetNF()), GetZF()),
+                Ccc.Le => context.BitwiseExclusiveOr(GetNF(), context.BitwiseOr(GetZF(), GetVF())),
+                Ccc.Gt => context.BitwiseNot(context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF())),
+                Ccc.Ne => context.BitwiseNot(GetZF()),
+                Ccc.Ge => context.BitwiseNot(context.BitwiseExclusiveOr(GetNF(), GetVF())),
+                Ccc.Num => context.BitwiseNot(context.BitwiseAnd(GetNF(), GetZF())),
+                Ccc.Nan => context.BitwiseAnd(GetNF(), GetZF()),
+                Ccc.Ltu => context.BitwiseExclusiveOr(GetNF(), GetVF()),
+                Ccc.Equ => GetZF(),
+                Ccc.Leu => context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF()),
+                Ccc.Gtu => context.BitwiseExclusiveOr(context.BitwiseNot(GetNF()), context.BitwiseOr(GetVF(), GetZF())),
+                Ccc.Neu => context.BitwiseOr(GetNF(), context.BitwiseNot(GetZF())),
+                Ccc.Geu => context.BitwiseExclusiveOr(context.BitwiseOr(context.BitwiseNot(GetNF()), GetZF()), GetVF()),
+                Ccc.T => Const(IrConsts.True),
+                Ccc.Off => context.BitwiseNot(GetVF()),
+                Ccc.Lo => context.BitwiseNot(GetCF()),
+                Ccc.Sff => context.BitwiseNot(GetNF()),
+                Ccc.Ls => context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF())),
+                Ccc.Hi => context.BitwiseAnd(GetCF(), context.BitwiseNot(GetZF())),
+                Ccc.Sft => GetNF(),
+                Ccc.Hs => GetCF(),
+                Ccc.Oft => GetVF(),
+                Ccc.Rle => context.BitwiseOr(GetNF(), GetZF()),
+                Ccc.Rgt => context.BitwiseNot(context.BitwiseOr(GetNF(), GetZF())),
+                _ => Const(defaultCond)
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs
index f1dd279c87..91c2323037 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
             }
             else
             {
-                // TODO: Support CC here aswell (condition).
+                // TODO: Support CC here as well (condition).
                 foreach (SyncTarget target in targets.Values)
                 {
                     PushOpInfo pushOpInfo = target.PushOpInfo;
@@ -318,21 +318,5 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 context.BranchIfTrue(label, pred);
             }
         }
-
-        private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True)
-        {
-            // TODO: More condition codes, figure out how they work.
-            switch (cond)
-            {
-                case Ccc.Eq:
-                case Ccc.Equ:
-                    return GetZF();
-                case Ccc.Ne:
-                case Ccc.Neu:
-                    return context.BitwiseNot(GetZF());
-            }
-
-            return Const(defaultCond);
-        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs
index ddd90f8e81..dcdb189fbc 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs
@@ -11,23 +11,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
 {
     static partial class InstEmit
     {
-        public static void Csetp(EmitterContext context)
-        {
-            InstCsetp op = context.GetOp<InstCsetp>();
-
-            // TODO: Implement that properly.
-
-            Operand p0Res = Const(IrConsts.True);
-            Operand p1Res = context.BitwiseNot(p0Res);
-            Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
-
-            p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred);
-            p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred);
-
-            context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
-            context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
-        }
-
         public static void IcmpR(EmitterContext context)
         {
             InstIcmpR op = context.GetOp<InstIcmpR>();