diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 40f6d18c50..366c93df2b 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
///
/// Version of the codegen (to be changed when codegen or guest format change).
///
- private const ulong ShaderCodeGenVersion = 1878;
+ private const ulong ShaderCodeGenVersion = 1901;
///
/// Creates a new instance of the shader cache.
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
index 5d00adb564..64ac0eb780 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
@@ -119,17 +119,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand res = context.IAdd(srcA, srcB);
- bool isSubtraction = negateA || negateB;
-
if (op.Extended)
{
- // Add carry, or subtract borrow.
- res = context.IAdd(res, isSubtraction
- ? context.BitwiseNot(GetCF())
- : context.BitwiseAnd(GetCF(), Const(1)));
+ res = context.IAdd(res, context.BitwiseAnd(GetCF(), Const(1)));
}
- SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, op.Extended, isSubtraction);
+ SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, op.Extended);
context.Copy(GetDest(context), res);
}
@@ -317,9 +312,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand res = context.IAdd(srcA, srcB);
- context.Copy(GetDest(context), res);
+ SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, false);
- // TODO: CC, X
+ context.Copy(GetDest(context), res);
}
public static void Iset(EmitterContext context)
@@ -334,7 +329,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand srcA = GetSrcA(context);
Operand srcB = GetSrcB(context);
- Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned);
+ Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, op.Extended);
Operand pred = GetPredicate39(context);
@@ -356,8 +351,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
SetZnFlags(context, res, op.SetCondCode, op.Extended);
}
-
- // TODO: X
}
public static void Isetp(EmitterContext context)
@@ -371,7 +364,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand srcA = GetSrcA(context);
Operand srcB = GetSrcB(context);
- Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned);
+ Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, op.Extended);
Operand p1Res = context.BitwiseNot(p0Res);
@@ -799,6 +792,84 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(GetDest(context), res);
}
+ private static Operand GetIntComparison(
+ EmitterContext context,
+ IntegerCondition cond,
+ Operand srcA,
+ Operand srcB,
+ bool isSigned,
+ bool extended)
+ {
+ return extended
+ ? GetIntComparisonExtended(context, cond, srcA, srcB, isSigned)
+ : GetIntComparison (context, cond, srcA, srcB, isSigned);
+ }
+
+ private static Operand GetIntComparisonExtended(
+ EmitterContext context,
+ IntegerCondition cond,
+ Operand srcA,
+ Operand srcB,
+ bool isSigned)
+ {
+ Operand res;
+
+ if (cond == IntegerCondition.Always)
+ {
+ res = Const(IrConsts.True);
+ }
+ else if (cond == IntegerCondition.Never)
+ {
+ res = Const(IrConsts.False);
+ }
+ else
+ {
+ res = context.ISubtract(srcA, srcB);
+ res = context.IAdd(res, context.BitwiseNot(GetCF()));
+
+ switch (cond)
+ {
+ case Decoders.IntegerCondition.Equal: // r = xh == yh && xl == yl
+ res = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetZF());
+ break;
+ case Decoders.IntegerCondition.Less: // r = xh < yh || (xh == yh && xl < yl)
+ Operand notC = context.BitwiseNot(GetCF());
+ Operand prevLt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notC);
+ res = isSigned
+ ? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLt)
+ : context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLt);
+ break;
+ case Decoders.IntegerCondition.LessOrEqual: // r = xh < yh || (xh == yh && xl <= yl)
+ Operand zOrNotC = context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF()));
+ Operand prevLe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), zOrNotC);
+ res = isSigned
+ ? context.BitwiseOr(context.ICompareLess(srcA, srcB), prevLe)
+ : context.BitwiseOr(context.ICompareLessUnsigned(srcA, srcB), prevLe);
+ break;
+ case Decoders.IntegerCondition.Greater: // r = xh > yh || (xh == yh && xl > yl)
+ Operand notZAndC = context.BitwiseAnd(context.BitwiseNot(GetZF()), GetCF());
+ Operand prevGt = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), notZAndC);
+ res = isSigned
+ ? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGt)
+ : context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGt);
+ break;
+ case Decoders.IntegerCondition.GreaterOrEqual: // r = xh > yh || (xh == yh && xl >= yl)
+ Operand prevGe = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetCF());
+ res = isSigned
+ ? context.BitwiseOr(context.ICompareGreater(srcA, srcB), prevGe)
+ : context.BitwiseOr(context.ICompareGreaterUnsigned(srcA, srcB), prevGe);
+ break;
+ case Decoders.IntegerCondition.NotEqual: // r = xh != yh || xl != yl
+ context.BitwiseOr(context.ICompareNotEqual(srcA, srcB), context.BitwiseNot(GetZF()));
+ break;
+ default:
+ throw new InvalidOperationException($"Unexpected condition \"{cond}\".");
+ }
+ }
+
+ return res;
+ }
+
private static Operand GetIntComparison(
EmitterContext context,
IntegerCondition cond,
@@ -879,20 +950,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand srcA,
Operand srcB,
bool setCC,
- bool extended,
- bool isSubtraction = false)
+ bool extended)
{
if (!setCC)
{
return;
}
- if (!extended || isSubtraction)
- {
- // C = d < a
- context.Copy(GetCF(), context.ICompareLessUnsigned(res, srcA));
- }
- else
+ if (extended)
{
// C = (d == a && CIn) || d < a
Operand tempC0 = context.ICompareEqual (res, srcA);
@@ -902,6 +967,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(GetCF(), context.BitwiseOr(tempC0, tempC1));
}
+ else
+ {
+ // C = d < a
+ context.Copy(GetCF(), context.ICompareLessUnsigned(res, srcA));
+ }
// V = (d ^ a) & ~(a ^ b) < 0
Operand tempV0 = context.BitwiseExclusiveOr(res, srcA);
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index 63f9cff787..81d5c7af2e 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -501,7 +501,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
for (int index = 0; index < count; index++)
{
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
+ bool isRz = op.Rd.IsRZ;
+
+ Register rd = new Register(isRz ? op.Rd.Index : op.Rd.Index + index, RegisterType.Gpr);
Operand value = Register(rd);
@@ -525,11 +527,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
case MemoryRegion.Local: context.StoreLocal (offset, value); break;
case MemoryRegion.Shared: context.StoreShared(offset, value); break;
}
-
- if (rd.IsRZ)
- {
- break;
- }
}
}
@@ -547,7 +544,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
for (int index = 0; index < count; index++)
{
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
+ bool isRz = op.Rd.IsRZ;
+
+ Register rd = new Register(isRz ? op.Rd.Index : op.Rd.Index + index, RegisterType.Gpr);
Operand value = Register(rd);
@@ -559,11 +558,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
context.StoreGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh, value);
-
- if (rd.IsRZ)
- {
- break;
- }
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
index 40f3370fa4..b2418c2e14 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
@@ -406,11 +406,41 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.CompareEqual, Local(), a, b);
}
+ public static Operand ICompareGreater(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareGreater, Local(), a, b);
+ }
+
+ public static Operand ICompareGreaterOrEqual(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareGreaterOrEqual, Local(), a, b);
+ }
+
+ public static Operand ICompareGreaterOrEqualUnsigned(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareGreaterOrEqualU32, Local(), a, b);
+ }
+
+ public static Operand ICompareGreaterUnsigned(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareGreaterU32, Local(), a, b);
+ }
+
public static Operand ICompareLess(this EmitterContext context, Operand a, Operand b)
{
return context.Add(Instruction.CompareLess, Local(), a, b);
}
+ public static Operand ICompareLessOrEqual(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareLessOrEqual, Local(), a, b);
+ }
+
+ public static Operand ICompareLessOrEqualUnsigned(this EmitterContext context, Operand a, Operand b)
+ {
+ return context.Add(Instruction.CompareLessOrEqualU32, Local(), a, b);
+ }
+
public static Operand ICompareLessUnsigned(this EmitterContext context, Operand a, Operand b)
{
return context.Add(Instruction.CompareLessU32, Local(), a, b);