aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Instructions
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2021-10-12 17:35:31 -0300
committerGitHub <noreply@github.com>2021-10-12 22:35:31 +0200
commita7109c767bdc014327b574012794156c92174495 (patch)
treed7d7db6eaa63d4e3e0396a3182d0267b6ad31dd7 /Ryujinx.Graphics.Shader/Instructions
parent0510fde25ae66ec0b55091746a52931248d75b89 (diff)
Rewrite shader decoding stage (#2698)
* Rewrite shader decoding stage * Fix P2R constant buffer encoding * Fix PSET/PSETP * PR feedback * Log unimplemented shader instructions * Implement NOP * Remove using * PR feedback
Diffstat (limited to 'Ryujinx.Graphics.Shader/Instructions')
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmit.cs526
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs984
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs57
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs190
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs44
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs190
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs409
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs547
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs545
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs419
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs70
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs (renamed from Ryujinx.Graphics.Shader/Instructions/InstEmitFlow.cs)107
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs253
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs656
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs327
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs167
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs71
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs544
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs223
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs85
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs15
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs54
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs132
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs776
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs1368
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs18
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs (renamed from Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs)85
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs48
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs84
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs9
30 files changed, 5628 insertions, 3375 deletions
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
new file mode 100644
index 00000000..8214c9f4
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
@@ -0,0 +1,526 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.Translation;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void AtomCas(EmitterContext context)
+ {
+ InstAtomCas op = context.GetOp<InstAtomCas>();
+
+ context.Config.GpuAccessor.Log("Shader instruction AtomCas is not implemented.");
+ }
+
+ public static void AtomsCas(EmitterContext context)
+ {
+ InstAtomsCas op = context.GetOp<InstAtomsCas>();
+
+ context.Config.GpuAccessor.Log("Shader instruction AtomsCas is not implemented.");
+ }
+
+ public static void B2r(EmitterContext context)
+ {
+ InstB2r op = context.GetOp<InstB2r>();
+
+ context.Config.GpuAccessor.Log("Shader instruction B2r is not implemented.");
+ }
+
+ public static void Bpt(EmitterContext context)
+ {
+ InstBpt op = context.GetOp<InstBpt>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Bpt is not implemented.");
+ }
+
+ public static void Cctl(EmitterContext context)
+ {
+ InstCctl op = context.GetOp<InstCctl>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Cctl is not implemented.");
+ }
+
+ public static void Cctll(EmitterContext context)
+ {
+ InstCctll op = context.GetOp<InstCctll>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Cctll is not implemented.");
+ }
+
+ public static void Cctlt(EmitterContext context)
+ {
+ InstCctlt op = context.GetOp<InstCctlt>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented.");
+ }
+
+ public static void Cont(EmitterContext context)
+ {
+ InstContUnsup op = context.GetOp<InstContUnsup>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ContUnsup is not implemented.");
+ }
+
+ public static void Cset(EmitterContext context)
+ {
+ InstCsetUnsup op = context.GetOp<InstCsetUnsup>();
+
+ context.Config.GpuAccessor.Log("Shader instruction CsetUnsup is not implemented.");
+ }
+
+ public static void Cs2r(EmitterContext context)
+ {
+ InstCs2r op = context.GetOp<InstCs2r>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Cs2r is not implemented.");
+ }
+
+ public static void DmnmxR(EmitterContext context)
+ {
+ InstDmnmxR op = context.GetOp<InstDmnmxR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DmnmxR is not implemented.");
+ }
+
+ public static void DmnmxI(EmitterContext context)
+ {
+ InstDmnmxI op = context.GetOp<InstDmnmxI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DmnmxI is not implemented.");
+ }
+
+ public static void DmnmxC(EmitterContext context)
+ {
+ InstDmnmxC op = context.GetOp<InstDmnmxC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DmnmxC is not implemented.");
+ }
+
+ public static void DsetR(EmitterContext context)
+ {
+ InstDsetR op = context.GetOp<InstDsetR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetR is not implemented.");
+ }
+
+ public static void DsetI(EmitterContext context)
+ {
+ InstDsetI op = context.GetOp<InstDsetI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetI is not implemented.");
+ }
+
+ public static void DsetC(EmitterContext context)
+ {
+ InstDsetC op = context.GetOp<InstDsetC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetC is not implemented.");
+ }
+
+ public static void DsetpR(EmitterContext context)
+ {
+ InstDsetpR op = context.GetOp<InstDsetpR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetpR is not implemented.");
+ }
+
+ public static void DsetpI(EmitterContext context)
+ {
+ InstDsetpI op = context.GetOp<InstDsetpI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetpI is not implemented.");
+ }
+
+ public static void DsetpC(EmitterContext context)
+ {
+ InstDsetpC op = context.GetOp<InstDsetpC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction DsetpC is not implemented.");
+ }
+
+ public static void FchkR(EmitterContext context)
+ {
+ InstFchkR op = context.GetOp<InstFchkR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction FchkR is not implemented.");
+ }
+
+ public static void FchkI(EmitterContext context)
+ {
+ InstFchkI op = context.GetOp<InstFchkI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction FchkI is not implemented.");
+ }
+
+ public static void FchkC(EmitterContext context)
+ {
+ InstFchkC op = context.GetOp<InstFchkC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction FchkC is not implemented.");
+ }
+
+ public static void Getcrsptr(EmitterContext context)
+ {
+ InstGetcrsptr op = context.GetOp<InstGetcrsptr>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Getcrsptr is not implemented.");
+ }
+
+ public static void Getlmembase(EmitterContext context)
+ {
+ InstGetlmembase op = context.GetOp<InstGetlmembase>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Getlmembase is not implemented.");
+ }
+
+ public static void Ide(EmitterContext context)
+ {
+ InstIde op = context.GetOp<InstIde>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Ide is not implemented.");
+ }
+
+ public static void IdpR(EmitterContext context)
+ {
+ InstIdpR op = context.GetOp<InstIdpR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction IdpR is not implemented.");
+ }
+
+ public static void IdpC(EmitterContext context)
+ {
+ InstIdpC op = context.GetOp<InstIdpC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction IdpC is not implemented.");
+ }
+
+ public static void ImadspR(EmitterContext context)
+ {
+ InstImadspR op = context.GetOp<InstImadspR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImadspR is not implemented.");
+ }
+
+ public static void ImadspI(EmitterContext context)
+ {
+ InstImadspI op = context.GetOp<InstImadspI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImadspI is not implemented.");
+ }
+
+ public static void ImadspC(EmitterContext context)
+ {
+ InstImadspC op = context.GetOp<InstImadspC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImadspC is not implemented.");
+ }
+
+ public static void ImadspRc(EmitterContext context)
+ {
+ InstImadspRc op = context.GetOp<InstImadspRc>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImadspRc is not implemented.");
+ }
+
+ public static void ImulR(EmitterContext context)
+ {
+ InstImulR op = context.GetOp<InstImulR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImulR is not implemented.");
+ }
+
+ public static void ImulI(EmitterContext context)
+ {
+ InstImulI op = context.GetOp<InstImulI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImulI is not implemented.");
+ }
+
+ public static void ImulC(EmitterContext context)
+ {
+ InstImulC op = context.GetOp<InstImulC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ImulC is not implemented.");
+ }
+
+ public static void Imul32i(EmitterContext context)
+ {
+ InstImul32i op = context.GetOp<InstImul32i>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Imul32i is not implemented.");
+ }
+
+ public static void Jcal(EmitterContext context)
+ {
+ InstJcal op = context.GetOp<InstJcal>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Jcal is not implemented.");
+ }
+
+ public static void Jmp(EmitterContext context)
+ {
+ InstJmp op = context.GetOp<InstJmp>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Jmp is not implemented.");
+ }
+
+ public static void Jmx(EmitterContext context)
+ {
+ InstJmx op = context.GetOp<InstJmx>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Jmx is not implemented.");
+ }
+
+ public static void Ld(EmitterContext context)
+ {
+ InstLd op = context.GetOp<InstLd>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Ld is not implemented.");
+ }
+
+ public static void Lepc(EmitterContext context)
+ {
+ InstLepc op = context.GetOp<InstLepc>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Lepc is not implemented.");
+ }
+
+ public static void Longjmp(EmitterContext context)
+ {
+ InstLongjmp op = context.GetOp<InstLongjmp>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented.");
+ }
+
+ public static void P2rR(EmitterContext context)
+ {
+ InstP2rR op = context.GetOp<InstP2rR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction P2rR is not implemented.");
+ }
+
+ public static void P2rI(EmitterContext context)
+ {
+ InstP2rI op = context.GetOp<InstP2rI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction P2rI is not implemented.");
+ }
+
+ public static void P2rC(EmitterContext context)
+ {
+ InstP2rC op = context.GetOp<InstP2rC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction P2rC is not implemented.");
+ }
+
+ public static void Pcnt(EmitterContext context)
+ {
+ InstPcnt op = context.GetOp<InstPcnt>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Pcnt is not implemented.");
+ }
+
+ public static void Pexit(EmitterContext context)
+ {
+ InstPexit op = context.GetOp<InstPexit>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Pexit is not implemented.");
+ }
+
+ public static void Pixld(EmitterContext context)
+ {
+ InstPixld op = context.GetOp<InstPixld>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Pixld is not implemented.");
+ }
+
+ public static void Plongjmp(EmitterContext context)
+ {
+ InstPlongjmp op = context.GetOp<InstPlongjmp>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Plongjmp is not implemented.");
+ }
+
+ public static void Pret(EmitterContext context)
+ {
+ InstPret op = context.GetOp<InstPret>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Pret is not implemented.");
+ }
+
+ public static void PrmtR(EmitterContext context)
+ {
+ InstPrmtR op = context.GetOp<InstPrmtR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction PrmtR is not implemented.");
+ }
+
+ public static void PrmtI(EmitterContext context)
+ {
+ InstPrmtI op = context.GetOp<InstPrmtI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction PrmtI is not implemented.");
+ }
+
+ public static void PrmtC(EmitterContext context)
+ {
+ InstPrmtC op = context.GetOp<InstPrmtC>();
+
+ context.Config.GpuAccessor.Log("Shader instruction PrmtC is not implemented.");
+ }
+
+ public static void PrmtRc(EmitterContext context)
+ {
+ InstPrmtRc op = context.GetOp<InstPrmtRc>();
+
+ context.Config.GpuAccessor.Log("Shader instruction PrmtRc is not implemented.");
+ }
+
+ public static void R2b(EmitterContext context)
+ {
+ InstR2b op = context.GetOp<InstR2b>();
+
+ context.Config.GpuAccessor.Log("Shader instruction R2b is not implemented.");
+ }
+
+ public static void Ram(EmitterContext context)
+ {
+ InstRam op = context.GetOp<InstRam>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Ram is not implemented.");
+ }
+
+ public static void Rtt(EmitterContext context)
+ {
+ InstRtt op = context.GetOp<InstRtt>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Rtt is not implemented.");
+ }
+
+ public static void Sam(EmitterContext context)
+ {
+ InstSam op = context.GetOp<InstSam>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Sam is not implemented.");
+ }
+
+ public static void Setcrsptr(EmitterContext context)
+ {
+ InstSetcrsptr op = context.GetOp<InstSetcrsptr>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Setcrsptr is not implemented.");
+ }
+
+ public static void Setlmembase(EmitterContext context)
+ {
+ InstSetlmembase op = context.GetOp<InstSetlmembase>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Setlmembase is not implemented.");
+ }
+
+ public static void ShfLR(EmitterContext context)
+ {
+ InstShfLR op = context.GetOp<InstShfLR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ShfLR is not implemented.");
+ }
+
+ public static void ShfRR(EmitterContext context)
+ {
+ InstShfRR op = context.GetOp<InstShfRR>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ShfRR is not implemented.");
+ }
+
+ public static void ShfLI(EmitterContext context)
+ {
+ InstShfLI op = context.GetOp<InstShfLI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ShfLI is not implemented.");
+ }
+
+ public static void ShfRI(EmitterContext context)
+ {
+ InstShfRI op = context.GetOp<InstShfRI>();
+
+ context.Config.GpuAccessor.Log("Shader instruction ShfRI is not implemented.");
+ }
+
+ public static void St(EmitterContext context)
+ {
+ InstSt op = context.GetOp<InstSt>();
+
+ context.Config.GpuAccessor.Log("Shader instruction St is not implemented.");
+ }
+
+ public static void Stp(EmitterContext context)
+ {
+ InstStp op = context.GetOp<InstStp>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Stp is not implemented.");
+ }
+
+ public static void Txa(EmitterContext context)
+ {
+ InstTxa op = context.GetOp<InstTxa>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Txa is not implemented.");
+ }
+
+ public static void Vabsdiff(EmitterContext context)
+ {
+ InstVabsdiff op = context.GetOp<InstVabsdiff>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vabsdiff is not implemented.");
+ }
+
+ public static void Vabsdiff4(EmitterContext context)
+ {
+ InstVabsdiff4 op = context.GetOp<InstVabsdiff4>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vabsdiff4 is not implemented.");
+ }
+
+ public static void Vadd(EmitterContext context)
+ {
+ InstVadd op = context.GetOp<InstVadd>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vadd is not implemented.");
+ }
+
+ public static void Votevtg(EmitterContext context)
+ {
+ InstVotevtg op = context.GetOp<InstVotevtg>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Votevtg is not implemented.");
+ }
+
+ public static void Vset(EmitterContext context)
+ {
+ InstVset op = context.GetOp<InstVset>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented.");
+ }
+
+ public static void Vsetp(EmitterContext context)
+ {
+ InstVsetp op = context.GetOp<InstVsetp>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vsetp is not implemented.");
+ }
+
+ public static void Vshl(EmitterContext context)
+ {
+ InstVshl op = context.GetOp<InstVshl>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vshl is not implemented.");
+ }
+
+ public static void Vshr(EmitterContext context)
+ {
+ InstVshr op = context.GetOp<InstVshr>();
+
+ context.Config.GpuAccessor.Log("Shader instruction Vshr is not implemented.");
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
deleted file mode 100644
index 734d3aa7..00000000
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs
+++ /dev/null
@@ -1,984 +0,0 @@
-using Ryujinx.Graphics.Shader.Decoders;
-using Ryujinx.Graphics.Shader.IntermediateRepresentation;
-using Ryujinx.Graphics.Shader.Translation;
-using System;
-
-using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
-using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
-using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
-
-namespace Ryujinx.Graphics.Shader.Instructions
-{
- static partial class InstEmit
- {
- public static void Bfe(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool isReverse = op.RawOpCode.Extract(40);
- bool isSigned = op.RawOpCode.Extract(48);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- if (isReverse)
- {
- srcA = context.BitfieldReverse(srcA);
- }
-
- Operand position = context.BitwiseAnd(srcB, Const(0xff));
-
- Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8));
-
- Operand res = isSigned
- ? context.BitfieldExtractS32(srcA, position, size)
- : context.BitfieldExtractU32(srcA, position, size);
-
- context.Copy(GetDest(context), res);
-
- // TODO: CC, X, corner cases
- }
-
- public static void Bfi(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- Operand position = context.BitwiseAnd(srcB, Const(0xff));
-
- Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8));
-
- Operand res = context.BitfieldInsert(srcC, srcA, position, size);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Csetp(EmitterContext context)
- {
- OpCodePset op = (OpCodePset)context.CurrOp;
-
- // TODO: Implement that properly
-
- Operand p0Res = Const(IrConsts.True);
-
- Operand p1Res = context.BitwiseNot(p0Res);
-
- Operand pred = GetPredicate39(context);
-
- p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
- p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
-
- context.Copy(Register(op.Predicate3), p0Res);
- context.Copy(Register(op.Predicate0), p1Res);
- }
-
- public static void Flo(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool invert = op.RawOpCode.Extract(40);
- bool countZeros = op.RawOpCode.Extract(41);
- bool isSigned = op.RawOpCode.Extract(48);
-
- Operand srcB = context.BitwiseNot(GetSrcB(context), invert);
-
- Operand res = isSigned
- ? context.FindFirstSetS32(srcB)
- : context.FindFirstSetU32(srcB);
-
- if (countZeros)
- {
- res = context.BitwiseExclusiveOr(res, Const(31));
- }
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Iadd(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool negateA = false, negateB = false;
-
- if (!(op is OpCodeAluImm32))
- {
- negateB = op.RawOpCode.Extract(48);
- negateA = op.RawOpCode.Extract(49);
- }
- else
- {
- // TODO: Other IADD32I variant without the negate.
- negateA = op.RawOpCode.Extract(56);
- }
-
- Operand srcA = context.INegate(GetSrcA(context), negateA);
- Operand srcB = context.INegate(GetSrcB(context), negateB);
-
- Operand res = context.IAdd(srcA, srcB);
-
- if (op.Extended)
- {
- res = context.IAdd(res, context.BitwiseAnd(GetCF(), Const(1)));
- }
-
- SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, op.Extended);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Iadd3(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- IntegerHalfPart partC = (IntegerHalfPart)op.RawOpCode.Extract(31, 2);
- IntegerHalfPart partB = (IntegerHalfPart)op.RawOpCode.Extract(33, 2);
- IntegerHalfPart partA = (IntegerHalfPart)op.RawOpCode.Extract(35, 2);
-
- IntegerShift mode = (IntegerShift)op.RawOpCode.Extract(37, 2);
-
- bool negateC = op.RawOpCode.Extract(49);
- bool negateB = op.RawOpCode.Extract(50);
- bool negateA = op.RawOpCode.Extract(51);
-
- Operand Extend(Operand src, IntegerHalfPart part)
- {
- if (!(op is OpCodeAluReg) || part == IntegerHalfPart.B32)
- {
- return src;
- }
-
- if (part == IntegerHalfPart.H0)
- {
- return context.BitwiseAnd(src, Const(0xffff));
- }
- else if (part == IntegerHalfPart.H1)
- {
- return context.ShiftRightU32(src, Const(16));
- }
- else
- {
- // TODO: Warning.
- }
-
- return src;
- }
-
- Operand srcA = context.INegate(Extend(GetSrcA(context), partA), negateA);
- Operand srcB = context.INegate(Extend(GetSrcB(context), partB), negateB);
- Operand srcC = context.INegate(Extend(GetSrcC(context), partC), negateC);
-
- Operand res = context.IAdd(srcA, srcB);
-
- if (op is OpCodeAluReg && mode != IntegerShift.NoShift)
- {
- if (mode == IntegerShift.ShiftLeft)
- {
- res = context.ShiftLeft(res, Const(16));
- }
- else if (mode == IntegerShift.ShiftRight)
- {
- res = context.ShiftRightU32(res, Const(16));
- }
- else
- {
- // TODO: Warning.
- }
- }
-
- res = context.IAdd(res, srcC);
-
- context.Copy(GetDest(context), res);
-
- // TODO: CC, X, corner cases
- }
-
- public static void Icmp(EmitterContext context)
- {
- OpCode op = context.CurrOp;
-
- bool isSigned = op.RawOpCode.Extract(48);
-
- IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- Operand cmpRes = GetIntComparison(context, cmpOp, srcC, Const(0), isSigned);
-
- Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Imad(EmitterContext context)
- {
- bool signedA = context.CurrOp.RawOpCode.Extract(48);
- bool signedB = context.CurrOp.RawOpCode.Extract(53);
- bool high = context.CurrOp.RawOpCode.Extract(54);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- Operand res;
-
- if (high)
- {
- if (signedA && signedB)
- {
- res = context.MultiplyHighS32(srcA, srcB);
- }
- else
- {
- res = context.MultiplyHighU32(srcA, srcB);
-
- if (signedA)
- {
- res = context.IAdd(res, context.IMultiply(srcB, context.ShiftRightS32(srcA, Const(31))));
- }
- else if (signedB)
- {
- res = context.IAdd(res, context.IMultiply(srcA, context.ShiftRightS32(srcB, Const(31))));
- }
- }
- }
- else
- {
- res = context.IMultiply(srcA, srcB);
- }
-
- res = context.IAdd(res, srcC);
-
- // TODO: CC, X, SAT, and more?
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Imnmx(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool isSignedInt = op.RawOpCode.Extract(48);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- Operand resMin = isSignedInt
- ? context.IMinimumS32(srcA, srcB)
- : context.IMinimumU32(srcA, srcB);
-
- Operand resMax = isSignedInt
- ? context.IMaximumS32(srcA, srcB)
- : context.IMaximumU32(srcA, srcB);
-
- Operand pred = GetPredicate39(context);
- Operand res = context.ConditionalSelect(pred, resMin, resMax);
-
- context.Copy(GetDest(context), res);
-
- SetZnFlags(context, res, op.SetCondCode);
-
- // TODO: X flags.
- }
-
- public static void Iscadd(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool negateA = false, negateB = false;
-
- if (!(op is OpCodeAluImm32))
- {
- negateB = op.RawOpCode.Extract(48);
- negateA = op.RawOpCode.Extract(49);
- }
-
- int shift = op is OpCodeAluImm32
- ? op.RawOpCode.Extract(53, 5)
- : op.RawOpCode.Extract(39, 5);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- srcA = context.ShiftLeft(srcA, Const(shift));
-
- srcA = context.INegate(srcA, negateA);
- srcB = context.INegate(srcB, negateB);
-
- Operand res = context.IAdd(srcA, srcB);
-
- SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, false);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Iset(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- bool boolFloat = op.RawOpCode.Extract(44);
- bool isSigned = op.RawOpCode.Extract(48);
-
- IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, op.Extended);
-
- Operand pred = GetPredicate39(context);
-
- res = GetPredLogicalOp(context, op.LogicalOp, res, pred);
-
- Operand dest = GetDest(context);
-
- if (boolFloat)
- {
- res = context.ConditionalSelect(res, ConstF(1), Const(0));
-
- context.Copy(dest, res);
-
- SetFPZnFlags(context, res, op.SetCondCode);
- }
- else
- {
- context.Copy(dest, res);
-
- SetZnFlags(context, res, op.SetCondCode, op.Extended);
- }
- }
-
- public static void Isetp(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- bool isSigned = op.RawOpCode.Extract(48);
-
- IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, op.Extended);
-
- Operand p1Res = context.BitwiseNot(p0Res);
-
- Operand pred = GetPredicate39(context);
-
- p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
- p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
-
- context.Copy(Register(op.Predicate3), p0Res);
- context.Copy(Register(op.Predicate0), p1Res);
- }
-
- public static void Lea(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool negateA = op.RawOpCode.Extract(45);
-
- int shift = op.RawOpCode.Extract(39, 5);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- srcA = context.ShiftLeft(srcA, Const(shift));
- srcA = context.INegate(srcA, negateA);
-
- Operand res = context.IAdd(srcA, srcB);
-
- context.Copy(GetDest(context), res);
-
- // TODO: CC, X
- }
-
- public static void Lea_Hi(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool isReg = op is OpCodeAluReg;
- bool negateA;
- int shift;
-
- if (isReg)
- {
- negateA = op.RawOpCode.Extract(37);
- shift = op.RawOpCode.Extract(28, 5);
- }
- else
- {
- negateA = op.RawOpCode.Extract(56);
- shift = op.RawOpCode.Extract(51, 5);
- }
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- Operand aLow = context.ShiftLeft(srcA, Const(shift));
- Operand aHigh = shift == 0 ? Const(0) : context.ShiftRightU32(srcA, Const(32 - shift));
- aHigh = context.BitwiseOr(aHigh, context.ShiftLeft(srcC, Const(shift)));
-
- if (negateA)
- {
- // Perform 64-bit negation by doing bitwise not of the value,
- // then adding 1 and carrying over from low to high.
- aLow = context.BitwiseNot(aLow);
- aHigh = context.BitwiseNot(aHigh);
-
- aLow = AddWithCarry(context, aLow, Const(1), out Operand aLowCOut);
- aHigh = context.IAdd(aHigh, aLowCOut);
- }
-
- Operand res = context.IAdd(aHigh, srcB);
-
- context.Copy(GetDest(context), res);
-
- // TODO: CC, X
- }
-
- public static void Lop(EmitterContext context)
- {
- IOpCodeLop op = (IOpCodeLop)context.CurrOp;
-
- Operand srcA = context.BitwiseNot(GetSrcA(context), op.InvertA);
- Operand srcB = context.BitwiseNot(GetSrcB(context), op.InvertB);
-
- Operand res = srcB;
-
- switch (op.LogicalOp)
- {
- case LogicalOperation.And: res = context.BitwiseAnd (srcA, srcB); break;
- case LogicalOperation.Or: res = context.BitwiseOr (srcA, srcB); break;
- case LogicalOperation.ExclusiveOr: res = context.BitwiseExclusiveOr(srcA, srcB); break;
- }
-
- EmitLopPredWrite(context, op, res, (ConditionalOperation)context.CurrOp.RawOpCode.Extract(44, 2));
-
- context.Copy(GetDest(context), res);
-
- SetZnFlags(context, res, op.SetCondCode, op.Extended);
- }
-
- public static void Lop3(EmitterContext context)
- {
- IOpCodeLop op = (IOpCodeLop)context.CurrOp;
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- bool regVariant = op is OpCodeLopReg;
-
- int truthTable = regVariant
- ? op.RawOpCode.Extract(28, 8)
- : op.RawOpCode.Extract(48, 8);
-
- Operand res = Lop3Expression.GetFromTruthTable(context, srcA, srcB, srcC, truthTable);
-
- if (regVariant)
- {
- EmitLopPredWrite(context, op, res, (ConditionalOperation)context.CurrOp.RawOpCode.Extract(36, 2));
- }
-
- context.Copy(GetDest(context), res);
-
- SetZnFlags(context, res, op.SetCondCode, op.Extended);
- }
-
- public static void Popc(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool invert = op.RawOpCode.Extract(40);
-
- Operand srcB = context.BitwiseNot(GetSrcB(context), invert);
-
- Operand res = context.BitCount(srcB);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Pset(EmitterContext context)
- {
- OpCodePset op = (OpCodePset)context.CurrOp;
-
- bool boolFloat = op.RawOpCode.Extract(44);
-
- Operand srcA = context.BitwiseNot(Register(op.Predicate12), op.InvertA);
- Operand srcB = context.BitwiseNot(Register(op.Predicate29), op.InvertB);
- Operand srcC = context.BitwiseNot(Register(op.Predicate39), op.InvertP);
-
- Operand res = GetPredLogicalOp(context, op.LogicalOpAB, srcA, srcB);
-
- res = GetPredLogicalOp(context, op.LogicalOp, res, srcC);
-
- Operand dest = GetDest(context);
-
- if (boolFloat)
- {
- context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
- }
- else
- {
- context.Copy(dest, res);
- }
- }
-
- public static void Psetp(EmitterContext context)
- {
- OpCodePset op = (OpCodePset)context.CurrOp;
-
- Operand srcA = context.BitwiseNot(Register(op.Predicate12), op.InvertA);
- Operand srcB = context.BitwiseNot(Register(op.Predicate29), op.InvertB);
-
- Operand p0Res = GetPredLogicalOp(context, op.LogicalOpAB, srcA, srcB);
-
- Operand p1Res = context.BitwiseNot(p0Res);
-
- Operand pred = GetPredicate39(context);
-
- p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
- p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
-
- context.Copy(Register(op.Predicate3), p0Res);
- context.Copy(Register(op.Predicate0), p1Res);
- }
-
- public static void Rro(EmitterContext context)
- {
- // This is the range reduction operator,
- // we translate it as a simple move, as it
- // should be always followed by a matching
- // MUFU instruction.
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool negateB = op.RawOpCode.Extract(45);
- bool absoluteB = op.RawOpCode.Extract(49);
-
- Operand srcB = GetSrcB(context);
-
- srcB = context.FPAbsNeg(srcB, absoluteB, negateB);
-
- context.Copy(GetDest(context), srcB);
- }
-
- public static void Shl(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool isMasked = op.RawOpCode.Extract(39);
-
- Operand srcB = GetSrcB(context);
-
- if (isMasked)
- {
- srcB = context.BitwiseAnd(srcB, Const(0x1f));
- }
-
- Operand res = context.ShiftLeft(GetSrcA(context), srcB);
-
- if (!isMasked)
- {
- // Clamped shift value.
- Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
-
- res = context.ConditionalSelect(isLessThan32, res, Const(0));
- }
-
- // TODO: X, CC
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Shr(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool isMasked = op.RawOpCode.Extract(39);
- bool isReverse = op.RawOpCode.Extract(40);
- bool isSigned = op.RawOpCode.Extract(48);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- if (isReverse)
- {
- srcA = context.BitfieldReverse(srcA);
- }
-
- if (isMasked)
- {
- srcB = context.BitwiseAnd(srcB, Const(0x1f));
- }
-
- Operand res = isSigned
- ? context.ShiftRightS32(srcA, srcB)
- : context.ShiftRightU32(srcA, srcB);
-
- if (!isMasked)
- {
- // Clamped shift value.
- Operand resShiftBy32;
-
- if (isSigned)
- {
- resShiftBy32 = context.ShiftRightS32(srcA, Const(31));
- }
- else
- {
- resShiftBy32 = Const(0);
- }
-
- Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
-
- res = context.ConditionalSelect(isLessThan32, res, resShiftBy32);
- }
-
- // TODO: X, CC
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Xmad(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- bool signedA = context.CurrOp.RawOpCode.Extract(48);
- bool signedB = context.CurrOp.RawOpCode.Extract(49);
- bool highA = context.CurrOp.RawOpCode.Extract(53);
-
- bool isReg = (op is OpCodeAluReg) && !(op is OpCodeAluRegCbuf);
- bool isImm = (op is OpCodeAluImm);
-
- XmadCMode mode = isReg || isImm
- ? (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 3)
- : (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 2);
-
- bool highB = false;
-
- if (isReg)
- {
- highB = context.CurrOp.RawOpCode.Extract(35);
- }
- else if (!isImm)
- {
- highB = context.CurrOp.RawOpCode.Extract(52);
- }
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- // XMAD immediates are 16-bits unsigned integers.
- if (srcB.Type == OperandType.Constant)
- {
- srcB = Const(srcB.Value & 0xffff);
- }
-
- Operand Extend16To32(Operand src, bool high, bool signed)
- {
- if (signed && high)
- {
- return context.ShiftRightS32(src, Const(16));
- }
- else if (signed)
- {
- return context.BitfieldExtractS32(src, Const(0), Const(16));
- }
- else if (high)
- {
- return context.ShiftRightU32(src, Const(16));
- }
- else
- {
- return context.BitwiseAnd(src, Const(0xffff));
- }
- }
-
- srcA = Extend16To32(srcA, highA, signedA);
- srcB = Extend16To32(srcB, highB, signedB);
-
- bool productShiftLeft = false;
- bool merge = false;
-
- if (op is OpCodeAluCbuf)
- {
- productShiftLeft = context.CurrOp.RawOpCode.Extract(55);
- merge = context.CurrOp.RawOpCode.Extract(56);
- }
- else if (!(op is OpCodeAluRegCbuf))
- {
- productShiftLeft = context.CurrOp.RawOpCode.Extract(36);
- merge = context.CurrOp.RawOpCode.Extract(37);
- }
-
- bool extended;
-
- if ((op is OpCodeAluReg) || (op is OpCodeAluImm))
- {
- extended = context.CurrOp.RawOpCode.Extract(38);
- }
- else
- {
- extended = context.CurrOp.RawOpCode.Extract(54);
- }
-
- Operand res = context.IMultiply(srcA, srcB);
-
- if (productShiftLeft)
- {
- res = context.ShiftLeft(res, Const(16));
- }
-
- switch (mode)
- {
- case XmadCMode.Cfull: break;
-
- case XmadCMode.Clo: srcC = Extend16To32(srcC, high: false, signed: false); break;
- case XmadCMode.Chi: srcC = Extend16To32(srcC, high: true, signed: false); break;
-
- case XmadCMode.Cbcc:
- {
- srcC = context.IAdd(srcC, context.ShiftLeft(GetSrcB(context), Const(16)));
-
- break;
- }
-
- case XmadCMode.Csfu:
- {
- Operand signAdjustA = context.ShiftLeft(context.ShiftRightU32(srcA, Const(31)), Const(16));
- Operand signAdjustB = context.ShiftLeft(context.ShiftRightU32(srcB, Const(31)), Const(16));
-
- srcC = context.ISubtract(srcC, context.IAdd(signAdjustA, signAdjustB));
-
- break;
- }
-
- default: /* TODO: Warning */ break;
- }
-
- Operand product = res;
-
- if (extended)
- {
- // Add with carry.
- res = context.IAdd(res, context.BitwiseAnd(GetCF(), Const(1)));
- }
- else
- {
- // Add (no carry in).
- res = context.IAdd(res, srcC);
- }
-
- SetIaddFlags(context, res, product, srcC, op.SetCondCode, extended);
-
- if (merge)
- {
- res = context.BitwiseAnd(res, Const(0xffff));
- res = context.BitwiseOr(res, context.ShiftLeft(GetSrcB(context), Const(16)));
- }
-
- 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,
- 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
- {
- var inst = cond switch
- {
- IntegerCondition.Less => Instruction.CompareLessU32,
- IntegerCondition.Equal => Instruction.CompareEqual,
- IntegerCondition.LessOrEqual => Instruction.CompareLessOrEqualU32,
- IntegerCondition.Greater => Instruction.CompareGreaterU32,
- IntegerCondition.NotEqual => Instruction.CompareNotEqual,
- IntegerCondition.GreaterOrEqual => Instruction.CompareGreaterOrEqualU32,
- _ => throw new InvalidOperationException($"Unexpected condition \"{cond}\".")
- };
-
- if (isSigned)
- {
- switch (cond)
- {
- case IntegerCondition.Less: inst = Instruction.CompareLess; break;
- case IntegerCondition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break;
- case IntegerCondition.Greater: inst = Instruction.CompareGreater; break;
- case IntegerCondition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break;
- }
- }
-
- res = context.Add(inst, Local(), srcA, srcB);
- }
-
- return res;
- }
-
- private static void EmitLopPredWrite(EmitterContext context, IOpCodeLop op, Operand result, ConditionalOperation condOp)
- {
- if (op is OpCodeLop opLop && !opLop.Predicate48.IsPT)
- {
- Operand pRes;
-
- if (condOp == ConditionalOperation.False)
- {
- pRes = Const(IrConsts.False);
- }
- else if (condOp == ConditionalOperation.True)
- {
- pRes = Const(IrConsts.True);
- }
- else if (condOp == ConditionalOperation.Zero)
- {
- pRes = context.ICompareEqual(result, Const(0));
- }
- else /* if (opLop.CondOp == ConditionalOperation.NotZero) */
- {
- pRes = context.ICompareNotEqual(result, Const(0));
- }
-
- context.Copy(Register(opLop.Predicate48), pRes);
- }
- }
-
- private static void SetIaddFlags(
- EmitterContext context,
- Operand res,
- Operand srcA,
- Operand srcB,
- bool setCC,
- bool extended)
- {
- if (!setCC)
- {
- return;
- }
-
- if (extended)
- {
- // C = (d == a && CIn) || d < a
- Operand tempC0 = context.ICompareEqual (res, srcA);
- Operand tempC1 = context.ICompareLessUnsigned(res, srcA);
-
- tempC0 = context.BitwiseAnd(tempC0, GetCF());
-
- 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);
- Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB);
-
- tempV1 = context.BitwiseNot(tempV1);
-
- Operand tempV = context.BitwiseAnd(tempV0, tempV1);
-
- context.Copy(GetVF(), context.ICompareLess(tempV, Const(0)));
-
- SetZnFlags(context, res, setCC: true, extended: extended);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs
index 7fe969a0..3dda5316 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs
@@ -10,50 +10,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
static class InstEmitAluHelper
{
- public static long GetIntMin(IntegerType type)
+ public static long GetIntMin(IDstFmt type)
{
- switch (type)
+ return type switch
{
- case IntegerType.U8: return byte.MinValue;
- case IntegerType.S8: return sbyte.MinValue;
- case IntegerType.U16: return ushort.MinValue;
- case IntegerType.S16: return short.MinValue;
- case IntegerType.U32: return uint.MinValue;
- case IntegerType.S32: return int.MinValue;
- }
-
- throw new ArgumentException($"The type \"{type}\" is not a supported int type.");
+ IDstFmt.U16 => ushort.MinValue,
+ IDstFmt.S16 => short.MinValue,
+ IDstFmt.U32 => uint.MinValue,
+ IDstFmt.S32 => int.MinValue,
+ _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.")
+ };
}
- public static long GetIntMax(IntegerType type)
+ public static long GetIntMax(IDstFmt type)
{
- switch (type)
+ return type switch
{
- case IntegerType.U8: return byte.MaxValue;
- case IntegerType.S8: return sbyte.MaxValue;
- case IntegerType.U16: return ushort.MaxValue;
- case IntegerType.S16: return short.MaxValue;
- case IntegerType.U32: return uint.MaxValue;
- case IntegerType.S32: return int.MaxValue;
- }
-
- throw new ArgumentException($"The type \"{type}\" is not a supported int type.");
+ IDstFmt.U16 => ushort.MaxValue,
+ IDstFmt.S16 => short.MaxValue,
+ IDstFmt.U32 => uint.MaxValue,
+ IDstFmt.S32 => int.MaxValue,
+ _ => throw new ArgumentException($"The type \"{type}\" is not a supported integer type.")
+ };
}
- public static Operand GetPredLogicalOp(
- EmitterContext context,
- LogicalOperation logicalOp,
- Operand input,
- Operand pred)
+ public static Operand GetPredLogicalOp(EmitterContext context, BoolOp logicOp, Operand input, Operand pred)
{
- switch (logicalOp)
+ return logicOp switch
{
- case LogicalOperation.And: return context.BitwiseAnd (input, pred);
- case LogicalOperation.Or: return context.BitwiseOr (input, pred);
- case LogicalOperation.ExclusiveOr: return context.BitwiseExclusiveOr(input, pred);
- }
-
- return input;
+ BoolOp.And => context.BitwiseAnd(input, pred),
+ BoolOp.Or => context.BitwiseOr(input, pred),
+ BoolOp.Xor => context.BitwiseExclusiveOr(input, pred),
+ _ => input
+ };
}
public static void SetZnFlags(EmitterContext context, Operand dest, bool setCC, bool extended = false)
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
new file mode 100644
index 00000000..e865caf2
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
@@ -0,0 +1,190 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+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 Al2p(EmitterContext context)
+ {
+ InstAl2p op = context.GetOp<InstAl2p>();
+
+ context.Copy(GetDest(op.Dest), context.IAdd(GetSrcReg(context, op.SrcA), Const(op.Imm11)));
+ }
+
+ public static void Ald(EmitterContext context)
+ {
+ InstAld op = context.GetOp<InstAld>();
+
+ Operand primVertex = context.Copy(GetSrcReg(context, op.SrcB));
+
+ for (int index = 0; index < (int)op.AlSize + 1; index++)
+ {
+ Register rd = new Register(op.Dest + index, RegisterType.Gpr);
+
+ if (rd.IsRZ)
+ {
+ break;
+ }
+
+ if (op.Phys)
+ {
+ Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
+
+ context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
+
+ context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
+ }
+ else if (op.SrcB == RegisterConsts.RegisterZeroIndex)
+ {
+ Operand src = Attribute(op.Imm11 + index * 4);
+
+ context.FlagAttributeRead(src.Value);
+
+ context.Copy(Register(rd), src);
+ }
+ else
+ {
+ Operand src = Const(op.Imm11 + index * 4);
+
+ context.FlagAttributeRead(src.Value);
+
+ context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
+ }
+ }
+ }
+
+ public static void Ast(EmitterContext context)
+ {
+ InstAst op = context.GetOp<InstAst>();
+
+ for (int index = 0; index < (int)op.AlSize + 1; index++)
+ {
+ if (op.SrcB + index > RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ Register rd = new Register(op.SrcB + index, RegisterType.Gpr);
+
+ if (op.Phys)
+ {
+ Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
+
+ context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
+
+ context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
+ }
+ else
+ {
+ Operand dest = Attribute(op.Imm11 + index * 4);
+
+ context.FlagAttributeWritten(dest.Value);
+
+ context.Copy(dest, Register(rd));
+ }
+ }
+ }
+
+ public static void Ipa(EmitterContext context)
+ {
+ InstIpa op = context.GetOp<InstIpa>();
+
+ context.FlagAttributeRead(op.Imm10);
+
+ Operand res;
+
+ if (op.Idx)
+ {
+ Operand userAttrOffset = context.ISubtract(GetSrcReg(context, op.SrcA), Const(AttributeConsts.UserAttributeBase));
+ Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
+
+ res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0));
+ res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
+
+ context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
+ }
+ else
+ {
+ res = Attribute(op.Imm10);
+
+ if (op.Imm10 >= AttributeConsts.UserAttributeBase && op.Imm10 < AttributeConsts.UserAttributeEnd)
+ {
+ int index = (op.Imm10 - AttributeConsts.UserAttributeBase) >> 4;
+
+ if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
+ {
+ res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
+ }
+ }
+ }
+
+ if (op.IpaOp == IpaOp.Multiply)
+ {
+ Operand srcB = GetSrcReg(context, op.SrcB);
+
+ res = context.FPMultiply(res, srcB);
+ }
+
+ res = context.FPSaturate(res, op.Sat);
+
+ context.Copy(GetDest(op.Dest), res);
+ }
+
+ public static void Isberd(EmitterContext context)
+ {
+ InstIsberd op = context.GetOp<InstIsberd>();
+
+ // This instruction performs a load from ISBE memory,
+ // however it seems to be only used to get some vertex
+ // input data, so we instead propagate the offset so that
+ // it can be used on the attribute load.
+ context.Copy(GetDest(op.Dest), GetSrcReg(context, op.SrcA));
+ }
+
+ public static void OutR(EmitterContext context)
+ {
+ InstOutR op = context.GetOp<InstOutR>();
+
+ EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
+ }
+
+ public static void OutI(EmitterContext context)
+ {
+ InstOutI op = context.GetOp<InstOutI>();
+
+ EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
+ }
+
+ public static void OutC(EmitterContext context)
+ {
+ InstOutC op = context.GetOp<InstOutC>();
+
+ EmitOut(context, op.OutType.HasFlag(OutType.Emit), op.OutType.HasFlag(OutType.Cut));
+ }
+
+ private static void EmitOut(EmitterContext context, bool emit, bool cut)
+ {
+ if (!(emit || cut))
+ {
+ context.Config.GpuAccessor.Log("Invalid OUT encoding.");
+ }
+
+ if (emit)
+ {
+ context.EmitVertex();
+ }
+
+ if (cut)
+ {
+ context.EndPrimitive();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs
new file mode 100644
index 00000000..f3114c6e
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitBarrier.cs
@@ -0,0 +1,44 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.Translation;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void Bar(EmitterContext context)
+ {
+ InstBar op = context.GetOp<InstBar>();
+
+ // TODO: Support other modes.
+ if (op.BarOp == BarOp.Sync)
+ {
+ context.Barrier();
+ }
+ else
+ {
+ context.Config.GpuAccessor.Log($"Invalid barrier mode: {op.BarOp}.");
+ }
+ }
+
+ public static void Depbar(EmitterContext context)
+ {
+ InstDepbar op = context.GetOp<InstDepbar>();
+
+ // No operation.
+ }
+
+ public static void Membar(EmitterContext context)
+ {
+ InstMembar op = context.GetOp<InstMembar>();
+
+ if (op.Membar == Decoders.Membar.Cta)
+ {
+ context.GroupMemoryBarrier();
+ }
+ else
+ {
+ context.MemoryBarrier();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs
new file mode 100644
index 00000000..7a141bbc
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitBitfield.cs
@@ -0,0 +1,190 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+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 BfeR(EmitterContext context)
+ {
+ InstBfeR op = context.GetOp<InstBfeR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitBfe(context, srcA, srcB, op.Dest, op.Brev, op.Signed);
+ }
+
+ public static void BfeI(EmitterContext context)
+ {
+ InstBfeI op = context.GetOp<InstBfeI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitBfe(context, srcA, srcB, op.Dest, op.Brev, op.Signed);
+ }
+
+ public static void BfeC(EmitterContext context)
+ {
+ InstBfeC op = context.GetOp<InstBfeC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitBfe(context, srcA, srcB, op.Dest, op.Brev, op.Signed);
+ }
+
+ public static void BfiR(EmitterContext context)
+ {
+ InstBfiR op = context.GetOp<InstBfiR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitBfi(context, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void BfiI(EmitterContext context)
+ {
+ InstBfiI op = context.GetOp<InstBfiI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitBfi(context, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void BfiC(EmitterContext context)
+ {
+ InstBfiC op = context.GetOp<InstBfiC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitBfi(context, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void BfiRc(EmitterContext context)
+ {
+ InstBfiRc op = context.GetOp<InstBfiRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitBfi(context, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void FloR(EmitterContext context)
+ {
+ InstFloR op = context.GetOp<InstFloR>();
+
+ EmitFlo(context, GetSrcReg(context, op.SrcB), op.Dest, op.NegB, op.Sh, op.Signed);
+ }
+
+ public static void FloI(EmitterContext context)
+ {
+ InstFloI op = context.GetOp<InstFloI>();
+
+ EmitFlo(context, GetSrcImm(context, Imm20ToSInt(op.Imm20)), op.Dest, op.NegB, op.Sh, op.Signed);
+ }
+
+ public static void FloC(EmitterContext context)
+ {
+ InstFloC op = context.GetOp<InstFloC>();
+
+ EmitFlo(context, GetSrcCbuf(context, op.CbufSlot, op.CbufOffset), op.Dest, op.NegB, op.Sh, op.Signed);
+ }
+
+ public static void PopcR(EmitterContext context)
+ {
+ InstPopcR op = context.GetOp<InstPopcR>();
+
+ EmitPopc(context, GetSrcReg(context, op.SrcB), op.Dest, op.NegB);
+ }
+
+ public static void PopcI(EmitterContext context)
+ {
+ InstPopcI op = context.GetOp<InstPopcI>();
+
+ EmitPopc(context, GetSrcImm(context, Imm20ToSInt(op.Imm20)), op.Dest, op.NegB);
+ }
+
+ public static void PopcC(EmitterContext context)
+ {
+ InstPopcC op = context.GetOp<InstPopcC>();
+
+ EmitPopc(context, GetSrcCbuf(context, op.CbufSlot, op.CbufOffset), op.Dest, op.NegB);
+ }
+
+ private static void EmitBfe(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ bool bitReverse,
+ bool isSigned)
+ {
+ if (bitReverse)
+ {
+ srcA = context.BitfieldReverse(srcA);
+ }
+
+ Operand position = context.BitwiseAnd(srcB, Const(0xff));
+
+ Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8));
+
+ Operand res = isSigned
+ ? context.BitfieldExtractS32(srcA, position, size)
+ : context.BitfieldExtractU32(srcA, position, size);
+
+ context.Copy(GetDest(rd), res);
+
+ // TODO: CC, X, corner cases.
+ }
+
+ private static void EmitBfi(EmitterContext context, Operand srcA, Operand srcB, Operand srcC, int rd)
+ {
+ Operand position = context.BitwiseAnd(srcB, Const(0xff));
+
+ Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8));
+
+ Operand res = context.BitfieldInsert(srcC, srcA, position, size);
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitFlo(EmitterContext context, Operand src, int rd, bool invert, bool sh, bool isSigned)
+ {
+ Operand srcB = context.BitwiseNot(src, invert);
+
+ Operand res = isSigned
+ ? context.FindFirstSetS32(srcB)
+ : context.FindFirstSetU32(srcB);
+
+ if (sh)
+ {
+ res = context.BitwiseExclusiveOr(res, Const(31));
+ }
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitPopc(EmitterContext context, Operand src, int rd, bool invert)
+ {
+ Operand srcB = context.BitwiseNot(src, invert);
+
+ Operand res = context.BitCount(srcB);
+
+ context.Copy(GetDest(rd), res);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs
index c4c6c55b..43b07cc2 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs
@@ -1,253 +1,400 @@
using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
+using System;
-using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
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 F2F(EmitterContext context)
+ public static void F2fR(EmitterContext context)
{
- OpCodeFArith op = (OpCodeFArith)context.CurrOp;
+ InstF2fR op = context.GetOp<InstF2fR>();
- FPType dstType = (FPType)op.RawOpCode.Extract(8, 2);
- FPType srcType = (FPType)op.RawOpCode.Extract(10, 2);
+ var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
+
+ EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
- bool round = op.RawOpCode.Extract(42);
- bool negateB = op.RawOpCode.Extract(45);
- bool absoluteB = op.RawOpCode.Extract(49);
+ public static void F2fI(EmitterContext context)
+ {
+ InstF2fI op = context.GetOp<InstF2fI>();
- Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB, srcType.ToInstFPType());
+ var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
- if (round && srcType == dstType)
- {
- switch (op.RoundingMode)
- {
- case RoundingMode.ToNearest:
- srcB = context.FPRound(srcB, srcType.ToInstFPType());
- break;
+ EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
+
+ public static void F2fC(EmitterContext context)
+ {
+ InstF2fC op = context.GetOp<InstF2fC>();
+
+ var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
+
+ EmitF2F(context, op.SrcFmt, op.DstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
+
+ public static void F2iR(EmitterContext context)
+ {
+ InstF2iR op = context.GetOp<InstF2iR>();
+
+ var src = UnpackReg(context, op.SrcFmt, op.Sh, op.SrcB);
+
+ EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void F2iI(EmitterContext context)
+ {
+ InstF2iI op = context.GetOp<InstF2iI>();
+
+ var src = UnpackImm(context, op.SrcFmt, op.Sh, Imm20ToFloat(op.Imm20));
+
+ EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void F2iC(EmitterContext context)
+ {
+ InstF2iC op = context.GetOp<InstF2iC>();
+
+ var src = UnpackCbuf(context, op.SrcFmt, op.Sh, op.CbufSlot, op.CbufOffset);
+
+ EmitF2I(context, op.SrcFmt, op.IDstFmt, op.RoundMode, src, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void I2fR(EmitterContext context)
+ {
+ InstI2fR op = context.GetOp<InstI2fR>();
- case RoundingMode.TowardsNegativeInfinity:
- srcB = context.FPFloor(srcB, srcType.ToInstFPType());
- break;
+ var src = GetSrcReg(context, op.SrcB);
- case RoundingMode.TowardsPositiveInfinity:
- srcB = context.FPCeiling(srcB, srcType.ToInstFPType());
- break;
+ EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void I2fI(EmitterContext context)
+ {
+ InstI2fI op = context.GetOp<InstI2fI>();
+
+ var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void I2fC(EmitterContext context)
+ {
+ InstI2fC op = context.GetOp<InstI2fC>();
+
+ var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitI2F(context, op.ISrcFmt, op.DstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void I2iR(EmitterContext context)
+ {
+ InstI2iR op = context.GetOp<InstI2iR>();
+
+ var src = GetSrcReg(context, op.SrcB);
+
+ EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
+
+ public static void I2iI(EmitterContext context)
+ {
+ InstI2iI op = context.GetOp<InstI2iI>();
+
+ var src = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
+
+ public static void I2iC(EmitterContext context)
+ {
+ InstI2iC op = context.GetOp<InstI2iC>();
- case RoundingMode.TowardsZero:
- srcB = context.FPTruncate(srcB, srcType.ToInstFPType());
- break;
- }
+ var src = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitI2I(context, op.SrcFmt, op.IDstFmt, src, op.ByteSel, op.Dest, op.AbsB, op.NegB, op.Sat);
+ }
+
+ private static void EmitF2F(
+ EmitterContext context,
+ DstFmt srcType,
+ DstFmt dstType,
+ IntegerRound roundingMode,
+ Operand src,
+ int rd,
+ bool absolute,
+ bool negate,
+ bool saturate)
+ {
+ Operand srcB = context.FPAbsNeg(src, absolute, negate, srcType.ToInstFPType());
+
+ if (srcType == dstType)
+ {
+ srcB = roundingMode switch
+ {
+ IntegerRound.Round => context.FPRound(srcB, srcType.ToInstFPType()),
+ IntegerRound.Floor => context.FPFloor(srcB, srcType.ToInstFPType()),
+ IntegerRound.Ceil => context.FPCeiling(srcB, srcType.ToInstFPType()),
+ IntegerRound.Trunc => context.FPTruncate(srcB, srcType.ToInstFPType()),
+ _ => srcB
+ };
}
// We don't need to handle conversions between FP16 <-> FP32
// since we do FP16 operations as FP32 directly.
// FP16 <-> FP64 conversions are invalid.
- if (srcType == FPType.FP32 && dstType == FPType.FP64)
+ if (srcType == DstFmt.F32 && dstType == DstFmt.F64)
{
srcB = context.FP32ConvertToFP64(srcB);
}
- else if (srcType == FPType.FP64 && dstType == FPType.FP32)
+ else if (srcType == DstFmt.F64 && dstType == DstFmt.F32)
{
srcB = context.FP64ConvertToFP32(srcB);
}
- srcB = context.FPSaturate(srcB, op.Saturate, dstType.ToInstFPType());
+ srcB = context.FPSaturate(srcB, saturate, dstType.ToInstFPType());
- WriteFP(context, dstType, srcB);
+ WriteFP(context, dstType, srcB, rd);
// TODO: CC.
}
- public static void F2I(EmitterContext context)
+ private static void EmitF2I(
+ EmitterContext context,
+ DstFmt srcType,
+ IDstFmt dstType,
+ RoundMode2 roundingMode,
+ Operand src,
+ int rd,
+ bool absolute,
+ bool negate)
{
- OpCodeFArith op = (OpCodeFArith)context.CurrOp;
-
- IntegerType intType = (IntegerType)op.RawOpCode.Extract(8, 2);
-
- if (intType == IntegerType.U64)
+ if (dstType == IDstFmt.U64)
{
context.Config.GpuAccessor.Log("Unimplemented 64-bits F2I.");
-
return;
}
- bool isSmallInt = intType <= IntegerType.U16;
-
- FPType floatType = (FPType)op.RawOpCode.Extract(10, 2);
-
- bool isSignedInt = op.RawOpCode.Extract(12);
- bool negateB = op.RawOpCode.Extract(45);
- bool absoluteB = op.RawOpCode.Extract(49);
-
- if (isSignedInt)
- {
- intType |= IntegerType.S8;
- }
+ bool isSignedInt = dstType == IDstFmt.S16 || dstType == IDstFmt.S32 || dstType == IDstFmt.S64;
+ bool isSmallInt = dstType == IDstFmt.U16 || dstType == IDstFmt.S16;
- Operand srcB = context.FPAbsNeg(GetSrcB(context, floatType), absoluteB, negateB);
+ Operand srcB = context.FPAbsNeg(src, absolute, negate);
- switch (op.RoundingMode)
+ srcB = roundingMode switch
{
- case RoundingMode.ToNearest:
- srcB = context.FPRound(srcB);
- break;
-
- case RoundingMode.TowardsNegativeInfinity:
- srcB = context.FPFloor(srcB);
- break;
-
- case RoundingMode.TowardsPositiveInfinity:
- srcB = context.FPCeiling(srcB);
- break;
-
- case RoundingMode.TowardsZero:
- srcB = context.FPTruncate(srcB);
- break;
- }
+ RoundMode2.Round => context.FPRound(srcB),
+ RoundMode2.Floor => context.FPFloor(srcB),
+ RoundMode2.Ceil => context.FPCeiling(srcB),
+ RoundMode2.Trunc => context.FPTruncate(srcB),
+ _ => srcB
+ };
if (!isSignedInt)
{
- // Negative float to uint cast is undefined, so we clamp
- // the value before conversion.
+ // Negative float to uint cast is undefined, so we clamp the value before conversion.
srcB = context.FPMaximum(srcB, ConstF(0));
}
- srcB = isSignedInt
- ? context.FPConvertToS32(srcB)
- : context.FPConvertToU32(srcB);
+ srcB = isSignedInt ? context.FPConvertToS32(srcB) : context.FPConvertToU32(srcB);
if (isSmallInt)
{
- int min = (int)GetIntMin(intType);
- int max = (int)GetIntMax(intType);
+ int min = (int)GetIntMin(dstType);
+ int max = (int)GetIntMax(dstType);
srcB = isSignedInt
? context.IClampS32(srcB, Const(min), Const(max))
: context.IClampU32(srcB, Const(min), Const(max));
}
- Operand dest = GetDest(context);
+ Operand dest = GetDest(rd);
context.Copy(dest, srcB);
// TODO: CC.
}
- public static void I2F(EmitterContext context)
+ private static void EmitI2F(
+ EmitterContext context,
+ ISrcFmt srcType,
+ DstFmt dstType,
+ Operand src,
+ ByteSel byteSelection,
+ int rd,
+ bool absolute,
+ bool negate)
{
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- FPType dstType = (FPType)op.RawOpCode.Extract(8, 2);
-
- IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2);
+ bool isSignedInt =
+ srcType == ISrcFmt.S8 ||
+ srcType == ISrcFmt.S16 ||
+ srcType == ISrcFmt.S32 ||
+ srcType == ISrcFmt.S64;
+ bool isSmallInt =
+ srcType == ISrcFmt.U16 ||
+ srcType == ISrcFmt.S16 ||
+ srcType == ISrcFmt.U8 ||
+ srcType == ISrcFmt.S8;
// TODO: Handle S/U64.
- bool isSmallInt = srcType <= IntegerType.U16;
-
- bool isSignedInt = op.RawOpCode.Extract(13);
- bool negateB = op.RawOpCode.Extract(45);
- bool absoluteB = op.RawOpCode.Extract(49);
-
- Operand srcB = context.IAbsNeg(GetSrcB(context), absoluteB, negateB);
+ Operand srcB = context.IAbsNeg(src, absolute, negate);
if (isSmallInt)
{
- int size = srcType == IntegerType.U16 ? 16 : 8;
+ int size = srcType == ISrcFmt.U16 || srcType == ISrcFmt.S16 ? 16 : 8;
srcB = isSignedInt
- ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size))
- : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size));
+ ? context.BitfieldExtractS32(srcB, Const((int)byteSelection * 8), Const(size))
+ : context.BitfieldExtractU32(srcB, Const((int)byteSelection * 8), Const(size));
}
- srcB = isSignedInt
- ? context.IConvertS32ToFP(srcB)
- : context.IConvertU32ToFP(srcB);
+ srcB = isSignedInt ? context.IConvertS32ToFP(srcB) : context.IConvertU32ToFP(srcB);
- WriteFP(context, dstType, srcB);
+ WriteFP(context, dstType, srcB, rd);
// TODO: CC.
}
- public static void I2I(EmitterContext context)
+ private static void EmitI2I(
+ EmitterContext context,
+ ISrcFmt srcType,
+ IDstFmt dstType,
+ Operand src,
+ ByteSel byteSelection,
+ int rd,
+ bool absolute,
+ bool negate,
+ bool saturate)
{
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- IntegerType dstType = (IntegerType)op.RawOpCode.Extract(8, 2);
- IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2);
-
- if (srcType == IntegerType.U64 || dstType == IntegerType.U64)
+ if (srcType == ISrcFmt.U64 || dstType == IDstFmt.U64)
{
context.Config.GpuAccessor.Log("Invalid I2I encoding.");
-
return;
}
- bool srcIsSmallInt = srcType <= IntegerType.U16;
-
- bool dstIsSignedInt = op.RawOpCode.Extract(12);
- bool srcIsSignedInt = op.RawOpCode.Extract(13);
- bool negateB = op.RawOpCode.Extract(45);
- bool absoluteB = op.RawOpCode.Extract(49);
-
- Operand srcB = GetSrcB(context);
+ bool srcIsSignedInt =
+ srcType == ISrcFmt.S8 ||
+ srcType == ISrcFmt.S16 ||
+ srcType == ISrcFmt.S32 ||
+ srcType == ISrcFmt.S64;
+ bool dstIsSignedInt =
+ dstType == IDstFmt.S16 ||
+ dstType == IDstFmt.S32 ||
+ dstType == IDstFmt.S64;
+ bool srcIsSmallInt =
+ srcType == ISrcFmt.U16 ||
+ srcType == ISrcFmt.S16 ||
+ srcType == ISrcFmt.U8 ||
+ srcType == ISrcFmt.S8;
if (srcIsSmallInt)
{
- int size = srcType == IntegerType.U16 ? 16 : 8;
+ int size = srcType == ISrcFmt.U16 || srcType == ISrcFmt.S16 ? 16 : 8;
- srcB = srcIsSignedInt
- ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size))
- : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size));
+ src = srcIsSignedInt
+ ? context.BitfieldExtractS32(src, Const((int)byteSelection * 8), Const(size))
+ : context.BitfieldExtractU32(src, Const((int)byteSelection * 8), Const(size));
}
- srcB = context.IAbsNeg(srcB, absoluteB, negateB);
+ src = context.IAbsNeg(src, absolute, negate);
- if (op.Saturate)
+ if (saturate)
{
- if (dstIsSignedInt)
- {
- dstType |= IntegerType.S8;
- }
-
int min = (int)GetIntMin(dstType);
int max = (int)GetIntMax(dstType);
- srcB = dstIsSignedInt
- ? context.IClampS32(srcB, Const(min), Const(max))
- : context.IClampU32(srcB, Const(min), Const(max));
+ src = dstIsSignedInt
+ ? context.IClampS32(src, Const(min), Const(max))
+ : context.IClampU32(src, Const(min), Const(max));
}
- context.Copy(GetDest(context), srcB);
+ context.Copy(GetDest(rd), src);
// TODO: CC.
}
- private static void WriteFP(EmitterContext context, FPType type, Operand srcB)
+ private static Operand UnpackReg(EmitterContext context, DstFmt floatType, bool h, int reg)
+ {
+ if (floatType == DstFmt.F32)
+ {
+ return GetSrcReg(context, reg);
+ }
+ else if (floatType == DstFmt.F16)
+ {
+ return GetHalfUnpacked(context, GetSrcReg(context, reg), HalfSwizzle.F16)[h ? 1 : 0];
+ }
+ else if (floatType == DstFmt.F64)
+ {
+ return GetSrcReg(context, reg, isFP64: true);
+ }
+
+ throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
+ }
+
+ private static Operand UnpackCbuf(EmitterContext context, DstFmt floatType, bool h, int cbufSlot, int cbufOffset)
+ {
+ if (floatType == DstFmt.F32)
+ {
+ return GetSrcCbuf(context, cbufSlot, cbufOffset);
+ }
+ else if (floatType == DstFmt.F16)
+ {
+ return GetHalfUnpacked(context, GetSrcCbuf(context, cbufSlot, cbufOffset), HalfSwizzle.F16)[h ? 1 : 0];
+ }
+ else if (floatType == DstFmt.F64)
+ {
+ return GetSrcCbuf(context, cbufSlot, cbufOffset, isFP64: true);
+ }
+
+ throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
+ }
+
+ private static Operand UnpackImm(EmitterContext context, DstFmt floatType, bool h, int imm)
{
- Operand dest = GetDest(context);
+ if (floatType == DstFmt.F32)
+ {
+ return GetSrcImm(context, imm);
+ }
+ else if (floatType == DstFmt.F16)
+ {
+ return GetHalfUnpacked(context, GetSrcImm(context, imm), HalfSwizzle.F16)[h ? 1 : 0];
+ }
+ else if (floatType == DstFmt.F64)
+ {
+ return GetSrcImm(context, imm, isFP64: true);
+ }
- if (type == FPType.FP32)
+ throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
+ }
+
+ private static void WriteFP(EmitterContext context, DstFmt type, Operand srcB, int rd)
+ {
+ Operand dest = GetDest(rd);
+
+ if (type == DstFmt.F32)
{
context.Copy(dest, srcB);
}
- else if (type == FPType.FP16)
+ else if (type == DstFmt.F16)
{
context.Copy(dest, context.PackHalf2x16(srcB, ConstF(0)));
}
else /* if (type == FPType.FP64) */
{
- Operand dest2 = GetDest2(context);
+ Operand dest2 = GetDest2(rd);
context.Copy(dest, context.UnpackDouble2x32Low(srcB));
context.Copy(dest2, context.UnpackDouble2x32High(srcB));
}
}
+
+ private static Instruction ToInstFPType(this DstFmt type)
+ {
+ return type == DstFmt.F64 ? Instruction.FP64 : Instruction.FP32;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs
deleted file mode 100644
index 6dc4c093..00000000
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs
+++ /dev/null
@@ -1,547 +0,0 @@
-using Ryujinx.Graphics.Shader.Decoders;
-using Ryujinx.Graphics.Shader.IntermediateRepresentation;
-using Ryujinx.Graphics.Shader.Translation;
-using System;
-
-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 Dadd(EmitterContext context) => EmitFPAdd(context, Instruction.FP64);
- public static void Dfma(EmitterContext context) => EmitFPFma(context, Instruction.FP64);
- public static void Dmul(EmitterContext context) => EmitFPMultiply(context, Instruction.FP64);
-
- public static void Fadd(EmitterContext context) => EmitFPAdd(context, Instruction.FP32);
-
- public static void Fcmp(EmitterContext context)
- {
- OpCode op = context.CurrOp;
-
- Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
- Operand srcC = GetSrcC(context);
-
- Operand cmpRes = GetFPComparison(context, cmpOp, srcC, ConstF(0));
-
- Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Ffma(EmitterContext context) => EmitFPFma(context, Instruction.FP32);
-
- public static void Ffma32i(EmitterContext context)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool saturate = op.RawOpCode.Extract(55);
- bool negateA = op.RawOpCode.Extract(56);
- bool negateC = op.RawOpCode.Extract(57);
-
- Operand srcA = context.FPNegate(GetSrcA(context), negateA);
- Operand srcC = context.FPNegate(GetDest(context), negateC);
-
- Operand srcB = GetSrcB(context);
-
- Operand dest = GetDest(context);
-
- context.Copy(dest, context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC), saturate));
-
- SetFPZnFlags(context, dest, op.SetCondCode);
- }
-
- public static void Fmnmx(EmitterContext context)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool absoluteA = op.AbsoluteA;
- bool negateB = op.RawOpCode.Extract(45);
- bool negateA = op.RawOpCode.Extract(48);
- bool absoluteB = op.RawOpCode.Extract(49);
-
- Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
- Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
-
- Operand resMin = context.FPMinimum(srcA, srcB);
- Operand resMax = context.FPMaximum(srcA, srcB);
-
- Operand pred = GetPredicate39(context);
-
- Operand dest = GetDest(context);
-
- context.Copy(dest, context.ConditionalSelect(pred, resMin, resMax));
-
- SetFPZnFlags(context, dest, op.SetCondCode);
- }
-
- public static void Fmul(EmitterContext context) => EmitFPMultiply(context, Instruction.FP32);
-
- public static void Fset(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4);
-
- bool negateA = op.RawOpCode.Extract(43);
- bool absoluteB = op.RawOpCode.Extract(44);
- bool boolFloat = op.RawOpCode.Extract(52);
- bool negateB = op.RawOpCode.Extract(53);
- bool absoluteA = op.RawOpCode.Extract(54);
-
- Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
- Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
-
- Operand res = GetFPComparison(context, cmpOp, srcA, srcB);
-
- Operand pred = GetPredicate39(context);
-
- res = GetPredLogicalOp(context, op.LogicalOp, res, pred);
-
- Operand dest = GetDest(context);
-
- if (boolFloat)
- {
- res = context.ConditionalSelect(res, ConstF(1), Const(0));
-
- context.Copy(dest, res);
-
- SetFPZnFlags(context, res, op.SetCondCode);
- }
- else
- {
- context.Copy(dest, res);
-
- SetZnFlags(context, res, op.SetCondCode, op.Extended);
- }
-
- // TODO: X
- }
-
- public static void Fsetp(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4);
-
- bool negateB = op.RawOpCode.Extract(6);
- bool absoluteA = op.RawOpCode.Extract(7);
- bool negateA = op.RawOpCode.Extract(43);
- bool absoluteB = op.RawOpCode.Extract(44);
-
- Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA);
- Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB);
-
- Operand p0Res = GetFPComparison(context, cmpOp, srcA, srcB);
-
- Operand p1Res = context.BitwiseNot(p0Res);
-
- Operand pred = GetPredicate39(context);
-
- p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
- p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
-
- context.Copy(Register(op.Predicate3), p0Res);
- context.Copy(Register(op.Predicate0), p1Res);
- }
-
- public static void Fswzadd(EmitterContext context)
- {
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
-
- int mask = op.RawOpCode.Extract(28, 8);
-
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
-
- Operand dest = GetDest(context);
-
- context.Copy(dest, context.FPSwizzleAdd(srcA, srcB, mask));
-
- SetFPZnFlags(context, dest, op.SetCondCode);
- }
-
- public static void Hadd2(EmitterContext context)
- {
- Hadd2Hmul2Impl(context, isAdd: true);
- }
-
- public static void Hfma2(EmitterContext context)
- {
- IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
-
- Operand[] srcA = GetHfmaSrcA(context);
- Operand[] srcB = GetHfmaSrcB(context);
- Operand[] srcC = GetHfmaSrcC(context);
-
- Operand[] res = new Operand[2];
-
- for (int index = 0; index < res.Length; index++)
- {
- res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]);
-
- res[index] = context.FPSaturate(res[index], op.Saturate);
- }
-
- context.Copy(GetDest(context), GetHalfPacked(context, res));
- }
-
- public static void Hmul2(EmitterContext context)
- {
- Hadd2Hmul2Impl(context, isAdd: false);
- }
-
- private static void Hadd2Hmul2Impl(EmitterContext context, bool isAdd)
- {
- OpCode op = context.CurrOp;
-
- bool saturate = op.RawOpCode.Extract(op is IOpCodeReg ? 32 : 52);
-
- Operand[] srcA = GetHalfSrcA(context, isAdd);
- Operand[] srcB = GetHalfSrcB(context, !isAdd);
-
- Operand[] res = new Operand[2];
-
- for (int index = 0; index < res.Length; index++)
- {
- if (isAdd)
- {
- res[index] = context.FPAdd(srcA[index], srcB[index]);
- }
- else
- {
- res[index] = context.FPMultiply(srcA[index], srcB[index]);
- }
-
- res[index] = context.FPSaturate(res[index], saturate);
- }
-
- context.Copy(GetDest(context), GetHalfPacked(context, res));
- }
-
- public static void Hset2(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- bool isRegVariant = op is IOpCodeReg;
-
- bool boolFloat = isRegVariant
- ? op.RawOpCode.Extract(49)
- : op.RawOpCode.Extract(53);
-
- Condition cmpOp = isRegVariant
- ? (Condition)op.RawOpCode.Extract(35, 4)
- : (Condition)op.RawOpCode.Extract(49, 4);
-
- Operand[] srcA = GetHalfSrcA(context);
- Operand[] srcB = GetHalfSrcB(context);
-
- Operand[] res = new Operand[2];
-
- res[0] = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
- res[1] = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
-
- Operand pred = GetPredicate39(context);
-
- res[0] = GetPredLogicalOp(context, op.LogicalOp, res[0], pred);
- res[1] = GetPredLogicalOp(context, op.LogicalOp, res[1], pred);
-
- if (boolFloat)
- {
- res[0] = context.ConditionalSelect(res[0], ConstF(1), Const(0));
- res[1] = context.ConditionalSelect(res[1], ConstF(1), Const(0));
-
- context.Copy(GetDest(context), context.PackHalf2x16(res[0], res[1]));
- }
- else
- {
- Operand low = context.BitwiseAnd(res[0], Const(0xffff));
- Operand high = context.ShiftLeft (res[1], Const(16));
-
- Operand packed = context.BitwiseOr(low, high);
-
- context.Copy(GetDest(context), packed);
- }
- }
-
- public static void Hsetp2(EmitterContext context)
- {
- OpCodeSet op = (OpCodeSet)context.CurrOp;
-
- bool isRegVariant = op is IOpCodeReg;
-
- bool hAnd = isRegVariant
- ? op.RawOpCode.Extract(49)
- : op.RawOpCode.Extract(53);
-
- Condition cmpOp = isRegVariant
- ? (Condition)op.RawOpCode.Extract(35, 4)
- : (Condition)op.RawOpCode.Extract(49, 4);
-
- Operand[] srcA = GetHalfSrcA(context);
- Operand[] srcB = GetHalfSrcB(context);
-
- Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
- Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
-
- if (hAnd)
- {
- p0Res = context.BitwiseAnd(p0Res, p1Res);
- p1Res = context.BitwiseNot(p0Res);
- }
-
- Operand pred = GetPredicate39(context);
-
- p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred);
- p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred);
-
- context.Copy(Register(op.Predicate3), p0Res);
- context.Copy(Register(op.Predicate0), p1Res);
- }
-
- public static void Mufu(EmitterContext context)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool negateB = op.RawOpCode.Extract(48);
-
- Operand res = context.FPAbsNeg(GetSrcA(context), op.AbsoluteA, negateB);
-
- MufuOperation subOp = (MufuOperation)context.CurrOp.RawOpCode.Extract(20, 4);
-
- switch (subOp)
- {
- case MufuOperation.Cosine:
- res = context.FPCosine(res);
- break;
-
- case MufuOperation.Sine:
- res = context.FPSine(res);
- break;
-
- case MufuOperation.ExponentB2:
- res = context.FPExponentB2(res);
- break;
-
- case MufuOperation.LogarithmB2:
- res = context.FPLogarithmB2(res);
- break;
-
- case MufuOperation.Reciprocal:
- res = context.FPReciprocal(res);
- break;
-
- case MufuOperation.ReciprocalSquareRoot:
- res = context.FPReciprocalSquareRoot(res);
- break;
-
- case MufuOperation.SquareRoot:
- res = context.FPSquareRoot(res);
- break;
-
- default: /* TODO */ break;
- }
-
- context.Copy(GetDest(context), context.FPSaturate(res, op.Saturate));
- }
-
- private static void EmitFPAdd(EmitterContext context, Instruction fpType)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool isFP64 = fpType == Instruction.FP64;
-
- bool absoluteA = op.AbsoluteA, absoluteB, negateA, negateB;
-
- if (op is OpCodeFArithImm32)
- {
- negateB = op.RawOpCode.Extract(53);
- negateA = op.RawOpCode.Extract(56);
- absoluteB = op.RawOpCode.Extract(57);
- }
- else
- {
- negateB = op.RawOpCode.Extract(45);
- negateA = op.RawOpCode.Extract(48);
- absoluteB = op.RawOpCode.Extract(49);
- }
-
- Operand srcA = context.FPAbsNeg(GetSrcA(context, isFP64), absoluteA, negateA, fpType);
- Operand srcB = context.FPAbsNeg(GetSrcB(context, isFP64), absoluteB, negateB, fpType);
-
- Operand res = context.FPSaturate(context.FPAdd(srcA, srcB, fpType), op.Saturate, fpType);
-
- SetDest(context, res, isFP64);
-
- SetFPZnFlags(context, res, op.SetCondCode, fpType);
- }
-
- private static void EmitFPFma(EmitterContext context, Instruction fpType)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool isFP64 = fpType == Instruction.FP64;
-
- bool negateB = op.RawOpCode.Extract(48);
- bool negateC = op.RawOpCode.Extract(49);
-
- Operand srcA = GetSrcA(context, isFP64);
-
- Operand srcB = context.FPNegate(GetSrcB(context, isFP64), negateB, fpType);
- Operand srcC = context.FPNegate(GetSrcC(context, isFP64), negateC, fpType);
-
- Operand res = context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC, fpType), op.Saturate, fpType);
-
- SetDest(context, res, isFP64);
-
- SetFPZnFlags(context, res, op.SetCondCode, fpType);
- }
-
- private static void EmitFPMultiply(EmitterContext context, Instruction fpType)
- {
- IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
-
- bool isFP64 = fpType == Instruction.FP64;
-
- bool isImm32 = op is OpCodeFArithImm32;
-
- bool negateB = !isImm32 && op.RawOpCode.Extract(48);
-
- Operand srcA = GetSrcA(context, isFP64);
-
- Operand srcB = context.FPNegate(GetSrcB(context, isFP64), negateB, fpType);
-
- if (op.Scale != FPMultiplyScale.None)
- {
- Operand scale = op.Scale switch
- {
- FPMultiplyScale.Divide2 => ConstF(0.5f),
- FPMultiplyScale.Divide4 => ConstF(0.25f),
- FPMultiplyScale.Divide8 => ConstF(0.125f),
- FPMultiplyScale.Multiply2 => ConstF(2f),
- FPMultiplyScale.Multiply4 => ConstF(4f),
- FPMultiplyScale.Multiply8 => ConstF(8f),
- _ => ConstF(1) // Invalid, behave as if it had no scale.
- };
-
- if (scale.AsFloat() == 1)
- {
- context.Config.GpuAccessor.Log($"Invalid FP multiply scale \"{op.Scale}\".");
- }
-
- if (isFP64)
- {
- scale = context.FP32ConvertToFP64(scale);
- }
-
- srcA = context.FPMultiply(srcA, scale, fpType);
- }
-
- bool saturate = isImm32 ? op.RawOpCode.Extract(55) : op.Saturate;
-
- Operand res = context.FPSaturate(context.FPMultiply(srcA, srcB, fpType), saturate, fpType);
-
- SetDest(context, res, isFP64);
-
- SetFPZnFlags(context, res, op.SetCondCode, fpType);
- }
-
- private static Operand GetFPComparison(
- EmitterContext context,
- Condition cond,
- Operand srcA,
- Operand srcB)
- {
- Operand res;
-
- if (cond == Condition.Always)
- {
- res = Const(IrConsts.True);
- }
- else if (cond == Condition.Never)
- {
- res = Const(IrConsts.False);
- }
- else if (cond == Condition.Nan || cond == Condition.Number)
- {
- res = context.BitwiseOr(context.IsNan(srcA), context.IsNan(srcB));
-
- if (cond == Condition.Number)
- {
- res = context.BitwiseNot(res);
- }
- }
- else
- {
- Instruction inst;
-
- switch (cond & ~Condition.Nan)
- {
- case Condition.Less: inst = Instruction.CompareLess; break;
- case Condition.Equal: inst = Instruction.CompareEqual; break;
- case Condition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break;
- case Condition.Greater: inst = Instruction.CompareGreater; break;
- case Condition.NotEqual: inst = Instruction.CompareNotEqual; break;
- case Condition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break;
-
- default: throw new InvalidOperationException($"Unexpected condition \"{cond}\".");
- }
-
- res = context.Add(inst | Instruction.FP32, Local(), srcA, srcB);
-
- if ((cond & Condition.Nan) != 0)
- {
- res = context.BitwiseOr(res, context.IsNan(srcA));
- res = context.BitwiseOr(res, context.IsNan(srcB));
- }
- }
-
- return res;
- }
-
- private static Operand[] GetHfmaSrcA(EmitterContext context)
- {
- IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
-
- return GetHalfUnpacked(context, GetSrcA(context), op.SwizzleA);
- }
-
- private static Operand[] GetHfmaSrcB(EmitterContext context)
- {
- IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
-
- Operand[] operands = GetHalfUnpacked(context, GetSrcB(context), op.SwizzleB);
-
- return FPAbsNeg(context, operands, false, op.NegateB);
- }
-
- private static Operand[] GetHfmaSrcC(EmitterContext context)
- {
- IOpCodeHfma op = (IOpCodeHfma)context.CurrOp;
-
- Operand[] operands = GetHalfUnpacked(context, GetSrcC(context), op.SwizzleC);
-
- return FPAbsNeg(context, operands, false, op.NegateC);
- }
-
- private static void SetDest(EmitterContext context, Operand value, bool isFP64)
- {
- if (isFP64)
- {
- IOpCodeRd op = (IOpCodeRd)context.CurrOp;
-
- context.Copy(Register(op.Rd.Index, op.Rd.Type), context.UnpackDouble2x32Low(value));
- context.Copy(Register(op.Rd.Index | 1, op.Rd.Type), context.UnpackDouble2x32High(value));
- }
- else
- {
- context.Copy(GetDest(context), value);
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs
new file mode 100644
index 00000000..2318f2b7
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatArithmetic.cs
@@ -0,0 +1,545 @@
+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 DaddR(EmitterContext context)
+ {
+ InstDaddR op = context.GetOp<InstDaddR>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
+
+ EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
+ }
+
+ public static void DaddI(EmitterContext context)
+ {
+ InstDaddI op = context.GetOp<InstDaddI>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
+
+ EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
+ }
+
+ public static void DaddC(EmitterContext context)
+ {
+ InstDaddC op = context.GetOp<InstDaddC>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
+
+ EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
+ }
+
+ public static void DfmaR(EmitterContext context)
+ {
+ InstDfmaR op = context.GetOp<InstDfmaR>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
+ var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
+
+ EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
+ }
+
+ public static void DfmaI(EmitterContext context)
+ {
+ InstDfmaI op = context.GetOp<InstDfmaI>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
+ var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
+
+ EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
+ }
+
+ public static void DfmaC(EmitterContext context)
+ {
+ InstDfmaC op = context.GetOp<InstDfmaC>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
+ var srcC = GetSrcReg(context, op.SrcC, isFP64: true);
+
+ EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
+ }
+
+ public static void DfmaRc(EmitterContext context)
+ {
+ InstDfmaRc op = context.GetOp<InstDfmaRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcReg(context, op.SrcC, isFP64: true);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
+
+ EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC);
+ }
+
+ public static void DmulR(EmitterContext context)
+ {
+ InstDmulR op = context.GetOp<InstDmulR>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcReg(context, op.SrcB, isFP64: true);
+
+ EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
+ }
+
+ public static void DmulI(EmitterContext context)
+ {
+ InstDmulI op = context.GetOp<InstDmulI>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true);
+
+ EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
+ }
+
+ public static void DmulC(EmitterContext context)
+ {
+ InstDmulC op = context.GetOp<InstDmulC>();
+
+ var srcA = GetSrcReg(context, op.SrcA, isFP64: true);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true);
+
+ EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC);
+ }
+
+ public static void FaddR(EmitterContext context)
+ {
+ InstFaddR op = context.GetOp<InstFaddR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
+ }
+
+ public static void FaddI(EmitterContext context)
+ {
+ InstFaddI op = context.GetOp<InstFaddI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+
+ EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
+ }
+
+ public static void FaddC(EmitterContext context)
+ {
+ InstFaddC op = context.GetOp<InstFaddC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC);
+ }
+
+ public static void Fadd32i(EmitterContext context)
+ {
+ InstFadd32i op = context.GetOp<InstFadd32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+
+ EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC);
+ }
+
+ public static void FfmaR(EmitterContext context)
+ {
+ InstFfmaR op = context.GetOp<InstFfmaR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
+ }
+
+ public static void FfmaI(EmitterContext context)
+ {
+ InstFfmaI op = context.GetOp<InstFfmaI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
+ }
+
+ public static void FfmaC(EmitterContext context)
+ {
+ InstFfmaC op = context.GetOp<InstFfmaC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
+ }
+
+ public static void FfmaRc(EmitterContext context)
+ {
+ InstFfmaRc op = context.GetOp<InstFfmaRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
+ }
+
+ public static void Ffma32i(EmitterContext context)
+ {
+ InstFfma32i op = context.GetOp<InstFfma32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC);
+ }
+
+ public static void FmulR(EmitterContext context)
+ {
+ InstFmulR op = context.GetOp<InstFmulR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
+ }
+
+ public static void FmulI(EmitterContext context)
+ {
+ InstFmulI op = context.GetOp<InstFmulI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+
+ EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
+ }
+
+ public static void FmulC(EmitterContext context)
+ {
+ InstFmulC op = context.GetOp<InstFmulC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC);
+ }
+
+ public static void Fmul32i(EmitterContext context)
+ {
+ InstFmul32i op = context.GetOp<InstFmul32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+
+ EmitFmul(context, Instruction.FP32, MultiplyScale.NoScale, srcA, srcB, op.Dest, false, op.Sat, op.WriteCC);
+ }
+
+ public static void Hadd2R(EmitterContext context)
+ {
+ InstHadd2R op = context.GetOp<InstHadd2R>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
+ }
+
+ public static void Hadd2I(EmitterContext context)
+ {
+ InstHadd2I op = context.GetOp<InstHadd2I>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
+ }
+
+ public static void Hadd2C(EmitterContext context)
+ {
+ InstHadd2C op = context.GetOp<InstHadd2C>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, op.AbsB);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat);
+ }
+
+ public static void Hadd232i(EmitterContext context)
+ {
+ InstHadd232i op = context.GetOp<InstHadd232i>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, false);
+ var srcB = GetHalfSrc(context, op.Imm);
+
+ EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: true, op.Dest, op.Sat);
+ }
+
+ public static void Hfma2R(EmitterContext context)
+ {
+ InstHfma2R op = context.GetOp<InstHfma2R>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, false);
+ var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
+
+ EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
+ }
+
+ public static void Hfma2I(EmitterContext context)
+ {
+ InstHfma2I op = context.GetOp<InstHfma2I>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
+ var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
+
+ EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
+ }
+
+ public static void Hfma2C(EmitterContext context)
+ {
+ InstHfma2C op = context.GetOp<InstHfma2C>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, false);
+ var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false);
+
+ EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
+ }
+
+ public static void Hfma2Rc(EmitterContext context)
+ {
+ InstHfma2Rc op = context.GetOp<InstHfma2Rc>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegA, false);
+ var srcC = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegC, false);
+
+ EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat);
+ }
+
+ public static void Hfma232iI(EmitterContext context)
+ {
+ InstHfma232i op = context.GetOp<InstHfma232i>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, op.Imm);
+ var srcC = GetHalfSrc(context, HalfSwizzle.F16, op.SrcC, op.NegC, false);
+
+ EmitHfma2(context, OFmt.F16, srcA, srcB, srcC, op.Dest, saturate: false);
+ }
+
+ public static void Hmul2R(EmitterContext context)
+ {
+ InstHmul2R op = context.GetOp<InstHmul2R>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, op.AbsB);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
+ }
+
+ public static void Hmul2I(EmitterContext context)
+ {
+ InstHmul2I op = context.GetOp<InstHmul2I>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
+ }
+
+ public static void Hmul2C(EmitterContext context)
+ {
+ InstHmul2C op = context.GetOp<InstHmul2C>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA);
+ var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, op.AbsB);
+
+ EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat);
+ }
+
+ public static void Hmul232i(EmitterContext context)
+ {
+ InstHmul232i op = context.GetOp<InstHmul232i>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false);
+ var srcB = GetHalfSrc(context, op.Imm32);
+
+ EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: false, op.Dest, op.Sat);
+ }
+
+ private static void EmitFadd(
+ EmitterContext context,
+ Instruction fpType,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ bool negateA,
+ bool negateB,
+ bool absoluteA,
+ bool absoluteB,
+ bool saturate,
+ bool writeCC)
+ {
+ bool isFP64 = fpType == Instruction.FP64;
+
+ srcA = context.FPAbsNeg(srcA, absoluteA, negateA, fpType);
+ srcB = context.FPAbsNeg(srcB, absoluteB, negateB, fpType);
+
+ Operand res = context.FPSaturate(context.FPAdd(srcA, srcB, fpType), saturate, fpType);
+
+ SetDest(context, res, rd, isFP64);
+
+ SetFPZnFlags(context, res, writeCC, fpType);
+ }
+
+ private static void EmitFfma(
+ EmitterContext context,
+ Instruction fpType,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ bool negateB,
+ bool negateC,
+ bool saturate,
+ bool writeCC)
+ {
+ bool isFP64 = fpType == Instruction.FP64;
+
+ srcB = context.FPNegate(srcB, negateB, fpType);
+ srcC = context.FPNegate(srcC, negateC, fpType);
+
+ Operand res = context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC, fpType), saturate, fpType);
+
+ SetDest(context, res, rd, isFP64);
+
+ SetFPZnFlags(context, res, writeCC, fpType);
+ }
+
+ private static void EmitFmul(
+ EmitterContext context,
+ Instruction fpType,
+ MultiplyScale scale,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ bool negateB,
+ bool saturate,
+ bool writeCC)
+ {
+ bool isFP64 = fpType == Instruction.FP64;
+
+ srcB = context.FPNegate(srcB, negateB, fpType);
+
+ if (scale != MultiplyScale.NoScale)
+ {
+ Operand scaleConst = scale switch
+ {
+ MultiplyScale.D2 => ConstF(0.5f),
+ MultiplyScale.D4 => ConstF(0.25f),
+ MultiplyScale.D8 => ConstF(0.125f),
+ MultiplyScale.M2 => ConstF(2f),
+ MultiplyScale.M4 => ConstF(4f),
+ MultiplyScale.M8 => ConstF(8f),
+ _ => ConstF(1f) // Invalid, behave as if it had no scale.
+ };
+
+ if (scaleConst.AsFloat() == 1f)
+ {
+ context.Config.GpuAccessor.Log($"Invalid FP multiply scale \"{scale}\".");
+ }
+
+ if (isFP64)
+ {
+ scaleConst = context.FP32ConvertToFP64(scaleConst);
+ }
+
+ srcA = context.FPMultiply(srcA, scaleConst, fpType);
+ }
+
+ Operand res = context.FPSaturate(context.FPMultiply(srcA, srcB, fpType), saturate, fpType);
+
+ SetDest(context, res, rd, isFP64);
+
+ SetFPZnFlags(context, res, writeCC, fpType);
+ }
+
+ private static void EmitHadd2Hmul2(
+ EmitterContext context,
+ OFmt swizzle,
+ Operand[] srcA,
+ Operand[] srcB,
+ bool isAdd,
+ int rd,
+ bool saturate)
+ {
+ Operand[] res = new Operand[2];
+
+ for (int index = 0; index < res.Length; index++)
+ {
+ if (isAdd)
+ {
+ res[index] = context.FPAdd(srcA[index], srcB[index]);
+ }
+ else
+ {
+ res[index] = context.FPMultiply(srcA[index], srcB[index]);
+ }
+
+ res[index] = context.FPSaturate(res[index], saturate);
+ }
+
+ context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd));
+ }
+
+ public static void EmitHfma2(
+ EmitterContext context,
+ OFmt swizzle,
+ Operand[] srcA,
+ Operand[] srcB,
+ Operand[] srcC,
+ int rd,
+ bool saturate)
+ {
+ Operand[] res = new Operand[2];
+
+ for (int index = 0; index < res.Length; index++)
+ {
+ res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]);
+ res[index] = context.FPSaturate(res[index], saturate);
+ }
+
+ context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd));
+ }
+
+ private static void SetDest(EmitterContext context, Operand value, int rd, bool isFP64)
+ {
+ if (isFP64)
+ {
+ context.Copy(GetDest(rd), context.UnpackDouble2x32Low(value));
+ context.Copy(GetDest2(rd), context.UnpackDouble2x32High(value));
+ }
+ else
+ {
+ context.Copy(GetDest(rd), value);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs
new file mode 100644
index 00000000..b7b5f9ba
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatComparison.cs
@@ -0,0 +1,419 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System;
+
+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 FcmpR(EmitterContext context)
+ {
+ InstFcmpR op = context.GetOp<InstFcmpR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void FcmpI(EmitterContext context)
+ {
+ InstFcmpI op = context.GetOp<InstFcmpI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void FcmpC(EmitterContext context)
+ {
+ InstFcmpC op = context.GetOp<InstFcmpC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void FcmpRc(EmitterContext context)
+ {
+ InstFcmpRc op = context.GetOp<InstFcmpRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFcmp(context, op.FComp, srcA, srcB, srcC, op.Dest);
+ }
+
+ public static void FsetR(EmitterContext context)
+ {
+ InstFsetR op = context.GetOp<InstFsetR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC);
+ }
+
+ public static void FsetC(EmitterContext context)
+ {
+ InstFsetC op = context.GetOp<InstFsetC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC);
+ }
+
+ public static void FsetI(EmitterContext context)
+ {
+ InstFsetI op = context.GetOp<InstFsetI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+
+ EmitFset(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.BVal, op.WriteCC);
+ }
+
+ public static void FsetpR(EmitterContext context)
+ {
+ InstFsetpR op = context.GetOp<InstFsetpR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitFsetp(
+ context,
+ op.FComp,
+ op.Bop,
+ srcA,
+ srcB,
+ op.SrcPred,
+ op.SrcPredInv,
+ op.DestPred,
+ op.DestPredInv,
+ op.AbsA,
+ op.AbsB,
+ op.NegA,
+ op.NegB,
+ op.WriteCC);
+ }
+
+ public static void FsetpI(EmitterContext context)
+ {
+ InstFsetpI op = context.GetOp<InstFsetpI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+
+ EmitFsetp(
+ context,
+ op.FComp,
+ op.Bop,
+ srcA,
+ srcB,
+ op.SrcPred,
+ op.SrcPredInv,
+ op.DestPred,
+ op.DestPredInv,
+ op.AbsA,
+ op.AbsB,
+ op.NegA,
+ op.NegB,
+ op.WriteCC);
+ }
+
+ public static void FsetpC(EmitterContext context)
+ {
+ InstFsetpC op = context.GetOp<InstFsetpC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitFsetp(
+ context,
+ op.FComp,
+ op.Bop,
+ srcA,
+ srcB,
+ op.SrcPred,
+ op.SrcPredInv,
+ op.DestPred,
+ op.DestPredInv,
+ op.AbsA,
+ op.AbsB,
+ op.NegA,
+ op.NegB,
+ op.WriteCC);
+ }
+
+ public static void Hset2R(EmitterContext context)
+ {
+ InstHset2R op = context.GetOp<InstHset2R>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB);
+
+ EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval);
+ }
+
+ public static void Hset2I(EmitterContext context)
+ {
+ InstHset2I op = context.GetOp<InstHset2I>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
+
+ EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval);
+ }
+
+ public static void Hset2C(EmitterContext context)
+ {
+ InstHset2C op = context.GetOp<InstHset2C>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, false);
+
+ EmitHset2(context, op.Cmp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.Bval);
+ }
+
+ public static void Hsetp2R(EmitterContext context)
+ {
+ InstHsetp2R op = context.GetOp<InstHsetp2R>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB);
+
+ EmitHsetp2(context, op.FComp2, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd);
+ }
+
+ public static void Hsetp2I(EmitterContext context)
+ {
+ InstHsetp2I op = context.GetOp<InstHsetp2I>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1);
+
+ EmitHsetp2(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd);
+ }
+
+ public static void Hsetp2C(EmitterContext context)
+ {
+ InstHsetp2C op = context.GetOp<InstHsetp2C>();
+
+ var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA);
+ var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, op.AbsB);
+
+ EmitHsetp2(context, op.FComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.HAnd);
+ }
+
+ private static void EmitFcmp(EmitterContext context, FComp cmpOp, Operand srcA, Operand srcB, Operand srcC, int rd)
+ {
+ Operand cmpRes = GetFPComparison(context, cmpOp, srcC, ConstF(0));
+
+ Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitFset(
+ EmitterContext context,
+ FComp cmpOp,
+ BoolOp logicOp,
+ Operand srcA,
+ Operand srcB,
+ int srcPred,
+ bool srcPredInv,
+ int rd,
+ bool absoluteA,
+ bool absoluteB,
+ bool negateA,
+ bool negateB,
+ bool boolFloat,
+ bool writeCC)
+ {
+ srcA = context.FPAbsNeg(srcA, absoluteA, negateA);
+ srcB = context.FPAbsNeg(srcB, absoluteB, negateB);
+
+ Operand res = GetFPComparison(context, cmpOp, srcA, srcB);
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ res = GetPredLogicalOp(context, logicOp, res, pred);
+
+ Operand dest = GetDest(rd);
+
+ if (boolFloat)
+ {
+ res = context.ConditionalSelect(res, ConstF(1), Const(0));
+
+ context.Copy(dest, res);
+
+ SetFPZnFlags(context, res, writeCC);
+ }
+ else
+ {
+ context.Copy(dest, res);
+
+ SetZnFlags(context, res, writeCC, extended: false);
+ }
+ }
+
+ private static void EmitFsetp(
+ EmitterContext context,
+ FComp cmpOp,
+ BoolOp logicOp,
+ Operand srcA,
+ Operand srcB,
+ int srcPred,
+ bool srcPredInv,
+ int destPred,
+ int destPredInv,
+ bool absoluteA,
+ bool absoluteB,
+ bool negateA,
+ bool negateB,
+ bool writeCC)
+ {
+ srcA = context.FPAbsNeg(srcA, absoluteA, negateA);
+ srcB = context.FPAbsNeg(srcB, absoluteB, negateB);
+
+ Operand p0Res = GetFPComparison(context, cmpOp, srcA, srcB);
+ Operand p1Res = context.BitwiseNot(p0Res);
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred);
+ p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred);
+
+ context.Copy(Register(destPred, RegisterType.Predicate), p0Res);
+ context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res);
+ }
+
+ private static void EmitHset2(
+ EmitterContext context,
+ FComp cmpOp,
+ BoolOp logicOp,
+ Operand[] srcA,
+ Operand[] srcB,
+ int srcPred,
+ bool srcPredInv,
+ int rd,
+ bool boolFloat)
+ {
+ Operand[] res = new Operand[2];
+
+ res[0] = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
+ res[1] = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
+
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ res[0] = GetPredLogicalOp(context, logicOp, res[0], pred);
+ res[1] = GetPredLogicalOp(context, logicOp, res[1], pred);
+
+ if (boolFloat)
+ {
+ res[0] = context.ConditionalSelect(res[0], ConstF(1), Const(0));
+ res[1] = context.ConditionalSelect(res[1], ConstF(1), Const(0));
+
+ context.Copy(GetDest(rd), context.PackHalf2x16(res[0], res[1]));
+ }
+ else
+ {
+ Operand low = context.BitwiseAnd(res[0], Const(0xffff));
+ Operand high = context.ShiftLeft (res[1], Const(16));
+
+ Operand packed = context.BitwiseOr(low, high);
+
+ context.Copy(GetDest(rd), packed);
+ }
+ }
+
+ private static void EmitHsetp2(
+ EmitterContext context,
+ FComp cmpOp,
+ BoolOp logicOp,
+ Operand[] srcA,
+ Operand[] srcB,
+ int srcPred,
+ bool srcPredInv,
+ int destPred,
+ int destPredInv,
+ bool hAnd)
+ {
+ Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]);
+ Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]);
+
+ if (hAnd)
+ {
+ p0Res = context.BitwiseAnd(p0Res, p1Res);
+ p1Res = context.BitwiseNot(p0Res);
+ }
+
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred);
+ p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred);
+
+ context.Copy(Register(destPred, RegisterType.Predicate), p0Res);
+ context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res);
+ }
+
+ private static Operand GetFPComparison(EmitterContext context, FComp cond, Operand srcA, Operand srcB)
+ {
+ Operand res;
+
+ if (cond == FComp.T)
+ {
+ res = Const(IrConsts.True);
+ }
+ else if (cond == FComp.F)
+ {
+ res = Const(IrConsts.False);
+ }
+ else if (cond == FComp.Nan || cond == FComp.Num)
+ {
+ res = context.BitwiseOr(context.IsNan(srcA), context.IsNan(srcB));
+
+ if (cond == FComp.Num)
+ {
+ res = context.BitwiseNot(res);
+ }
+ }
+ else
+ {
+ Instruction inst;
+
+ switch (cond & ~FComp.Nan)
+ {
+ case FComp.Lt: inst = Instruction.CompareLess; break;
+ case FComp.Eq: inst = Instruction.CompareEqual; break;
+ case FComp.Le: inst = Instruction.CompareLessOrEqual; break;
+ case FComp.Gt: inst = Instruction.CompareGreater; break;
+ case FComp.Ne: inst = Instruction.CompareNotEqual; break;
+ case FComp.Ge: inst = Instruction.CompareGreaterOrEqual; break;
+
+ default: throw new ArgumentException($"Unexpected condition \"{cond}\".");
+ }
+
+ res = context.Add(inst | Instruction.FP32, Local(), srcA, srcB);
+
+ if ((cond & FComp.Nan) != 0)
+ {
+ res = context.BitwiseOr(res, context.IsNan(srcA));
+ res = context.BitwiseOr(res, context.IsNan(srcB));
+ }
+ }
+
+ return res;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs
new file mode 100644
index 00000000..3e91fc8a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFloatMinMax.cs
@@ -0,0 +1,70 @@
+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;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void FmnmxR(EmitterContext context)
+ {
+ InstFmnmxR op = context.GetOp<InstFmnmxR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitFmnmx(context, srcA, srcB, srcPred, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.WriteCC);
+ }
+
+ public static void FmnmxI(EmitterContext context)
+ {
+ InstFmnmxI op = context.GetOp<InstFmnmxI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20));
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitFmnmx(context, srcA, srcB, srcPred, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.WriteCC);
+ }
+
+ public static void FmnmxC(EmitterContext context)
+ {
+ InstFmnmxC op = context.GetOp<InstFmnmxC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitFmnmx(context, srcA, srcB, srcPred, op.Dest, op.AbsA, op.AbsB, op.NegA, op.NegB, op.WriteCC);
+ }
+
+ private static void EmitFmnmx(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ Operand srcPred,
+ int rd,
+ bool absoluteA,
+ bool absoluteB,
+ bool negateA,
+ bool negateB,
+ bool writeCC)
+ {
+ srcA = context.FPAbsNeg(srcA, absoluteA, negateA);
+ srcB = context.FPAbsNeg(srcB, absoluteB, negateB);
+
+ Operand resMin = context.FPMinimum(srcA, srcB);
+ Operand resMax = context.FPMaximum(srcA, srcB);
+
+ Operand dest = GetDest(rd);
+
+ context.Copy(dest, context.ConditionalSelect(srcPred, resMin, resMax));
+
+ SetFPZnFlags(context, dest, writeCC);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlow.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs
index 1f5bf35b..fce951ba 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitFlow.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlowControl.cs
@@ -13,32 +13,38 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
public static void Bra(EmitterContext context)
{
- EmitBranch(context, context.CurrBlock.Branch.Address);
+ InstBra op = context.GetOp<InstBra>();
+
+ EmitBranch(context, context.CurrBlock.Successors[^1].Address);
}
public static void Brk(EmitterContext context)
{
+ InstBrk op = context.GetOp<InstBrk>();
+
EmitBrkOrSync(context);
}
public static void Brx(EmitterContext context)
{
- OpCodeBranchIndir op = (OpCodeBranchIndir)context.CurrOp;
+ InstBrx op = context.GetOp<InstBrx>();
+ InstOp currOp = context.CurrOp;
+ int startIndex = context.CurrBlock.HasNext() ? 1 : 0;
- if (op.PossibleTargets.Count == 0)
+ if (context.CurrBlock.Successors.Count <= startIndex)
{
- context.Config.GpuAccessor.Log($"Failed to find targets for BRX instruction at 0x{op.Address:X}.");
+ context.Config.GpuAccessor.Log($"Failed to find targets for BRX instruction at 0x{currOp.Address:X}.");
return;
}
- int offset = (int)op.Address + 8 + op.Offset;
+ int offset = (int)currOp.GetAbsoluteAddress();
- Operand address = context.IAdd(Register(op.Ra), Const(offset));
+ Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
// Sorting the target addresses in descending order improves the code,
// since it will always check the most distant targets first, then the
// near ones. This can be easily transformed into if/else statements.
- IOrderedEnumerable<Block> sortedTargets = op.PossibleTargets.OrderByDescending(x => x.Address);
+ var sortedTargets = context.CurrBlock.Successors.Skip(startIndex).OrderByDescending(x => x.Address);
Block lastTarget = sortedTargets.LastOrDefault();
@@ -59,28 +65,24 @@ namespace Ryujinx.Graphics.Shader.Instructions
public static void Cal(EmitterContext context)
{
- OpCodeBranch op = (OpCodeBranch)context.CurrOp;
+ InstCal op = context.GetOp<InstCal>();
- context.Call(context.GetFunctionId(op.GetAbsoluteAddress()), false);
- }
-
- public static void Depbar(EmitterContext context)
- {
+ context.Call(context.GetFunctionId(context.CurrOp.GetAbsoluteAddress()), false);
}
public static void Exit(EmitterContext context)
{
+ InstExit op = context.GetOp<InstExit>();
+
if (context.IsNonMain)
{
context.Config.GpuAccessor.Log("Invalid exit on non-main function.");
return;
}
- OpCodeExit op = (OpCodeExit)context.CurrOp;
-
// TODO: Figure out how this is supposed to work in the
// presence of other condition codes.
- if (op.Condition == Condition.Always)
+ if (op.Ccc == Ccc.T)
{
context.Return();
}
@@ -88,20 +90,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
public static void Kil(EmitterContext context)
{
- context.Discard();
- }
+ InstKil op = context.GetOp<InstKil>();
- public static void Nop(EmitterContext context)
- {
+ context.Discard();
}
public static void Pbk(EmitterContext context)
{
+ InstPbk op = context.GetOp<InstPbk>();
+
EmitPbkOrSsy(context);
}
public static void Ret(EmitterContext context)
{
+ InstRet op = context.GetOp<InstRet>();
+
if (context.IsNonMain)
{
context.Return();
@@ -114,63 +118,62 @@ namespace Ryujinx.Graphics.Shader.Instructions
public static void Ssy(EmitterContext context)
{
+ InstSsy op = context.GetOp<InstSsy>();
+
EmitPbkOrSsy(context);
}
public static void Sync(EmitterContext context)
{
+ InstSync op = context.GetOp<InstSync>();
+
EmitBrkOrSync(context);
}
private static void EmitPbkOrSsy(EmitterContext context)
{
- OpCodePush op = (OpCodePush)context.CurrOp;
+ var consumers = context.CurrBlock.PushOpCodes.First(x => x.Op.Address == context.CurrOp.Address).Consumers;
- foreach (KeyValuePair<OpCodeBranchPop, Operand> kv in op.PopOps)
+ foreach (KeyValuePair<Block, Operand> kv in consumers)
{
- OpCodeBranchPop opSync = kv.Key;
-
+ Block consumerBlock = kv.Key;
Operand local = kv.Value;
- int pushOpIndex = opSync.Targets[op];
+ int id = consumerBlock.SyncTargets[context.CurrOp.Address].PushOpId;
- context.Copy(local, Const(pushOpIndex));
+ context.Copy(local, Const(id));
}
}
private static void EmitBrkOrSync(EmitterContext context)
{
- OpCodeBranchPop op = (OpCodeBranchPop)context.CurrOp;
+ var targets = context.CurrBlock.SyncTargets;
- if (op.Targets.Count == 1)
+ if (targets.Count == 1)
{
// If we have only one target, then the SSY/PBK is basically
// a branch, we can produce better codegen for this case.
- OpCodePush pushOp = op.Targets.Keys.First();
-
- EmitBranch(context, pushOp.GetAbsoluteAddress());
+ EmitBranch(context, targets.Values.First().PushOpInfo.Op.GetAbsoluteAddress());
}
else
{
// TODO: Support CC here aswell (condition).
- foreach (KeyValuePair<OpCodePush, int> kv in op.Targets)
+ foreach (SyncTarget target in targets.Values)
{
- OpCodePush pushOp = kv.Key;
-
- Operand label = context.GetLabel(pushOp.GetAbsoluteAddress());
-
- Operand local = pushOp.PopOps[op];
+ PushOpInfo pushOpInfo = target.PushOpInfo;
- int pushOpIndex = kv.Value;
+ Operand label = context.GetLabel(pushOpInfo.Op.GetAbsoluteAddress());
+ Operand local = pushOpInfo.Consumers[context.CurrBlock];
- context.BranchIfTrue(label, context.ICompareEqual(local, Const(pushOpIndex)));
+ context.BranchIfTrue(label, context.ICompareEqual(local, Const(target.PushOpId)));
}
}
}
private static void EmitBranch(EmitterContext context, ulong address)
{
- OpCode op = context.CurrOp;
+ InstOp op = context.CurrOp;
+ InstConditional opCond = new InstConditional(op.RawOpCode);
// If we're branching to the next instruction, then the branch
// is useless and we can ignore it.
@@ -181,17 +184,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand label = context.GetLabel(address);
- Operand pred = Register(op.Predicate);
+ Operand pred = Register(opCond.Pred, RegisterType.Predicate);
- if (op is OpCodeConditional opCond && opCond.Condition != Condition.Always)
+ if (opCond.Ccc != Ccc.T)
{
- Operand cond = GetCondition(context, opCond.Condition);
+ Operand cond = GetCondition(context, opCond.Ccc);
- if (op.Predicate.IsPT)
+ if (opCond.Pred == RegisterConsts.PredicateTrueIndex)
{
pred = cond;
}
- else if (op.InvertPredicate)
+ else if (opCond.PredInv)
{
pred = context.BitwiseAnd(context.BitwiseNot(pred), cond);
}
@@ -202,11 +205,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.BranchIfTrue(label, pred);
}
- else if (op.Predicate.IsPT)
+ else if (opCond.Pred == RegisterConsts.PredicateTrueIndex)
{
context.Branch(label);
}
- else if (op.InvertPredicate)
+ else if (opCond.PredInv)
{
context.BranchIfFalse(label, pred);
}
@@ -216,16 +219,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- private static Operand GetCondition(EmitterContext context, Condition cond)
+ private static Operand GetCondition(EmitterContext context, Ccc cond)
{
// TODO: More condition codes, figure out how they work.
switch (cond)
{
- case Condition.Equal:
- case Condition.EqualUnordered:
+ case Ccc.Eq:
+ case Ccc.Equ:
return GetZF();
- case Condition.NotEqual:
- case Condition.NotEqualUnordered:
+ case Ccc.Ne:
+ case Ccc.Neu:
return context.BitwiseNot(GetZF());
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs
index 5a351c73..9f5bbdf7 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using System;
+using System.Runtime.CompilerServices;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -29,184 +30,101 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Register(3, RegisterType.Flag);
}
- public static Operand GetDest(EmitterContext context)
+ public static Operand GetDest(int rd)
{
- return Register(((IOpCodeRd)context.CurrOp).Rd);
+ return Register(rd, RegisterType.Gpr);
}
- public static Operand GetDest2(EmitterContext context)
+ public static Operand GetDest2(int rd)
{
- Register rd = ((IOpCodeRd)context.CurrOp).Rd;
-
- return Register(rd.Index | 1, rd.Type);
+ return Register(rd | 1, RegisterType.Gpr);
}
- public static Operand GetSrcA(EmitterContext context, bool isFP64 = false)
+ public static Operand GetSrcCbuf(EmitterContext context, int cbufSlot, int cbufOffset, bool isFP64 = false)
{
- IOpCodeRa op = (IOpCodeRa)context.CurrOp;
-
if (isFP64)
{
- return context.PackDouble2x32(Register(op.Ra.Index, op.Ra.Type), Register(op.Ra.Index | 1, op.Ra.Type));
+ return context.PackDouble2x32(
+ context.Config.CreateCbuf(cbufSlot, cbufOffset),
+ context.Config.CreateCbuf(cbufSlot, cbufOffset + 1));
}
else
{
- return Register(op.Ra);
+ return context.Config.CreateCbuf(cbufSlot, cbufOffset);
}
}
- public static Operand GetSrcB(EmitterContext context, FPType floatType)
+ public static Operand GetSrcImm(EmitterContext context, int imm, bool isFP64 = false)
{
- if (floatType == FPType.FP32)
+ if (isFP64)
{
- return GetSrcB(context);
+ return context.FP32ConvertToFP64(Const(imm));
}
- else if (floatType == FPType.FP16)
- {
- int h = context.CurrOp.RawOpCode.Extract(41, 1);
-
- return GetHalfUnpacked(context, GetSrcB(context), FPHalfSwizzle.FP16)[h];
- }
- else if (floatType == FPType.FP64)
+ else
{
- return GetSrcB(context, true);
+ return Const(imm);
}
-
- throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
}
- public static Operand GetSrcB(EmitterContext context, bool isFP64 = false)
+ public static Operand GetSrcReg(EmitterContext context, int reg, bool isFP64 = false)
{
if (isFP64)
{
- switch (context.CurrOp)
- {
- case IOpCodeCbuf op:
- return context.PackDouble2x32(
- context.Config.CreateCbuf(op.Slot, op.Offset),
- context.Config.CreateCbuf(op.Slot, op.Offset + 1));
-
- case IOpCodeImmF op:
- return context.FP32ConvertToFP64(ConstF(op.Immediate));
-
- case IOpCodeReg op:
- return context.PackDouble2x32(Register(op.Rb.Index, op.Rb.Type), Register(op.Rb.Index | 1, op.Rb.Type));
-
- case IOpCodeRegCbuf op:
- return context.PackDouble2x32(Register(op.Rc.Index, op.Rc.Type), Register(op.Rc.Index | 1, op.Rc.Type));
- }
+ return context.PackDouble2x32(Register(reg, RegisterType.Gpr), Register(reg | 1, RegisterType.Gpr));
}
else
{
- switch (context.CurrOp)
- {
- case IOpCodeCbuf op:
- return context.Config.CreateCbuf(op.Slot, op.Offset);
-
- case IOpCodeImm op:
- return Const(op.Immediate);
-
- case IOpCodeImmF op:
- return ConstF(op.Immediate);
-
- case IOpCodeReg op:
- return Register(op.Rb);
-
- case IOpCodeRegCbuf op:
- return Register(op.Rc);
- }
+ return Register(reg, RegisterType.Gpr);
}
-
- throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\".");
}
- public static Operand GetSrcC(EmitterContext context, bool isFP64 = false)
+ public static Operand[] GetHalfSrc(
+ EmitterContext context,
+ HalfSwizzle swizzle,
+ int ra,
+ bool negate,
+ bool absolute)
{
- if (isFP64)
- {
- switch (context.CurrOp)
- {
- case IOpCodeRegCbuf op:
- return context.PackDouble2x32(
- context.Config.CreateCbuf(op.Slot, op.Offset),
- context.Config.CreateCbuf(op.Slot, op.Offset + 1));
+ Operand[] operands = GetHalfUnpacked(context, GetSrcReg(context, ra), swizzle);
- case IOpCodeRc op:
- return context.PackDouble2x32(Register(op.Rc.Index, op.Rc.Type), Register(op.Rc.Index | 1, op.Rc.Type));
- }
- }
- else
- {
- switch (context.CurrOp)
- {
- case IOpCodeRegCbuf op:
- return context.Config.CreateCbuf(op.Slot, op.Offset);
+ return FPAbsNeg(context, operands, absolute, negate);
+ }
- case IOpCodeRc op:
- return Register(op.Rc);
- }
- }
+ public static Operand[] GetHalfSrc(
+ EmitterContext context,
+ HalfSwizzle swizzle,
+ int cbufSlot,
+ int cbufOffset,
+ bool negate,
+ bool absolute)
+ {
+ Operand[] operands = GetHalfUnpacked(context, GetSrcCbuf(context, cbufSlot, cbufOffset), swizzle);
- throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\".");
+ return FPAbsNeg(context, operands, absolute, negate);
}
- public static Operand[] GetHalfSrcA(EmitterContext context, bool isAdd = false)
+ public static Operand[] GetHalfSrc(EmitterContext context, int immH0, int immH1)
{
- OpCode op = context.CurrOp;
+ ushort low = (ushort)(immH0 << 6);
+ ushort high = (ushort)(immH1 << 6);
- bool absoluteA = false, negateA = false;
-
- if (op is OpCodeAluImm32 && isAdd)
- {
- negateA = op.RawOpCode.Extract(56);
- }
- else if (isAdd || op is IOpCodeCbuf || op is IOpCodeImm)
+ return new Operand[]
{
- negateA = op.RawOpCode.Extract(43);
- absoluteA = op.RawOpCode.Extract(44);
- }
- else if (op is IOpCodeReg)
- {
- absoluteA = op.RawOpCode.Extract(44);
- }
-
- FPHalfSwizzle swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(47, 2);
-
- Operand[] operands = GetHalfUnpacked(context, GetSrcA(context), swizzle);
-
- return FPAbsNeg(context, operands, absoluteA, negateA);
+ ConstF((float)Unsafe.As<ushort, Half>(ref low)),
+ ConstF((float)Unsafe.As<ushort, Half>(ref high))
+ };
}
- public static Operand[] GetHalfSrcB(EmitterContext context, bool isMul = false)
+ public static Operand[] GetHalfSrc(EmitterContext context, int imm32)
{
- OpCode op = context.CurrOp;
-
- FPHalfSwizzle swizzle = FPHalfSwizzle.FP16;
-
- bool absoluteB = false, negateB = false;
-
- if (op is IOpCodeReg)
- {
- swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(28, 2);
+ ushort low = (ushort)imm32;
+ ushort high = (ushort)(imm32 >> 16);
- absoluteB = op.RawOpCode.Extract(30);
- negateB = op.RawOpCode.Extract(31);
- }
- else if (op is IOpCodeCbuf)
+ return new Operand[]
{
- swizzle = FPHalfSwizzle.FP32;
-
- absoluteB = op.RawOpCode.Extract(54);
-
- if (!isMul)
- {
- negateB = op.RawOpCode.Extract(56);
- }
- }
-
- Operand[] operands = GetHalfUnpacked(context, GetSrcB(context), swizzle);
-
- return FPAbsNeg(context, operands, absoluteB, negateB);
+ ConstF((float)Unsafe.As<ushort, Half>(ref low)),
+ ConstF((float)Unsafe.As<ushort, Half>(ref high))
+ };
}
public static Operand[] FPAbsNeg(EmitterContext context, Operand[] operands, bool abs, bool neg)
@@ -219,27 +137,27 @@ namespace Ryujinx.Graphics.Shader.Instructions
return operands;
}
- public static Operand[] GetHalfUnpacked(EmitterContext context, Operand src, FPHalfSwizzle swizzle)
+ public static Operand[] GetHalfUnpacked(EmitterContext context, Operand src, HalfSwizzle swizzle)
{
switch (swizzle)
{
- case FPHalfSwizzle.FP16:
+ case HalfSwizzle.F16:
return new Operand[]
{
context.UnpackHalf2x16Low (src),
context.UnpackHalf2x16High(src)
};
- case FPHalfSwizzle.FP32: return new Operand[] { src, src };
+ case HalfSwizzle.F32: return new Operand[] { src, src };
- case FPHalfSwizzle.DupH0:
+ case HalfSwizzle.H0H0:
return new Operand[]
{
context.UnpackHalf2x16Low(src),
context.UnpackHalf2x16Low(src)
};
- case FPHalfSwizzle.DupH1:
+ case HalfSwizzle.H1H1:
return new Operand[]
{
context.UnpackHalf2x16High(src),
@@ -250,33 +168,24 @@ namespace Ryujinx.Graphics.Shader.Instructions
throw new ArgumentException($"Invalid swizzle \"{swizzle}\".");
}
- public static Operand GetHalfPacked(EmitterContext context, Operand[] results)
+ public static Operand GetHalfPacked(EmitterContext context, OFmt swizzle, Operand[] results, int rd)
{
- OpCode op = context.CurrOp;
-
- FPHalfSwizzle swizzle = FPHalfSwizzle.FP16;
-
- if (!(op is OpCodeAluImm32))
- {
- swizzle = (FPHalfSwizzle)context.CurrOp.RawOpCode.Extract(49, 2);
- }
-
switch (swizzle)
{
- case FPHalfSwizzle.FP16: return context.PackHalf2x16(results[0], results[1]);
+ case OFmt.F16: return context.PackHalf2x16(results[0], results[1]);
- case FPHalfSwizzle.FP32: return results[0];
+ case OFmt.F32: return results[0];
- case FPHalfSwizzle.DupH0:
+ case OFmt.MrgH0:
{
- Operand h1 = GetHalfDest(context, isHigh: true);
+ Operand h1 = GetHalfDest(context, rd, isHigh: true);
return context.PackHalf2x16(results[0], h1);
}
- case FPHalfSwizzle.DupH1:
+ case OFmt.MrgH1:
{
- Operand h0 = GetHalfDest(context, isHigh: false);
+ Operand h0 = GetHalfDest(context, rd, isHigh: false);
return context.PackHalf2x16(h0, results[1]);
}
@@ -285,25 +194,23 @@ namespace Ryujinx.Graphics.Shader.Instructions
throw new ArgumentException($"Invalid swizzle \"{swizzle}\".");
}
- public static Operand GetHalfDest(EmitterContext context, bool isHigh)
+ public static Operand GetHalfDest(EmitterContext context, int rd, bool isHigh)
{
if (isHigh)
{
- return context.UnpackHalf2x16High(GetDest(context));
+ return context.UnpackHalf2x16High(GetDest(rd));
}
else
{
- return context.UnpackHalf2x16Low(GetDest(context));
+ return context.UnpackHalf2x16Low(GetDest(rd));
}
}
- public static Operand GetPredicate39(EmitterContext context)
+ public static Operand GetPredicate(EmitterContext context, int pred, bool not)
{
- IOpCodePredicate39 op = (IOpCodePredicate39)context.CurrOp;
-
- Operand local = Register(op.Predicate39);
+ Operand local = Register(pred, RegisterType.Predicate);
- if (op.InvertP)
+ if (not)
{
local = context.BitwiseNot(local);
}
@@ -311,6 +218,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
return local;
}
+ public static int Imm16ToSInt(int imm16)
+ {
+ return (short)imm16;
+ }
+
+ public static int Imm20ToFloat(int imm20)
+ {
+ return imm20 << 12;
+ }
+
+ public static int Imm20ToSInt(int imm20)
+ {
+ return (imm20 << 12) >> 12;
+ }
+
+ public static int Imm24ToSInt(int imm24)
+ {
+ return (imm24 << 8) >> 8;
+ }
+
public static Operand SignExtendTo32(EmitterContext context, Operand src, int srcBits)
{
return context.BitfieldExtractS32(src, Const(0), Const(srcBits));
@@ -318,7 +245,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
public static Operand ZeroExtendTo32(EmitterContext context, Operand src, int srcBits)
{
- int mask = (int)(0xffffffffu >> (32 - srcBits));
+ int mask = (int)(uint.MaxValue >> (32 - srcBits));
return context.BitwiseAnd(src, Const(mask));
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs
new file mode 100644
index 00000000..ac8cca1b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerArithmetic.cs
@@ -0,0 +1,656 @@
+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 IaddR(EmitterContext context)
+ {
+ InstIaddR op = context.GetOp<InstIaddR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitIadd(context, srcA, srcB, op.Dest, op.AvgMode, op.X, op.WriteCC);
+ }
+
+ public static void IaddI(EmitterContext context)
+ {
+ InstIaddI op = context.GetOp<InstIaddI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitIadd(context, srcA, srcB, op.Dest, op.AvgMode, op.X, op.WriteCC);
+ }
+
+ public static void IaddC(EmitterContext context)
+ {
+ InstIaddC op = context.GetOp<InstIaddC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitIadd(context, srcA, srcB, op.Dest, op.AvgMode, op.X, op.WriteCC);
+ }
+
+ public static void Iadd32i(EmitterContext context)
+ {
+ InstIadd32i op = context.GetOp<InstIadd32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+
+ EmitIadd(context, srcA, srcB, op.Dest, op.AvgMode, op.X, op.WriteCC);
+ }
+
+ public static void Iadd3R(EmitterContext context)
+ {
+ InstIadd3R op = context.GetOp<InstIadd3R>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIadd3(context, op.Lrs, srcA, srcB, srcC, op.Apart, op.Bpart, op.Cpart, op.Dest, op.NegA, op.NegB, op.NegC);
+ }
+
+ public static void Iadd3I(EmitterContext context)
+ {
+ InstIadd3I op = context.GetOp<InstIadd3I>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIadd3(context, Lrs.None, srcA, srcB, srcC, HalfSelect.B32, HalfSelect.B32, HalfSelect.B32, op.Dest, op.NegA, op.NegB, op.NegC);
+ }
+
+ public static void Iadd3C(EmitterContext context)
+ {
+ InstIadd3C op = context.GetOp<InstIadd3C>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIadd3(context, Lrs.None, srcA, srcB, srcC, HalfSelect.B32, HalfSelect.B32, HalfSelect.B32, op.Dest, op.NegA, op.NegB, op.NegC);
+ }
+
+ public static void ImadR(EmitterContext context)
+ {
+ InstImadR op = context.GetOp<InstImadR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitImad(context, srcA, srcB, srcC, op.Dest, op.AvgMode, op.ASigned, op.BSigned, op.Hilo);
+ }
+
+ public static void ImadI(EmitterContext context)
+ {
+ InstImadI op = context.GetOp<InstImadI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitImad(context, srcA, srcB, srcC, op.Dest, op.AvgMode, op.ASigned, op.BSigned, op.Hilo);
+ }
+
+ public static void ImadC(EmitterContext context)
+ {
+ InstImadC op = context.GetOp<InstImadC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitImad(context, srcA, srcB, srcC, op.Dest, op.AvgMode, op.ASigned, op.BSigned, op.Hilo);
+ }
+
+ public static void ImadRc(EmitterContext context)
+ {
+ InstImadRc op = context.GetOp<InstImadRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitImad(context, srcA, srcB, srcC, op.Dest, op.AvgMode, op.ASigned, op.BSigned, op.Hilo);
+ }
+
+ public static void Imad32i(EmitterContext context)
+ {
+ InstImad32i op = context.GetOp<InstImad32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+ var srcC = GetSrcReg(context, op.Dest);
+
+ EmitImad(context, srcA, srcB, srcC, op.Dest, op.AvgMode, op.ASigned, op.BSigned, op.Hilo);
+ }
+
+ public static void IscaddR(EmitterContext context)
+ {
+ InstIscaddR op = context.GetOp<InstIscaddR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitIscadd(context, srcA, srcB, op.Dest, op.Imm5, op.AvgMode, op.WriteCC);
+ }
+
+ public static void IscaddI(EmitterContext context)
+ {
+ InstIscaddI op = context.GetOp<InstIscaddI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitIscadd(context, srcA, srcB, op.Dest, op.Imm5, op.AvgMode, op.WriteCC);
+ }
+
+ public static void IscaddC(EmitterContext context)
+ {
+ InstIscaddC op = context.GetOp<InstIscaddC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitIscadd(context, srcA, srcB, op.Dest, op.Imm5, op.AvgMode, op.WriteCC);
+ }
+
+ public static void Iscadd32i(EmitterContext context)
+ {
+ InstIscadd32i op = context.GetOp<InstIscadd32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+
+ EmitIscadd(context, srcA, srcB, op.Dest, op.Imm5, AvgMode.NoNeg, op.WriteCC);
+ }
+
+ public static void LeaR(EmitterContext context)
+ {
+ InstLeaR op = context.GetOp<InstLeaR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitLea(context, srcA, srcB, op.Dest, op.NegA, op.ImmU5);
+ }
+
+ public static void LeaI(EmitterContext context)
+ {
+ InstLeaI op = context.GetOp<InstLeaI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitLea(context, srcA, srcB, op.Dest, op.NegA, op.ImmU5);
+ }
+
+ public static void LeaC(EmitterContext context)
+ {
+ InstLeaC op = context.GetOp<InstLeaC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitLea(context, srcA, srcB, op.Dest, op.NegA, op.ImmU5);
+ }
+
+ public static void LeaHiR(EmitterContext context)
+ {
+ InstLeaHiR op = context.GetOp<InstLeaHiR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitLeaHi(context, srcA, srcB, srcC, op.Dest, op.NegA, op.ImmU5);
+ }
+
+ public static void LeaHiC(EmitterContext context)
+ {
+ InstLeaHiC op = context.GetOp<InstLeaHiC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitLeaHi(context, srcA, srcB, srcC, op.Dest, op.NegA, op.ImmU5);
+ }
+
+ public static void XmadR(EmitterContext context)
+ {
+ InstXmadR op = context.GetOp<InstXmadR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitXmad(context, op.XmadCop, srcA, srcB, srcC, op.Dest, op.ASigned, op.BSigned, op.HiloA, op.HiloB, op.Psl, op.Mrg, op.X, op.WriteCC);
+ }
+
+ public static void XmadI(EmitterContext context)
+ {
+ InstXmadI op = context.GetOp<InstXmadI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm16);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitXmad(context, op.XmadCop, srcA, srcB, srcC, op.Dest, op.ASigned, op.BSigned, op.HiloA, false, op.Psl, op.Mrg, op.X, op.WriteCC);
+ }
+
+ public static void XmadC(EmitterContext context)
+ {
+ InstXmadC op = context.GetOp<InstXmadC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitXmad(context, op.XmadCop, srcA, srcB, srcC, op.Dest, op.ASigned, op.BSigned, op.HiloA, op.HiloB, op.Psl, op.Mrg, op.X, op.WriteCC);
+ }
+
+ public static void XmadRc(EmitterContext context)
+ {
+ InstXmadRc op = context.GetOp<InstXmadRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitXmad(context, op.XmadCop, srcA, srcB, srcC, op.Dest, op.ASigned, op.BSigned, op.HiloA, op.HiloB, false, false, op.X, op.WriteCC);
+ }
+
+ private static void EmitIadd(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ AvgMode avgMode,
+ bool extended,
+ bool writeCC)
+ {
+ srcA = context.INegate(srcA, avgMode == AvgMode.NegA);
+ srcB = context.INegate(srcB, avgMode == AvgMode.NegB);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ if (extended)
+ {
+ res = context.IAdd(res, context.BitwiseAnd(GetCF(), Const(1)));
+ }
+
+ SetIaddFlags(context, res, srcA, srcB, writeCC, extended);
+
+ // TODO: SAT.
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitIadd3(
+ EmitterContext context,
+ Lrs mode,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ HalfSelect partA,
+ HalfSelect partB,
+ HalfSelect partC,
+ int rd,
+ bool negateA,
+ bool negateB,
+ bool negateC)
+ {
+ Operand Extend(Operand src, HalfSelect part)
+ {
+ if (part == HalfSelect.B32)
+ {
+ return src;
+ }
+
+ if (part == HalfSelect.H0)
+ {
+ return context.BitwiseAnd(src, Const(0xffff));
+ }
+ else if (part == HalfSelect.H1)
+ {
+ return context.ShiftRightU32(src, Const(16));
+ }
+ else
+ {
+ context.Config.GpuAccessor.Log($"Iadd3 has invalid component selection {part}.");
+ }
+
+ return src;
+ }
+
+ srcA = context.INegate(Extend(srcA, partA), negateA);
+ srcB = context.INegate(Extend(srcB, partB), negateB);
+ srcC = context.INegate(Extend(srcC, partC), negateC);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ if (mode != Lrs.None)
+ {
+ if (mode == Lrs.LeftShift)
+ {
+ res = context.ShiftLeft(res, Const(16));
+ }
+ else if (mode == Lrs.RightShift)
+ {
+ res = context.ShiftRightU32(res, Const(16));
+ }
+ else
+ {
+ // TODO: Warning.
+ }
+ }
+
+ res = context.IAdd(res, srcC);
+
+ context.Copy(GetDest(rd), res);
+
+ // TODO: CC, X, corner cases.
+ }
+
+ public static void EmitImad(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ AvgMode avgMode,
+ bool signedA,
+ bool signedB,
+ bool high)
+ {
+ srcB = context.INegate(srcB, avgMode == AvgMode.NegA);
+ srcC = context.INegate(srcC, avgMode == AvgMode.NegB);
+
+ Operand res;
+
+ if (high)
+ {
+ if (signedA && signedB)
+ {
+ res = context.MultiplyHighS32(srcA, srcB);
+ }
+ else
+ {
+ res = context.MultiplyHighU32(srcA, srcB);
+
+ if (signedA)
+ {
+ res = context.IAdd(res, context.IMultiply(srcB, context.ShiftRightS32(srcA, Const(31))));
+ }
+ else if (signedB)
+ {
+ res = context.IAdd(res, context.IMultiply(srcA, context.ShiftRightS32(srcB, Const(31))));
+ }
+ }
+ }
+ else
+ {
+ res = context.IMultiply(srcA, srcB);
+ }
+
+ res = context.IAdd(res, srcC);
+
+ // TODO: CC, X, SAT, and more?
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitIscadd(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ int shift,
+ AvgMode avgMode,
+ bool writeCC)
+ {
+ srcA = context.ShiftLeft(srcA, Const(shift));
+
+ srcA = context.INegate(srcA, avgMode == AvgMode.NegA);
+ srcB = context.INegate(srcB, avgMode == AvgMode.NegB);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ SetIaddFlags(context, res, srcA, srcB, writeCC, false);
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ public static void EmitLea(EmitterContext context, Operand srcA, Operand srcB, int rd, bool negateA, int shift)
+ {
+ srcA = context.ShiftLeft(srcA, Const(shift));
+ srcA = context.INegate(srcA, negateA);
+
+ Operand res = context.IAdd(srcA, srcB);
+
+ context.Copy(GetDest(rd), res);
+
+ // TODO: CC, X.
+ }
+
+ private static void EmitLeaHi(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ bool negateA,
+ int shift)
+ {
+ Operand aLow = context.ShiftLeft(srcA, Const(shift));
+ Operand aHigh = shift == 0 ? Const(0) : context.ShiftRightU32(srcA, Const(32 - shift));
+ aHigh = context.BitwiseOr(aHigh, context.ShiftLeft(srcC, Const(shift)));
+
+ if (negateA)
+ {
+ // Perform 64-bit negation by doing bitwise not of the value,
+ // then adding 1 and carrying over from low to high.
+ aLow = context.BitwiseNot(aLow);
+ aHigh = context.BitwiseNot(aHigh);
+
+ aLow = AddWithCarry(context, aLow, Const(1), out Operand aLowCOut);
+ aHigh = context.IAdd(aHigh, aLowCOut);
+ }
+
+ Operand res = context.IAdd(aHigh, srcB);
+
+ context.Copy(GetDest(rd), res);
+
+ // TODO: CC, X.
+ }
+
+ public static void EmitXmad(
+ EmitterContext context,
+ XmadCop2 mode,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ bool signedA,
+ bool signedB,
+ bool highA,
+ bool highB,
+ bool productShiftLeft,
+ bool merge,
+ bool extended,
+ bool writeCC)
+ {
+ XmadCop modeConv;
+ switch (mode)
+ {
+ case XmadCop2.Cfull:
+ modeConv = XmadCop.Cfull;
+ break;
+ case XmadCop2.Clo:
+ modeConv = XmadCop.Clo;
+ break;
+ case XmadCop2.Chi:
+ modeConv = XmadCop.Chi;
+ break;
+ case XmadCop2.Csfu:
+ modeConv = XmadCop.Csfu;
+ break;
+ default:
+ context.Config.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\".");
+ return;
+ }
+
+ EmitXmad(context, modeConv, srcA, srcB, srcC, rd, signedA, signedB, highA, highB, productShiftLeft, merge, extended, writeCC);
+ }
+
+ public static void EmitXmad(
+ EmitterContext context,
+ XmadCop mode,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ bool signedA,
+ bool signedB,
+ bool highA,
+ bool highB,
+ bool productShiftLeft,
+ bool merge,
+ bool extended,
+ bool writeCC)
+ {
+ var srcBUnmodified = srcB;
+
+ Operand Extend16To32(Operand src, bool high, bool signed)
+ {
+ if (signed && high)
+ {
+ return context.ShiftRightS32(src, Const(16));
+ }
+ else if (signed)
+ {
+ return context.BitfieldExtractS32(src, Const(0), Const(16));
+ }
+ else if (high)
+ {
+ return context.ShiftRightU32(src, Const(16));
+ }
+ else
+ {
+ return context.BitwiseAnd(src, Const(0xffff));
+ }
+ }
+
+ srcA = Extend16To32(srcA, highA, signedA);
+ srcB = Extend16To32(srcB, highB, signedB);
+
+ Operand res = context.IMultiply(srcA, srcB);
+
+ if (productShiftLeft)
+ {
+ res = context.ShiftLeft(res, Const(16));
+ }
+
+ switch (mode)
+ {
+ case XmadCop.Cfull:
+ break;
+
+ case XmadCop.Clo:
+ srcC = Extend16To32(srcC, high: false, signed: false);
+ break;
+ case XmadCop.Chi:
+ srcC = Extend16To32(srcC, high: true, signed: false);
+ break;
+
+ case XmadCop.Cbcc:
+ srcC = context.IAdd(srcC, context.ShiftLeft(srcBUnmodified, Const(16)));
+ break;
+
+ case XmadCop.Csfu:
+ Operand signAdjustA = context.ShiftLeft(context.ShiftRightU32(srcA, Const(31)), Const(16));
+ Operand signAdjustB = context.ShiftLeft(context.ShiftRightU32(srcB, Const(31)), Const(16));
+
+ srcC = context.ISubtract(srcC, context.IAdd(signAdjustA, signAdjustB));
+ break;
+
+ default:
+ context.Config.GpuAccessor.Log($"Invalid XMAD mode \"{mode}\".");
+ return;
+ }
+
+ Operand product = res;
+
+ if (extended)
+ {
+ // Add with carry.
+ res = context.IAdd(res, context.BitwiseAnd(GetCF(), Const(1)));
+ }
+ else
+ {
+ // Add (no carry in).
+ res = context.IAdd(res, srcC);
+ }
+
+ SetIaddFlags(context, res, product, srcC, writeCC, extended);
+
+ if (merge)
+ {
+ res = context.BitwiseAnd(res, Const(0xffff));
+ res = context.BitwiseOr(res, context.ShiftLeft(srcBUnmodified, Const(16)));
+ }
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void SetIaddFlags(EmitterContext context, Operand res, Operand srcA, Operand srcB, bool setCC, bool extended)
+ {
+ if (!setCC)
+ {
+ return;
+ }
+
+ if (extended)
+ {
+ // C = (d == a && CIn) || d < a
+ Operand tempC0 = context.ICompareEqual(res, srcA);
+ Operand tempC1 = context.ICompareLessUnsigned(res, srcA);
+
+ tempC0 = context.BitwiseAnd(tempC0, GetCF());
+
+ 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);
+ Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB);
+
+ tempV1 = context.BitwiseNot(tempV1);
+
+ Operand tempV = context.BitwiseAnd(tempV0, tempV1);
+
+ context.Copy(GetVF(), context.ICompareLess(tempV, Const(0)));
+
+ SetZnFlags(context, res, setCC: true, extended: extended);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs
new file mode 100644
index 00000000..ddd90f8e
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerComparison.cs
@@ -0,0 +1,327 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System;
+
+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 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>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
+ }
+
+ public static void IcmpI(EmitterContext context)
+ {
+ InstIcmpI op = context.GetOp<InstIcmpI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
+ }
+
+ public static void IcmpC(EmitterContext context)
+ {
+ InstIcmpC op = context.GetOp<InstIcmpC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
+ }
+
+ public static void IcmpRc(EmitterContext context)
+ {
+ InstIcmpRc op = context.GetOp<InstIcmpRc>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcC);
+ var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitIcmp(context, op.IComp, srcA, srcB, srcC, op.Dest, op.Signed);
+ }
+
+ public static void IsetR(EmitterContext context)
+ {
+ InstIsetR op = context.GetOp<InstIsetR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
+ }
+
+ public static void IsetI(EmitterContext context)
+ {
+ InstIsetI op = context.GetOp<InstIsetI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
+ }
+
+ public static void IsetC(EmitterContext context)
+ {
+ InstIsetC op = context.GetOp<InstIsetC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitIset(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.Dest, op.BVal, op.Signed, op.X, op.WriteCC);
+ }
+
+ public static void IsetpR(EmitterContext context)
+ {
+ InstIsetpR op = context.GetOp<InstIsetpR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
+ }
+
+ public static void IsetpI(EmitterContext context)
+ {
+ InstIsetpI op = context.GetOp<InstIsetpI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
+ }
+
+ public static void IsetpC(EmitterContext context)
+ {
+ InstIsetpC op = context.GetOp<InstIsetpC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitIsetp(context, op.IComp, op.Bop, srcA, srcB, op.SrcPred, op.SrcPredInv, op.DestPred, op.DestPredInv, op.Signed, op.X);
+ }
+
+ private static void EmitIcmp(
+ EmitterContext context,
+ IComp cmpOp,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ bool isSigned)
+ {
+ Operand cmpRes = GetIntComparison(context, cmpOp, srcC, Const(0), isSigned);
+
+ Operand res = context.ConditionalSelect(cmpRes, srcA, srcB);
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitIset(
+ EmitterContext context,
+ IComp cmpOp,
+ BoolOp logicOp,
+ Operand srcA,
+ Operand srcB,
+ int srcPred,
+ bool srcPredInv,
+ int rd,
+ bool boolFloat,
+ bool isSigned,
+ bool extended,
+ bool writeCC)
+ {
+ Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ res = GetPredLogicalOp(context, logicOp, res, pred);
+
+ Operand dest = GetDest(rd);
+
+ if (boolFloat)
+ {
+ res = context.ConditionalSelect(res, ConstF(1), Const(0));
+
+ context.Copy(dest, res);
+
+ SetFPZnFlags(context, res, writeCC);
+ }
+ else
+ {
+ context.Copy(dest, res);
+
+ SetZnFlags(context, res, writeCC, extended);
+ }
+ }
+
+ private static void EmitIsetp(
+ EmitterContext context,
+ IComp cmpOp,
+ BoolOp logicOp,
+ Operand srcA,
+ Operand srcB,
+ int srcPred,
+ bool srcPredInv,
+ int destPred,
+ int destPredInv,
+ bool isSigned,
+ bool extended)
+ {
+ Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned, extended);
+ Operand p1Res = context.BitwiseNot(p0Res);
+ Operand pred = GetPredicate(context, srcPred, srcPredInv);
+
+ p0Res = GetPredLogicalOp(context, logicOp, p0Res, pred);
+ p1Res = GetPredLogicalOp(context, logicOp, p1Res, pred);
+
+ context.Copy(Register(destPred, RegisterType.Predicate), p0Res);
+ context.Copy(Register(destPredInv, RegisterType.Predicate), p1Res);
+ }
+
+ private static Operand GetIntComparison(
+ EmitterContext context,
+ IComp 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, IComp cond, Operand srcA, Operand srcB, bool isSigned)
+ {
+ Operand res;
+
+ if (cond == IComp.T)
+ {
+ res = Const(IrConsts.True);
+ }
+ else if (cond == IComp.F)
+ {
+ res = Const(IrConsts.False);
+ }
+ else
+ {
+ res = context.ISubtract(srcA, srcB);
+ res = context.IAdd(res, context.BitwiseNot(GetCF()));
+
+ switch (cond)
+ {
+ case IComp.Eq: // r = xh == yh && xl == yl
+ res = context.BitwiseAnd(context.ICompareEqual(srcA, srcB), GetZF());
+ break;
+ case IComp.Lt: // 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 IComp.Le: // 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 IComp.Gt: // 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 IComp.Ge: // 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 IComp.Ne: // r = xh != yh || xl != yl
+ res = context.BitwiseOr(context.ICompareNotEqual(srcA, srcB), context.BitwiseNot(GetZF()));
+ break;
+ default:
+ throw new ArgumentException($"Unexpected condition \"{cond}\".");
+ }
+ }
+
+ return res;
+ }
+
+ private static Operand GetIntComparison(EmitterContext context, IComp cond, Operand srcA, Operand srcB, bool isSigned)
+ {
+ Operand res;
+
+ if (cond == IComp.T)
+ {
+ res = Const(IrConsts.True);
+ }
+ else if (cond == IComp.F)
+ {
+ res = Const(IrConsts.False);
+ }
+ else
+ {
+ var inst = cond switch
+ {
+ IComp.Lt => Instruction.CompareLessU32,
+ IComp.Eq => Instruction.CompareEqual,
+ IComp.Le => Instruction.CompareLessOrEqualU32,
+ IComp.Gt => Instruction.CompareGreaterU32,
+ IComp.Ne => Instruction.CompareNotEqual,
+ IComp.Ge => Instruction.CompareGreaterOrEqualU32,
+ _ => throw new InvalidOperationException($"Unexpected condition \"{cond}\".")
+ };
+
+ if (isSigned)
+ {
+ switch (cond)
+ {
+ case IComp.Lt: inst = Instruction.CompareLess; break;
+ case IComp.Le: inst = Instruction.CompareLessOrEqual; break;
+ case IComp.Gt: inst = Instruction.CompareGreater; break;
+ case IComp.Ge: inst = Instruction.CompareGreaterOrEqual; break;
+ }
+ }
+
+ res = context.Add(inst, Local(), srcA, srcB);
+ }
+
+ return res;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs
new file mode 100644
index 00000000..1f3f66ae
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerLogical.cs
@@ -0,0 +1,167 @@
+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
+ {
+ private const int PT = RegisterConsts.PredicateTrueIndex;
+
+ public static void LopR(EmitterContext context)
+ {
+ InstLopR op = context.GetOp<InstLopR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitLop(context, op.Lop, op.PredicateOp, srcA, srcB, op.Dest, op.DestPred, op.NegA, op.NegB, op.X, op.WriteCC);
+ }
+
+ public static void LopI(EmitterContext context)
+ {
+ InstLopI op = context.GetOp<InstLopI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitLop(context, op.LogicOp, op.PredicateOp, srcA, srcB, op.Dest, op.DestPred, op.NegA, op.NegB, op.X, op.WriteCC);
+ }
+
+ public static void LopC(EmitterContext context)
+ {
+ InstLopC op = context.GetOp<InstLopC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitLop(context, op.LogicOp, op.PredicateOp, srcA, srcB, op.Dest, op.DestPred, op.NegA, op.NegB, op.X, op.WriteCC);
+ }
+
+ public static void Lop32i(EmitterContext context)
+ {
+ InstLop32i op = context.GetOp<InstLop32i>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, op.Imm32);
+
+ EmitLop(context, op.LogicOp, PredicateOp.F, srcA, srcB, op.Dest, PT, op.NegA, op.NegB, op.X, op.WriteCC);
+ }
+
+ public static void Lop3R(EmitterContext context)
+ {
+ InstLop3R op = context.GetOp<InstLop3R>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitLop3(context, op.Imm, op.PredicateOp, srcA, srcB, srcC, op.Dest, op.DestPred, op.X, op.WriteCC);
+ }
+
+ public static void Lop3I(EmitterContext context)
+ {
+ InstLop3I op = context.GetOp<InstLop3I>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitLop3(context, op.Imm, PredicateOp.F, srcA, srcB, srcC, op.Dest, PT, false, op.WriteCC);
+ }
+
+ public static void Lop3C(EmitterContext context)
+ {
+ InstLop3C op = context.GetOp<InstLop3C>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcC = GetSrcReg(context, op.SrcC);
+
+ EmitLop3(context, op.Imm, PredicateOp.F, srcA, srcB, srcC, op.Dest, PT, false, op.WriteCC);
+ }
+
+ private static void EmitLop(
+ EmitterContext context,
+ LogicOp logicOp,
+ PredicateOp predOp,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ int destPred,
+ bool invertA,
+ bool invertB,
+ bool extended,
+ bool writeCC)
+ {
+ srcA = context.BitwiseNot(srcA, invertA);
+ srcB = context.BitwiseNot(srcB, invertB);
+
+ Operand res = logicOp switch
+ {
+ LogicOp.And => res = context.BitwiseAnd(srcA, srcB),
+ LogicOp.Or => res = context.BitwiseOr(srcA, srcB),
+ LogicOp.Xor => res = context.BitwiseExclusiveOr(srcA, srcB),
+ _ => srcB
+ };
+
+ EmitLopPredWrite(context, res, predOp, destPred);
+
+ context.Copy(GetDest(rd), res);
+
+ SetZnFlags(context, res, writeCC, extended);
+ }
+
+ private static void EmitLop3(
+ EmitterContext context,
+ int truthTable,
+ PredicateOp predOp,
+ Operand srcA,
+ Operand srcB,
+ Operand srcC,
+ int rd,
+ int destPred,
+ bool extended,
+ bool writeCC)
+ {
+ Operand res = Lop3Expression.GetFromTruthTable(context, srcA, srcB, srcC, truthTable);
+
+ EmitLopPredWrite(context, res, predOp, destPred);
+
+ context.Copy(GetDest(rd), res);
+
+ SetZnFlags(context, res, writeCC, extended);
+ }
+
+ private static void EmitLopPredWrite(EmitterContext context, Operand result, PredicateOp predOp, int pred)
+ {
+ if (pred != RegisterConsts.PredicateTrueIndex)
+ {
+ Operand pRes;
+
+ if (predOp == PredicateOp.F)
+ {
+ pRes = Const(IrConsts.False);
+ }
+ else if (predOp == PredicateOp.T)
+ {
+ pRes = Const(IrConsts.True);
+ }
+ else if (predOp == PredicateOp.Z)
+ {
+ pRes = context.ICompareEqual(result, Const(0));
+ }
+ else /* if (predOp == Pop.Nz) */
+ {
+ pRes = context.ICompareNotEqual(result, Const(0));
+ }
+
+ context.Copy(Register(pred, RegisterType.Predicate), pRes);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs
new file mode 100644
index 00000000..73930ed1
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitIntegerMinMax.cs
@@ -0,0 +1,71 @@
+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;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void ImnmxR(EmitterContext context)
+ {
+ InstImnmxR op = context.GetOp<InstImnmxR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitImnmx(context, srcA, srcB, srcPred, op.Dest, op.Signed, op.WriteCC);
+ }
+
+ public static void ImnmxI(EmitterContext context)
+ {
+ InstImnmxI op = context.GetOp<InstImnmxI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitImnmx(context, srcA, srcB, srcPred, op.Dest, op.Signed, op.WriteCC);
+ }
+
+ public static void ImnmxC(EmitterContext context)
+ {
+ InstImnmxC op = context.GetOp<InstImnmxC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ var srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ EmitImnmx(context, srcA, srcB, srcPred, op.Dest, op.Signed, op.WriteCC);
+ }
+
+ private static void EmitImnmx(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ Operand srcPred,
+ int rd,
+ bool isSignedInt,
+ bool writeCC)
+ {
+ Operand resMin = isSignedInt
+ ? context.IMinimumS32(srcA, srcB)
+ : context.IMinimumU32(srcA, srcB);
+
+ Operand resMax = isSignedInt
+ ? context.IMaximumS32(srcA, srcB)
+ : context.IMaximumU32(srcA, srcB);
+
+ Operand res = context.ConditionalSelect(srcPred, resMin, resMax);
+
+ context.Copy(GetDest(rd), res);
+
+ SetZnFlags(context, res, writeCC);
+
+ // TODO: X flags.
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index 6744a9e8..78f41e8e 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -15,369 +15,160 @@ namespace Ryujinx.Graphics.Shader.Instructions
Shared
}
- public static void Al2p(EmitterContext context)
- {
- OpCodeAl2p op = (OpCodeAl2p)context.CurrOp;
-
- if (op.Rd.IsRZ)
- {
- return;
- }
-
- context.Copy(Register(op.Rd), context.IAdd(Register(op.Ra), Const(op.Immediate)));
- }
-
- public static void Ald(EmitterContext context)
- {
- OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
-
- Operand primVertex = context.Copy(GetSrcC(context));
-
- for (int index = 0; index < op.Count; index++)
- {
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
-
- if (rd.IsRZ)
- {
- break;
- }
-
- if (op.Phys)
- {
- Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
-
- context.Copy(Register(rd), context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, primVertex));
-
- context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
- }
- else if (op.Rc.IsRZ)
- {
- Operand src = Attribute(op.AttributeOffset + index * 4);
-
- context.FlagAttributeRead(src.Value);
-
- context.Copy(Register(rd), src);
- }
- else
- {
- Operand src = Const(op.AttributeOffset + index * 4);
-
- context.FlagAttributeRead(src.Value);
-
- context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
- }
- }
- }
-
- public static void Ast(EmitterContext context)
- {
- OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
-
- for (int index = 0; index < op.Count; index++)
- {
- if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex)
- {
- break;
- }
-
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
-
- if (op.Phys)
- {
- Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
-
- context.StoreAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Register(rd));
-
- context.Config.SetUsedFeature(FeatureFlags.OaIndexing);
- }
- else
- {
- Operand dest = Attribute(op.AttributeOffset + index * 4);
-
- context.FlagAttributeWritten(dest.Value);
-
- context.Copy(dest, Register(rd));
- }
- }
- }
-
public static void Atom(EmitterContext context)
{
- OpCodeAtom op = (OpCodeAtom)context.CurrOp;
+ InstAtom op = context.GetOp<InstAtom>();
- ReductionType type = (ReductionType)op.RawOpCode.Extract(49, 2);
+ int sOffset = (op.Imm20 << 12) >> 12;
- int sOffset = (op.RawOpCode.Extract(28, 20) << 12) >> 12;
+ (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, sOffset);
- (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, sOffset);
+ Operand value = GetSrcReg(context, op.SrcB);
- Operand value = GetSrcB(context);
+ Operand res = EmitAtomicOp(context, Instruction.MrGlobal, op.Op, op.Size, addrLow, addrHigh, value);
- Operand res = EmitAtomicOp(
- context,
- Instruction.MrGlobal,
- op.AtomicOp,
- type,
- addrLow,
- addrHigh,
- value);
-
- context.Copy(GetDest(context), res);
+ context.Copy(GetDest(op.Dest), res);
}
public static void Atoms(EmitterContext context)
{
- OpCodeAtom op = (OpCodeAtom)context.CurrOp;
+ InstAtoms op = context.GetOp<InstAtoms>();
- ReductionType type = op.RawOpCode.Extract(28, 2) switch
- {
- 0 => ReductionType.U32,
- 1 => ReductionType.S32,
- 2 => ReductionType.U64,
- _ => ReductionType.S64
- };
-
- Operand offset = context.ShiftRightU32(GetSrcA(context), Const(2));
+ Operand offset = context.ShiftRightU32(GetSrcReg(context, op.SrcA), Const(2));
- int sOffset = (op.RawOpCode.Extract(30, 22) << 10) >> 10;
+ int sOffset = (op.Imm22 << 10) >> 10;
offset = context.IAdd(offset, Const(sOffset));
- Operand value = GetSrcB(context);
-
- Operand res = EmitAtomicOp(
- context,
- Instruction.MrShared,
- op.AtomicOp,
- type,
- offset,
- Const(0),
- value);
-
- context.Copy(GetDest(context), res);
- }
-
- public static void Bar(EmitterContext context)
- {
- OpCodeBarrier op = (OpCodeBarrier)context.CurrOp;
-
- // TODO: Support other modes.
- if (op.Mode == BarrierMode.Sync)
- {
- context.Barrier();
- }
- else
- {
- context.Config.GpuAccessor.Log($"Invalid barrier mode: {op.Mode}.");
- }
- }
-
- public static void Ipa(EmitterContext context)
- {
- OpCodeIpa op = (OpCodeIpa)context.CurrOp;
-
- context.FlagAttributeRead(op.AttributeOffset);
-
- Operand res;
-
- if (op.Idx)
- {
- Operand userAttrOffset = context.ISubtract(GetSrcA(context), Const(AttributeConsts.UserAttributeBase));
- Operand userAttrIndex = context.ShiftRightU32(userAttrOffset, Const(2));
-
- res = context.LoadAttribute(Const(AttributeConsts.UserAttributeBase), userAttrIndex, Const(0));
- res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
-
- context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
- }
- else
- {
- res = Attribute(op.AttributeOffset);
-
- if (op.AttributeOffset >= AttributeConsts.UserAttributeBase &&
- op.AttributeOffset < AttributeConsts.UserAttributeEnd)
- {
- int index = (op.AttributeOffset - AttributeConsts.UserAttributeBase) >> 4;
-
- if (context.Config.ImapTypes[index].GetFirstUsedType() == PixelImap.Perspective)
- {
- res = context.FPMultiply(res, Attribute(AttributeConsts.PositionW));
- }
- }
- }
+ Operand value = GetSrcReg(context, op.SrcB);
- if (op.Mode == InterpolationMode.Default)
+ AtomSize size = op.AtomsSize switch
{
- Operand srcB = GetSrcB(context);
-
- res = context.FPMultiply(res, srcB);
- }
-
- res = context.FPSaturate(res, op.Saturate);
-
- context.Copy(GetDest(context), res);
- }
+ AtomsSize.S32 => AtomSize.S32,
+ AtomsSize.U64 => AtomSize.U64,
+ AtomsSize.S64 => AtomSize.S64,
+ _ => AtomSize.U32
+ };
- public static void Isberd(EmitterContext context)
- {
- // This instruction performs a load from ISBE memory,
- // however it seems to be only used to get some vertex
- // input data, so we instead propagate the offset so that
- // it can be used on the attribute load.
- context.Copy(GetDest(context), GetSrcA(context));
- }
+ Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomOp, size, offset, Const(0), value);
- public static void Ld(EmitterContext context)
- {
- EmitLoad(context, MemoryRegion.Local);
+ context.Copy(GetDest(op.Dest), res);
}
public static void Ldc(EmitterContext context)
{
- OpCodeLdc op = (OpCodeLdc)context.CurrOp;
+ InstLdc op = context.GetOp<InstLdc>();
- if (op.Size > IntegerSize.B64)
+ if (op.LsSize > LsSize2.B64)
{
- context.Config.GpuAccessor.Log($"Invalid LDC size: {op.Size}.");
+ context.Config.GpuAccessor.Log($"Invalid LDC size: {op.LsSize}.");
+ return;
}
- bool isSmallInt = op.Size < IntegerSize.B32;
+ bool isSmallInt = op.LsSize < LsSize2.B32;
- int count = op.Size == IntegerSize.B64 ? 2 : 1;
+ int count = op.LsSize == LsSize2.B64 ? 2 : 1;
- Operand slot = Const(op.Slot);
- Operand srcA = GetSrcA(context);
+ Operand slot = Const(op.CbufSlot);
+ Operand srcA = GetSrcReg(context, op.SrcA);
- if (op.IndexMode == CbIndexMode.Is ||
- op.IndexMode == CbIndexMode.Isl)
+ if (op.AddressMode == AddressMode.Is || op.AddressMode == AddressMode.Isl)
{
slot = context.IAdd(slot, context.BitfieldExtractU32(srcA, Const(16), Const(16)));
srcA = context.BitwiseAnd(srcA, Const(0xffff));
}
- Operand addr = context.IAdd(srcA, Const(op.Offset));
-
+ Operand addr = context.IAdd(srcA, Const(Imm16ToSInt(op.CbufOffset)));
Operand wordOffset = context.ShiftRightU32(addr, Const(2));
-
Operand bitOffset = GetBitOffset(context, addr);
for (int index = 0; index < count; index++)
{
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
+ Register dest = new Register(op.Dest + index, RegisterType.Gpr);
- if (rd.IsRZ)
+ if (dest.IsRZ)
{
break;
}
Operand offset = context.IAdd(wordOffset, Const(index));
-
Operand value = context.LoadConstant(slot, offset);
if (isSmallInt)
{
- value = ExtractSmallInt(context, op.Size, bitOffset, value);
+ value = ExtractSmallInt(context, (LsSize)op.LsSize, bitOffset, value);
}
- context.Copy(Register(rd), value);
+ context.Copy(Register(dest), value);
}
}
public static void Ldg(EmitterContext context)
{
- EmitLoadGlobal(context);
- }
+ InstLdg op = context.GetOp<InstLdg>();
- public static void Lds(EmitterContext context)
- {
- EmitLoad(context, MemoryRegion.Shared);
+ EmitLdg(context, op.LsSize, op.SrcA, op.Dest, Imm24ToSInt(op.Imm24), op.E);
}
- public static void Membar(EmitterContext context)
+ public static void Ldl(EmitterContext context)
{
- OpCodeMemoryBarrier op = (OpCodeMemoryBarrier)context.CurrOp;
+ InstLdl op = context.GetOp<InstLdl>();
- if (op.Level == BarrierLevel.Cta)
- {
- context.GroupMemoryBarrier();
- }
- else
- {
- context.MemoryBarrier();
- }
+ EmitLoad(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
}
- public static void Out(EmitterContext context)
+ public static void Lds(EmitterContext context)
{
- OpCode op = context.CurrOp;
+ InstLds op = context.GetOp<InstLds>();
- bool emit = op.RawOpCode.Extract(39);
- bool cut = op.RawOpCode.Extract(40);
-
- if (!(emit || cut))
- {
- context.Config.GpuAccessor.Log("Invalid OUT encoding.");
- }
-
- if (emit)
- {
- context.EmitVertex();
- }
-
- if (cut)
- {
- context.EndPrimitive();
- }
+ EmitLoad(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
}
public static void Red(EmitterContext context)
{
- OpCodeRed op = (OpCodeRed)context.CurrOp;
-
- (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
-
- EmitAtomicOp(
- context,
- Instruction.MrGlobal,
- op.AtomicOp,
- op.Type,
- addrLow,
- addrHigh,
- GetDest(context));
+ InstRed op = context.GetOp<InstRed>();
+
+ (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(op.SrcA, RegisterType.Gpr), op.E, op.Imm20);
+
+ EmitAtomicOp(context, Instruction.MrGlobal, (AtomOp)op.RedOp, op.RedSize, addrLow, addrHigh, GetDest(op.SrcB));
}
- public static void St(EmitterContext context)
+ public static void Stg(EmitterContext context)
{
- EmitStore(context, MemoryRegion.Local);
+ InstStg op = context.GetOp<InstStg>();
+
+ EmitStg(context, op.LsSize, op.SrcA, op.Dest, Imm24ToSInt(op.Imm24), op.E);
}
- public static void Stg(EmitterContext context)
+ public static void Stl(EmitterContext context)
{
- EmitStoreGlobal(context);
+ InstStl op = context.GetOp<InstStl>();
+
+ EmitStore(context, MemoryRegion.Local, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
}
public static void Sts(EmitterContext context)
{
- EmitStore(context, MemoryRegion.Shared);
+ InstSts op = context.GetOp<InstSts>();
+
+ EmitStore(context, MemoryRegion.Shared, op.LsSize, GetSrcReg(context, op.SrcA), op.Dest, Imm24ToSInt(op.Imm24));
}
private static Operand EmitAtomicOp(
EmitterContext context,
- Instruction mr,
- AtomicOp op,
- ReductionType type,
- Operand addrLow,
- Operand addrHigh,
- Operand value)
+ Instruction mr,
+ AtomOp op,
+ AtomSize type,
+ Operand addrLow,
+ Operand addrHigh,
+ Operand value)
{
Operand res = Const(0);
switch (op)
{
- case AtomicOp.Add:
- if (type == ReductionType.S32 || type == ReductionType.U32)
+ case AtomOp.Add:
+ if (type == AtomSize.S32 || type == AtomSize.U32)
{
res = context.AtomicAdd(mr, addrLow, addrHigh, value);
}
@@ -386,8 +177,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log($"Invalid reduction type: {type}.");
}
break;
- case AtomicOp.BitwiseAnd:
- if (type == ReductionType.S32 || type == ReductionType.U32)
+ case AtomOp.And:
+ if (type == AtomSize.S32 || type == AtomSize.U32)
{
res = context.AtomicAnd(mr, addrLow, addrHigh, value);
}
@@ -396,8 +187,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log($"Invalid reduction type: {type}.");
}
break;
- case AtomicOp.BitwiseExclusiveOr:
- if (type == ReductionType.S32 || type == ReductionType.U32)
+ case AtomOp.Xor:
+ if (type == AtomSize.S32 || type == AtomSize.U32)
{
res = context.AtomicXor(mr, addrLow, addrHigh, value);
}
@@ -406,8 +197,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log($"Invalid reduction type: {type}.");
}
break;
- case AtomicOp.BitwiseOr:
- if (type == ReductionType.S32 || type == ReductionType.U32)
+ case AtomOp.Or:
+ if (type == AtomSize.S32 || type == AtomSize.U32)
{
res = context.AtomicOr(mr, addrLow, addrHigh, value);
}
@@ -416,12 +207,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log($"Invalid reduction type: {type}.");
}
break;
- case AtomicOp.Maximum:
- if (type == ReductionType.S32)
+ case AtomOp.Max:
+ if (type == AtomSize.S32)
{
res = context.AtomicMaxS32(mr, addrLow, addrHigh, value);
}
- else if (type == ReductionType.U32)
+ else if (type == AtomSize.U32)
{
res = context.AtomicMaxU32(mr, addrLow, addrHigh, value);
}
@@ -430,12 +221,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log($"Invalid reduction type: {type}.");
}
break;
- case AtomicOp.Minimum:
- if (type == ReductionType.S32)
+ case AtomOp.Min:
+ if (type == AtomSize.S32)
{
res = context.AtomicMinS32(mr, addrLow, addrHigh, value);
}
- else if (type == ReductionType.U32)
+ else if (type == AtomSize.U32)
{
res = context.AtomicMinU32(mr, addrLow, addrHigh, value);
}
@@ -449,77 +240,82 @@ namespace Ryujinx.Graphics.Shader.Instructions
return res;
}
- private static void EmitLoad(EmitterContext context, MemoryRegion region)
+ private static void EmitLoad(
+ EmitterContext context,
+ MemoryRegion region,
+ LsSize2 size,
+ Operand srcA,
+ int rd,
+ int offset)
{
- OpCodeMemory op = (OpCodeMemory)context.CurrOp;
-
- if (op.Size > IntegerSize.B128)
+ if (size > LsSize2.B128)
{
- context.Config.GpuAccessor.Log($"Invalid load size: {op.Size}.");
+ context.Config.GpuAccessor.Log($"Invalid load size: {size}.");
+ return;
}
- bool isSmallInt = op.Size < IntegerSize.B32;
+ bool isSmallInt = size < LsSize2.B32;
int count = 1;
- switch (op.Size)
+ switch (size)
{
- case IntegerSize.B64: count = 2; break;
- case IntegerSize.B128: count = 4; break;
+ case LsSize2.B64: count = 2; break;
+ case LsSize2.B128: count = 4; break;
}
- Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
-
- // Word offset = byte offset / 4 (one word = 4 bytes).
- Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
-
+ Operand baseOffset = context.IAdd(srcA, Const(offset));
+ Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); // Word offset = byte offset / 4 (one word = 4 bytes).
Operand bitOffset = GetBitOffset(context, baseOffset);
for (int index = 0; index < count; index++)
{
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
+ Register dest = new Register(rd + index, RegisterType.Gpr);
- if (rd.IsRZ)
+ if (dest.IsRZ)
{
break;
}
- Operand offset = context.IAdd(wordOffset, Const(index));
-
+ Operand elemOffset = context.IAdd(wordOffset, Const(index));
Operand value = null;
switch (region)
{
- case MemoryRegion.Local: value = context.LoadLocal (offset); break;
- case MemoryRegion.Shared: value = context.LoadShared(offset); break;
+ case MemoryRegion.Local: value = context.LoadLocal(elemOffset); break;
+ case MemoryRegion.Shared: value = context.LoadShared(elemOffset); break;
}
if (isSmallInt)
{
- value = ExtractSmallInt(context, op.Size, bitOffset, value);
+ value = ExtractSmallInt(context, (LsSize)size, bitOffset, value);
}
- context.Copy(Register(rd), value);
+ context.Copy(Register(dest), value);
}
}
- private static void EmitLoadGlobal(EmitterContext context)
+ private static void EmitLdg(
+ EmitterContext context,
+ LsSize size,
+ int ra,
+ int rd,
+ int offset,
+ bool extended)
{
- OpCodeMemory op = (OpCodeMemory)context.CurrOp;
-
- bool isSmallInt = op.Size < IntegerSize.B32;
+ bool isSmallInt = size < LsSize.B32;
- int count = GetVectorCount(op.Size);
+ int count = GetVectorCount(size);
- (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
+ (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
Operand bitOffset = GetBitOffset(context, addrLow);
for (int index = 0; index < count; index++)
{
- Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
+ Register dest = new Register(rd + index, RegisterType.Gpr);
- if (rd.IsRZ)
+ if (dest.IsRZ)
{
break;
}
@@ -528,47 +324,47 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (isSmallInt)
{
- value = ExtractSmallInt(context, op.Size, bitOffset, value);
+ value = ExtractSmallInt(context, size, bitOffset, value);
}
- context.Copy(Register(rd), value);
+ context.Copy(Register(dest), value);
}
}
- private static void EmitStore(EmitterContext context, MemoryRegion region)
+ private static void EmitStore(
+ EmitterContext context,
+ MemoryRegion region,
+ LsSize2 size,
+ Operand srcA,
+ int rd,
+ int offset)
{
- OpCodeMemory op = (OpCodeMemory)context.CurrOp;
-
- if (op.Size > IntegerSize.B128)
+ if (size > LsSize2.B128)
{
- context.Config.GpuAccessor.Log($"Invalid store size: {op.Size}.");
+ context.Config.GpuAccessor.Log($"Invalid store size: {size}.");
+ return;
}
- bool isSmallInt = op.Size < IntegerSize.B32;
+ bool isSmallInt = size < LsSize2.B32;
int count = 1;
- switch (op.Size)
+ switch (size)
{
- case IntegerSize.B64: count = 2; break;
- case IntegerSize.B128: count = 4; break;
+ case LsSize2.B64: count = 2; break;
+ case LsSize2.B128: count = 4; break;
}
- Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
-
+ Operand baseOffset = context.IAdd(srcA, Const(offset));
Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
-
Operand bitOffset = GetBitOffset(context, baseOffset);
for (int index = 0; index < count; index++)
{
- bool isRz = op.Rd.IsRZ;
-
- Register rd = new Register(isRz ? op.Rd.Index : op.Rd.Index + index, RegisterType.Gpr);
+ bool isRz = rd + index >= RegisterConsts.RegisterZeroIndex;
- Operand value = Register(rd);
-
- Operand offset = context.IAdd(wordOffset, Const(index));
+ Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
+ Operand elemOffset = context.IAdd(wordOffset, Const(index));
if (isSmallInt)
{
@@ -576,60 +372,68 @@ namespace Ryujinx.Graphics.Shader.Instructions
switch (region)
{
- case MemoryRegion.Local: word = context.LoadLocal (offset); break;
- case MemoryRegion.Shared: word = context.LoadShared(offset); break;
+ case MemoryRegion.Local: word = context.LoadLocal(elemOffset); break;
+ case MemoryRegion.Shared: word = context.LoadShared(elemOffset); break;
}
- value = InsertSmallInt(context, op.Size, bitOffset, word, value);
+ value = InsertSmallInt(context, (LsSize)size, bitOffset, word, value);
}
switch (region)
{
- case MemoryRegion.Local: context.StoreLocal (offset, value); break;
- case MemoryRegion.Shared: context.StoreShared(offset, value); break;
+ case MemoryRegion.Local: context.StoreLocal(elemOffset, value); break;
+ case MemoryRegion.Shared: context.StoreShared(elemOffset, value); break;
}
}
}
- private static void EmitStoreGlobal(EmitterContext context)
+ private static void EmitStg(
+ EmitterContext context,
+ LsSize2 size,
+ int ra,
+ int rd,
+ int offset,
+ bool extended)
{
- OpCodeMemory op = (OpCodeMemory)context.CurrOp;
+ if (size > LsSize2.B128)
+ {
+ context.Config.GpuAccessor.Log($"Invalid store size: {size}.");
+ return;
+ }
- bool isSmallInt = op.Size < IntegerSize.B32;
+ bool isSmallInt = size < LsSize2.B32;
- int count = GetVectorCount(op.Size);
+ int count = GetVectorCount((LsSize)size);
- (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
+ (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
Operand bitOffset = GetBitOffset(context, addrLow);
for (int index = 0; index < count; index++)
{
- bool isRz = op.Rd.IsRZ;
-
- Register rd = new Register(isRz ? op.Rd.Index : op.Rd.Index + index, RegisterType.Gpr);
+ bool isRz = rd + index >= RegisterConsts.RegisterZeroIndex;
- Operand value = Register(rd);
+ Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
if (isSmallInt)
{
Operand word = context.LoadGlobal(addrLow, addrHigh);
- value = InsertSmallInt(context, op.Size, bitOffset, word, value);
+ value = InsertSmallInt(context, (LsSize)size, bitOffset, word, value);
}
context.StoreGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh, value);
}
}
- private static int GetVectorCount(IntegerSize size)
+ private static int GetVectorCount(LsSize size)
{
switch (size)
{
- case IntegerSize.B64:
+ case LsSize.B64:
return 2;
- case IntegerSize.B128:
- case IntegerSize.UB128:
+ case LsSize.B128:
+ case LsSize.UB128:
return 4;
}
@@ -642,7 +446,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
bool extended,
int offset)
{
- Operand addrLow = GetSrcA(context);
+ Operand addrLow = Register(ra);
Operand addrHigh;
if (extended && !ra.IsRZ)
@@ -678,18 +482,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static Operand ExtractSmallInt(
EmitterContext context,
- IntegerSize size,
- Operand bitOffset,
- Operand value)
+ LsSize size,
+ Operand bitOffset,
+ Operand value)
{
value = context.ShiftRightU32(value, bitOffset);
switch (size)
{
- case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break;
- case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break;
- case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break;
- case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break;
+ case LsSize.U8: value = ZeroExtendTo32(context, value, 8); break;
+ case LsSize.U16: value = ZeroExtendTo32(context, value, 16); break;
+ case LsSize.S8: value = SignExtendTo32(context, value, 8); break;
+ case LsSize.S16: value = SignExtendTo32(context, value, 16); break;
}
return value;
@@ -697,21 +501,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
private static Operand InsertSmallInt(
EmitterContext context,
- IntegerSize size,
- Operand bitOffset,
- Operand word,
- Operand value)
+ LsSize size,
+ Operand bitOffset,
+ Operand word,
+ Operand value)
{
switch (size)
{
- case IntegerSize.U8:
- case IntegerSize.S8:
+ case LsSize.U8:
+ case LsSize.S8:
value = context.BitwiseAnd(value, Const(0xff));
value = context.BitfieldInsert(word, value, bitOffset, Const(8));
break;
- case IntegerSize.U16:
- case IntegerSize.S16:
+ case LsSize.U16:
+ case LsSize.S16:
value = context.BitwiseAnd(value, Const(0xffff));
value = context.BitfieldInsert(word, value, bitOffset, Const(16));
break;
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
index 98a713fb..245b2253 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
@@ -9,67 +9,85 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
static partial class InstEmit
{
- public static void Mov(EmitterContext context)
+ public static void MovR(EmitterContext context)
{
- context.Copy(GetDest(context), GetSrcB(context));
+ InstMovR op = context.GetOp<InstMovR>();
+
+ context.Copy(GetDest(op.Dest), GetSrcReg(context, op.SrcA));
}
- public static void R2p(EmitterContext context)
+ public static void MovI(EmitterContext context)
{
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+ InstMovI op = context.GetOp<InstMovI>();
- bool isCC = op.RawOpCode.Extract(40);
- int shift = op.RawOpCode.Extract(41, 2) * 8;
+ context.Copy(GetDest(op.Dest), GetSrcImm(context, op.Imm20));
+ }
- Operand value = GetSrcA(context);
- Operand mask = GetSrcB(context);
+ public static void MovC(EmitterContext context)
+ {
+ InstMovC op = context.GetOp<InstMovC>();
- Operand Test(Operand value, int bit)
- {
- return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
- }
+ context.Copy(GetDest(op.Dest), GetSrcCbuf(context, op.CbufSlot, op.CbufOffset));
+ }
- if (isCC)
- {
- // TODO: Support Register to condition code flags copy.
- context.Config.GpuAccessor.Log("R2P.CC not implemented.");
- }
- else
- {
- for (int bit = 0; bit < 7; bit++)
- {
- Operand pred = Register(bit, RegisterType.Predicate);
+ public static void Mov32i(EmitterContext context)
+ {
+ InstMov32i op = context.GetOp<InstMov32i>();
- Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred);
+ context.Copy(GetDest(op.Dest), GetSrcImm(context, op.Imm32));
+ }
- context.Copy(pred, res);
- }
- }
+ public static void R2pR(EmitterContext context)
+ {
+ InstR2pR op = context.GetOp<InstR2pR>();
+
+ Operand value = GetSrcReg(context, op.SrcA);
+ Operand mask = GetSrcReg(context, op.SrcB);
+
+ EmitR2p(context, value, mask, op.ByteSel, op.Ccpr);
}
- public static void S2r(EmitterContext context)
+ public static void R2pI(EmitterContext context)
+ {
+ InstR2pI op = context.GetOp<InstR2pI>();
+
+ Operand value = GetSrcReg(context, op.SrcA);
+ Operand mask = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitR2p(context, value, mask, op.ByteSel, op.Ccpr);
+ }
+
+ public static void R2pC(EmitterContext context)
{
- // TODO: Better impl.
- OpCodeAlu op = (OpCodeAlu)context.CurrOp;
+ InstR2pC op = context.GetOp<InstR2pC>();
+
+ Operand value = GetSrcReg(context, op.SrcA);
+ Operand mask = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
- SystemRegister sysReg = (SystemRegister)op.RawOpCode.Extract(20, 8);
+ EmitR2p(context, value, mask, op.ByteSel, op.Ccpr);
+ }
+
+ public static void S2r(EmitterContext context)
+ {
+ InstS2r op = context.GetOp<InstS2r>();
Operand src;
- switch (sysReg)
+ switch (op.SReg)
{
- case SystemRegister.LaneId: src = Attribute(AttributeConsts.LaneId); break;
+ case SReg.LaneId:
+ src = Attribute(AttributeConsts.LaneId);
+ break;
- // TODO: Use value from Y direction GPU register.
- case SystemRegister.YDirection: src = ConstF(1); break;
+ case SReg.YDirection:
+ src = ConstF(1); // TODO: Use value from Y direction GPU register.
+ break;
- case SystemRegister.ThreadKill: src = context.Config.Stage == ShaderStage.Fragment
- ? Attribute(AttributeConsts.ThreadKill)
- : Const(0);
+ case SReg.ThreadKill:
+ src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
break;
- case SystemRegister.ThreadId:
- {
+ case SReg.TId:
Operand tidX = Attribute(AttributeConsts.ThreadIdX);
Operand tidY = Attribute(AttributeConsts.ThreadIdY);
Operand tidZ = Attribute(AttributeConsts.ThreadIdZ);
@@ -78,62 +96,115 @@ namespace Ryujinx.Graphics.Shader.Instructions
tidZ = context.ShiftLeft(tidZ, Const(26));
src = context.BitwiseOr(tidX, context.BitwiseOr(tidY, tidZ));
+ break;
+ case SReg.TIdX:
+ src = Attribute(AttributeConsts.ThreadIdX);
+ break;
+ case SReg.TIdY:
+ src = Attribute(AttributeConsts.ThreadIdY);
+ break;
+ case SReg.TIdZ:
+ src = Attribute(AttributeConsts.ThreadIdZ);
+ break;
+
+ case SReg.CtaIdX:
+ src = Attribute(AttributeConsts.CtaIdX);
+ break;
+ case SReg.CtaIdY:
+ src = Attribute(AttributeConsts.CtaIdY);
+ break;
+ case SReg.CtaIdZ:
+ src = Attribute(AttributeConsts.CtaIdZ);
+ break;
+
+ case SReg.EqMask:
+ src = Attribute(AttributeConsts.EqMask);
+ break;
+ case SReg.LtMask:
+ src = Attribute(AttributeConsts.LtMask);
+ break;
+ case SReg.LeMask:
+ src = Attribute(AttributeConsts.LeMask);
+ break;
+ case SReg.GtMask:
+ src = Attribute(AttributeConsts.GtMask);
+ break;
+ case SReg.GeMask:
+ src = Attribute(AttributeConsts.GeMask);
break;
- }
- case SystemRegister.ThreadIdX: src = Attribute(AttributeConsts.ThreadIdX); break;
- case SystemRegister.ThreadIdY: src = Attribute(AttributeConsts.ThreadIdY); break;
- case SystemRegister.ThreadIdZ: src = Attribute(AttributeConsts.ThreadIdZ); break;
- case SystemRegister.CtaIdX: src = Attribute(AttributeConsts.CtaIdX); break;
- case SystemRegister.CtaIdY: src = Attribute(AttributeConsts.CtaIdY); break;
- case SystemRegister.CtaIdZ: src = Attribute(AttributeConsts.CtaIdZ); break;
- case SystemRegister.EqMask: src = Attribute(AttributeConsts.EqMask); break;
- case SystemRegister.LtMask: src = Attribute(AttributeConsts.LtMask); break;
- case SystemRegister.LeMask: src = Attribute(AttributeConsts.LeMask); break;
- case SystemRegister.GtMask: src = Attribute(AttributeConsts.GtMask); break;
- case SystemRegister.GeMask: src = Attribute(AttributeConsts.GeMask); break;
-
- default: src = Const(0); break;
+ default:
+ src = Const(0);
+ break;
}
- context.Copy(GetDest(context), src);
+ context.Copy(GetDest(op.Dest), src);
}
- public static void Sel(EmitterContext context)
+ public static void SelR(EmitterContext context)
{
- Operand pred = GetPredicate39(context);
+ InstSelR op = context.GetOp<InstSelR>();
+
+ Operand srcA = GetSrcReg(context, op.SrcA);
+ Operand srcB = GetSrcReg(context, op.SrcB);
+ Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
- Operand srcA = GetSrcA(context);
- Operand srcB = GetSrcB(context);
+ EmitSel(context, srcA, srcB, srcPred, op.Dest);
+ }
+
+ public static void SelI(EmitterContext context)
+ {
+ InstSelI op = context.GetOp<InstSelI>();
- Operand res = context.ConditionalSelect(pred, srcA, srcB);
+ Operand srcA = GetSrcReg(context, op.SrcA);
+ Operand srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+ Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
- context.Copy(GetDest(context), res);
+ EmitSel(context, srcA, srcB, srcPred, op.Dest);
}
- public static void Shfl(EmitterContext context)
+ public static void SelC(EmitterContext context)
{
- OpCodeShuffle op = (OpCodeShuffle)context.CurrOp;
+ InstSelC op = context.GetOp<InstSelC>();
- Operand pred = Register(op.Predicate48);
+ Operand srcA = GetSrcReg(context, op.SrcA);
+ Operand srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+ Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
- Operand srcA = GetSrcA(context);
+ EmitSel(context, srcA, srcB, srcPred, op.Dest);
+ }
- Operand srcB = op.IsBImmediate ? Const(op.ImmediateB) : Register(op.Rb);
- Operand srcC = op.IsCImmediate ? Const(op.ImmediateC) : Register(op.Rc);
+ private static void EmitR2p(EmitterContext context, Operand value, Operand mask, ByteSel byteSel, bool ccpr)
+ {
+ Operand Test(Operand value, int bit)
+ {
+ return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
+ }
- (Operand res, Operand valid) = op.ShuffleType switch
+ if (ccpr)
+ {
+ // TODO: Support Register to condition code flags copy.
+ context.Config.GpuAccessor.Log("R2P.CC not implemented.");
+ }
+ else
{
- ShuffleType.Indexed => context.Shuffle(srcA, srcB, srcC),
- ShuffleType.Up => context.ShuffleUp(srcA, srcB, srcC),
- ShuffleType.Down => context.ShuffleDown(srcA, srcB, srcC),
- ShuffleType.Butterfly => context.ShuffleXor(srcA, srcB, srcC),
- _ => (null, null)
- };
-
- context.Copy(GetDest(context), res);
- context.Copy(pred, valid);
+ int shift = (int)byteSel * 8;
+
+ for (int bit = 0; bit < RegisterConsts.PredsCount; bit++)
+ {
+ Operand pred = Register(bit, RegisterType.Predicate);
+ Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred);
+ context.Copy(pred, res);
+ }
+ }
+ }
+
+ private static void EmitSel(EmitterContext context, Operand srcA, Operand srcB, Operand srcPred, int rd)
+ {
+ Operand res = context.ConditionalSelect(srcPred, srcA, srcB);
+
+ context.Copy(GetDest(rd), res);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs
new file mode 100644
index 00000000..465024eb
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMultifunction.cs
@@ -0,0 +1,85 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void RroR(EmitterContext context)
+ {
+ InstRroR op = context.GetOp<InstRroR>();
+
+ EmitRro(context, GetSrcReg(context, op.SrcB), op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void RroI(EmitterContext context)
+ {
+ InstRroI op = context.GetOp<InstRroI>();
+
+ EmitRro(context, GetSrcImm(context, Imm20ToFloat(op.Imm20)), op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void RroC(EmitterContext context)
+ {
+ InstRroC op = context.GetOp<InstRroC>();
+
+ EmitRro(context, GetSrcCbuf(context, op.CbufSlot, op.CbufOffset), op.Dest, op.AbsB, op.NegB);
+ }
+
+ public static void Mufu(EmitterContext context)
+ {
+ InstMufu op = context.GetOp<InstMufu>();
+
+ Operand res = context.FPAbsNeg(GetSrcReg(context, op.SrcA), op.AbsA, op.NegA);
+
+ switch (op.MufuOp)
+ {
+ case MufuOp.Cos:
+ res = context.FPCosine(res);
+ break;
+
+ case MufuOp.Sin:
+ res = context.FPSine(res);
+ break;
+
+ case MufuOp.Ex2:
+ res = context.FPExponentB2(res);
+ break;
+
+ case MufuOp.Lg2:
+ res = context.FPLogarithmB2(res);
+ break;
+
+ case MufuOp.Rcp:
+ res = context.FPReciprocal(res);
+ break;
+
+ case MufuOp.Rsq:
+ res = context.FPReciprocalSquareRoot(res);
+ break;
+
+ case MufuOp.Sqrt:
+ res = context.FPSquareRoot(res);
+ break;
+
+ default: /* TODO */ break;
+ }
+
+ context.Copy(GetDest(op.Dest), context.FPSaturate(res, op.Sat));
+ }
+
+ private static void EmitRro(EmitterContext context, Operand srcB, int rd, bool absB, bool negB)
+ {
+ // This is the range reduction operator,
+ // we translate it as a simple move, as it
+ // should be always followed by a matching
+ // MUFU instruction.
+ srcB = context.FPAbsNeg(srcB, absB, negB);
+
+ context.Copy(GetDest(rd), srcB);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs
new file mode 100644
index 00000000..01144007
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitNop.cs
@@ -0,0 +1,15 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.Translation;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void Nop(EmitterContext context)
+ {
+ InstNop op = context.GetOp<InstNop>();
+
+ // No operation.
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs
new file mode 100644
index 00000000..d605661f
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitPredicate.cs
@@ -0,0 +1,54 @@
+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 Pset(EmitterContext context)
+ {
+ InstPset op = context.GetOp<InstPset>();
+
+ Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
+ Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
+ Operand srcC = context.BitwiseNot(Register(op.SrcPred, RegisterType.Predicate), op.SrcPredInv);
+
+ Operand res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
+ res = GetPredLogicalOp(context, op.BoolOpC, res, srcC);
+
+ Operand dest = GetDest(op.Dest);
+
+ if (op.BVal)
+ {
+ context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
+ }
+ else
+ {
+ context.Copy(dest, res);
+ }
+ }
+
+ public static void Psetp(EmitterContext context)
+ {
+ InstPsetp op = context.GetOp<InstPsetp>();
+
+ Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
+ Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
+
+ Operand p0Res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
+ Operand p1Res = context.BitwiseNot(p0Res);
+ Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+
+ p0Res = GetPredLogicalOp(context, op.BoolOpC, p0Res, srcPred);
+ p1Res = GetPredLogicalOp(context, op.BoolOpC, p1Res, srcPred);
+
+ context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
+ context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs
new file mode 100644
index 00000000..ca053ab6
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitShift.cs
@@ -0,0 +1,132 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+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 ShlR(EmitterContext context)
+ {
+ InstShlR op = context.GetOp<InstShlR>();
+
+ EmitShl(context, GetSrcReg(context, op.SrcA), GetSrcReg(context, op.SrcB), op.Dest, op.M);
+ }
+
+ public static void ShlI(EmitterContext context)
+ {
+ InstShlI op = context.GetOp<InstShlI>();
+
+ EmitShl(context, GetSrcReg(context, op.SrcA), GetSrcImm(context, Imm20ToSInt(op.Imm20)), op.Dest, op.M);
+ }
+
+ public static void ShlC(EmitterContext context)
+ {
+ InstShlC op = context.GetOp<InstShlC>();
+
+ EmitShl(context, GetSrcReg(context, op.SrcA), GetSrcCbuf(context, op.CbufSlot, op.CbufOffset), op.Dest, op.M);
+ }
+
+ public static void ShrR(EmitterContext context)
+ {
+ InstShrR op = context.GetOp<InstShrR>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcReg(context, op.SrcB);
+
+ EmitShr(context, srcA, srcB, op.Dest, op.M, op.Brev, op.Signed);
+ }
+
+ public static void ShrI(EmitterContext context)
+ {
+ InstShrI op = context.GetOp<InstShrI>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
+
+ EmitShr(context, srcA, srcB, op.Dest, op.M, op.Brev, op.Signed);
+ }
+
+ public static void ShrC(EmitterContext context)
+ {
+ InstShrC op = context.GetOp<InstShrC>();
+
+ var srcA = GetSrcReg(context, op.SrcA);
+ var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
+
+ EmitShr(context, srcA, srcB, op.Dest, op.M, op.Brev, op.Signed);
+ }
+
+ private static void EmitShl(EmitterContext context, Operand srcA, Operand srcB, int rd, bool mask)
+ {
+ if (mask)
+ {
+ srcB = context.BitwiseAnd(srcB, Const(0x1f));
+ }
+
+ Operand res = context.ShiftLeft(srcA, srcB);
+
+ if (!mask)
+ {
+ // Clamped shift value.
+ Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
+
+ res = context.ConditionalSelect(isLessThan32, res, Const(0));
+ }
+
+ // TODO: X, CC.
+
+ context.Copy(GetDest(rd), res);
+ }
+
+ private static void EmitShr(
+ EmitterContext context,
+ Operand srcA,
+ Operand srcB,
+ int rd,
+ bool mask,
+ bool bitReverse,
+ bool isSigned)
+ {
+ if (bitReverse)
+ {
+ srcA = context.BitfieldReverse(srcA);
+ }
+
+ if (mask)
+ {
+ srcB = context.BitwiseAnd(srcB, Const(0x1f));
+ }
+
+ Operand res = isSigned
+ ? context.ShiftRightS32(srcA, srcB)
+ : context.ShiftRightU32(srcA, srcB);
+
+ if (!mask)
+ {
+ // Clamped shift value.
+ Operand resShiftBy32;
+
+ if (isSigned)
+ {
+ resShiftBy32 = context.ShiftRightS32(srcA, Const(31));
+ }
+ else
+ {
+ resShiftBy32 = Const(0);
+ }
+
+ Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
+
+ res = context.ConditionalSelect(isLessThan32, res, resShiftBy32);
+ }
+
+ // TODO: X, CC.
+
+ context.Copy(GetDest(rd), res);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs
new file mode 100644
index 00000000..a47d6650
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitSurface.cs
@@ -0,0 +1,776 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+using System.Collections.Generic;
+
+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 SuatomB(EmitterContext context)
+ {
+ InstSuatomB op = context.GetOp<InstSuatomB>();
+
+ EmitSuatom(
+ context,
+ op.Dim,
+ op.Op,
+ op.Size,
+ 0,
+ op.SrcA,
+ op.SrcB,
+ op.SrcC,
+ op.Dest,
+ op.Ba,
+ isBindless: true,
+ compareAndSwap: false);
+ }
+
+ public static void Suatom(EmitterContext context)
+ {
+ InstSuatom op = context.GetOp<InstSuatom>();
+
+ EmitSuatom(
+ context,
+ op.Dim,
+ op.Op,
+ op.Size,
+ op.TidB,
+ op.SrcA,
+ op.SrcB,
+ 0,
+ op.Dest,
+ op.Ba,
+ isBindless: false,
+ compareAndSwap: false);
+ }
+
+ public static void SuatomB2(EmitterContext context)
+ {
+ InstSuatomB2 op = context.GetOp<InstSuatomB2>();
+
+ EmitSuatom(
+ context,
+ op.Dim,
+ op.Op,
+ op.Size,
+ 0,
+ op.SrcA,
+ op.SrcB,
+ op.SrcC,
+ op.Dest,
+ op.Ba,
+ isBindless: true,
+ compareAndSwap: false);
+ }
+
+ public static void SuatomCasB(EmitterContext context)
+ {
+ InstSuatomCasB op = context.GetOp<InstSuatomCasB>();
+
+ EmitSuatom(
+ context,
+ op.Dim,
+ 0,
+ op.Size,
+ 0,
+ op.SrcA,
+ op.SrcB,
+ op.SrcC,
+ op.Dest,
+ op.Ba,
+ isBindless: true,
+ compareAndSwap: true);
+ }
+
+ public static void SuatomCas(EmitterContext context)
+ {
+ InstSuatomCas op = context.GetOp<InstSuatomCas>();
+
+ EmitSuatom(
+ context,
+ op.Dim,
+ 0,
+ op.Size,
+ op.TidB,
+ op.SrcA,
+ op.SrcB,
+ 0,
+ op.Dest,
+ op.Ba,
+ isBindless: false,
+ compareAndSwap: true);
+ }
+
+ public static void SuldDB(EmitterContext context)
+ {
+ InstSuldDB op = context.GetOp<InstSuldDB>();
+
+ EmitSuld(context, op.Dim, op.Size, 0, 0, op.SrcA, op.Dest, op.SrcC, useComponents: false, op.Ba, isBindless: true);
+ }
+
+ public static void SuldD(EmitterContext context)
+ {
+ InstSuldD op = context.GetOp<InstSuldD>();
+
+ EmitSuld(context, op.Dim, op.Size, op.TidB, 0, op.SrcA, op.Dest, 0, useComponents: false, op.Ba, isBindless: false);
+ }
+
+ public static void SuldB(EmitterContext context)
+ {
+ InstSuldB op = context.GetOp<InstSuldB>();
+
+ EmitSuld(context, op.Dim, 0, 0, op.Rgba, op.SrcA, op.Dest, 0, useComponents: true, false, isBindless: true);
+ }
+
+ public static void Suld(EmitterContext context)
+ {
+ InstSuld op = context.GetOp<InstSuld>();
+
+ EmitSuld(context, op.Dim, 0, op.TidB, op.Rgba, op.SrcA, op.Dest, 0, useComponents: true, false, isBindless: false);
+ }
+
+ public static void SuredB(EmitterContext context)
+ {
+ InstSuredB op = context.GetOp<InstSuredB>();
+
+ EmitSured(context, op.Dim, op.Op, op.Size, 0, op.SrcA, op.Dest, op.SrcC, op.Ba, isBindless: true);
+ }
+
+ public static void Sured(EmitterContext context)
+ {
+ InstSured op = context.GetOp<InstSured>();
+
+ EmitSured(context, op.Dim, op.Op, op.Size, op.TidB, op.SrcA, op.Dest, 0, op.Ba, isBindless: false);
+ }
+
+ public static void SustDB(EmitterContext context)
+ {
+ InstSustDB op = context.GetOp<InstSustDB>();
+
+ EmitSust(context, op.Dim, op.Size, 0, 0, op.SrcA, op.Dest, op.SrcC, useComponents: false, op.Ba, isBindless: true);
+ }
+
+ public static void SustD(EmitterContext context)
+ {
+ InstSustD op = context.GetOp<InstSustD>();
+
+ EmitSust(context, op.Dim, op.Size, op.TidB, 0, op.SrcA, op.Dest, 0, useComponents: false, op.Ba, isBindless: false);
+ }
+
+ public static void SustB(EmitterContext context)
+ {
+ InstSustB op = context.GetOp<InstSustB>();
+
+ EmitSust(context, op.Dim, 0, 0, op.Rgba, op.SrcA, op.Dest, op.SrcC, useComponents: true, false, isBindless: true);
+ }
+
+ public static void Sust(EmitterContext context)
+ {
+ InstSust op = context.GetOp<InstSust>();
+
+ EmitSust(context, op.Dim, 0, op.TidB, op.Rgba, op.SrcA, op.Dest, 0, useComponents: true, false, isBindless: false);
+ }
+
+ private static void EmitSuatom(
+ EmitterContext context,
+ SuDim dimensions,
+ SuatomOp atomicOp,
+ SuatomSize size,
+ int imm,
+ int srcA,
+ int srcB,
+ int srcC,
+ int dest,
+ bool byteAddress,
+ bool isBindless,
+ bool compareAndSwap)
+ {
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid image atomic sampler type.");
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ Operand GetDest()
+ {
+ if (dest > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return Register(dest++, RegisterType.Gpr);
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(context.Copy(GetSrcReg(context, srcC)));
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(Const(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ if (type.HasFlag(SamplerType.Array))
+ {
+ sourcesList.Add(Ra());
+
+ type |= SamplerType.Array;
+ }
+
+ if (byteAddress)
+ {
+ int xIndex = isBindless ? 1 : 0;
+
+ sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(size)));
+ }
+
+ // TODO: FP and 64-bit formats.
+ TextureFormat format = !isBindless && (size == SuatomSize.Sd32 || size == SuatomSize.Sd64)
+ ? context.Config.GetTextureFormatAtomic(imm)
+ : GetTextureFormat(size);
+
+ if (compareAndSwap)
+ {
+ sourcesList.Add(Rb());
+ }
+
+ sourcesList.Add(Rb());
+
+ Operand[] sources = sourcesList.ToArray();
+
+ TextureFlags flags = compareAndSwap ? TextureFlags.CAS : GetAtomicOpFlags(atomicOp);
+
+ if (isBindless)
+ {
+ flags |= TextureFlags.Bindless;
+ }
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.ImageAtomic,
+ type,
+ format,
+ flags,
+ imm,
+ 0,
+ GetDest(),
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static void EmitSuld(
+ EmitterContext context,
+ SuDim dimensions,
+ SuSize size,
+ int imm,
+ SuRgba componentMask,
+ int srcA,
+ int srcB,
+ int srcC,
+ bool useComponents,
+ bool byteAddress,
+ bool isBindless)
+ {
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid image store sampler type.");
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(context.Copy(Register(srcC, RegisterType.Gpr)));
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(Const(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ if (type.HasFlag(SamplerType.Array))
+ {
+ sourcesList.Add(Ra());
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+
+ int handle = imm;
+
+ TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
+
+ if (useComponents)
+ {
+ for (int compMask = (int)componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ {
+ if ((compMask & 1) == 0)
+ {
+ continue;
+ }
+
+ if (srcB == RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ Operand rd = Register(srcB++, RegisterType.Gpr);
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.ImageLoad,
+ type,
+ flags,
+ handle,
+ compIndex,
+ rd,
+ sources);
+
+ if (!isBindless)
+ {
+ operation.Format = context.Config.GetTextureFormat(handle);
+ }
+
+ context.Add(operation);
+ }
+ }
+ else
+ {
+ if (byteAddress)
+ {
+ int xIndex = isBindless ? 1 : 0;
+
+ sources[xIndex] = context.ShiftRightS32(sources[xIndex], Const(GetComponentSizeInBytesLog2(size)));
+ }
+
+ int components = GetComponents(size);
+
+ for (int compIndex = 0; compIndex < components; compIndex++)
+ {
+ if (srcB == RegisterConsts.RegisterZeroIndex)
+ {
+ break;
+ }
+
+ Operand rd = Register(srcB++, RegisterType.Gpr);
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.ImageLoad,
+ type,
+ GetTextureFormat(size),
+ flags,
+ handle,
+ compIndex,
+ rd,
+ sources);
+
+ context.Add(operation);
+
+ switch (size)
+ {
+ case SuSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break;
+ case SuSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break;
+ case SuSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break;
+ case SuSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break;
+ }
+ }
+ }
+ }
+
+ private static void EmitSured(
+ EmitterContext context,
+ SuDim dimensions,
+ RedOp atomicOp,
+ SuatomSize size,
+ int imm,
+ int srcA,
+ int srcB,
+ int srcC,
+ bool byteAddress,
+ bool isBindless)
+ {
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid image reduction sampler type.");
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(context.Copy(GetSrcReg(context, srcC)));
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(Const(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ if (type.HasFlag(SamplerType.Array))
+ {
+ sourcesList.Add(Ra());
+
+ type |= SamplerType.Array;
+ }
+
+ if (byteAddress)
+ {
+ int xIndex = isBindless ? 1 : 0;
+
+ sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(size)));
+ }
+
+ // TODO: FP and 64-bit formats.
+ TextureFormat format = !isBindless && (size == SuatomSize.Sd32 || size == SuatomSize.Sd64)
+ ? context.Config.GetTextureFormatAtomic(imm)
+ : GetTextureFormat(size);
+
+ sourcesList.Add(Rb());
+
+ Operand[] sources = sourcesList.ToArray();
+
+ TextureFlags flags = GetAtomicOpFlags((SuatomOp)atomicOp);
+
+ if (isBindless)
+ {
+ flags |= TextureFlags.Bindless;
+ }
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.ImageAtomic,
+ type,
+ format,
+ flags,
+ imm,
+ 0,
+ null,
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static void EmitSust(
+ EmitterContext context,
+ SuDim dimensions,
+ SuSize size,
+ int imm,
+ SuRgba componentMask,
+ int srcA,
+ int srcB,
+ int srcC,
+ bool useComponents,
+ bool byteAddress,
+ bool isBindless)
+ {
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ if (type == SamplerType.None)
+ {
+ context.Config.GpuAccessor.Log("Invalid image store sampler type.");
+ return;
+ }
+
+ Operand Ra()
+ {
+ if (srcA > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
+ }
+
+ Operand Rb()
+ {
+ if (srcB > RegisterConsts.RegisterZeroIndex)
+ {
+ return Const(0);
+ }
+
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
+ }
+
+ List<Operand> sourcesList = new List<Operand>();
+
+ if (isBindless)
+ {
+ sourcesList.Add(context.Copy(Register(srcC, RegisterType.Gpr)));
+ }
+
+ int coordsCount = type.GetDimensions();
+
+ for (int index = 0; index < coordsCount; index++)
+ {
+ sourcesList.Add(Ra());
+ }
+
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ {
+ sourcesList.Add(Const(0));
+
+ type &= ~SamplerType.Mask;
+ type |= SamplerType.Texture2D;
+ }
+
+ if (type.HasFlag(SamplerType.Array))
+ {
+ sourcesList.Add(Ra());
+ }
+
+ TextureFormat format = TextureFormat.Unknown;
+
+ if (useComponents)
+ {
+ for (int compMask = (int)componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ {
+ if ((compMask & 1) != 0)
+ {
+ sourcesList.Add(Rb());
+ }
+ }
+
+ if (!isBindless)
+ {
+ format = context.Config.GetTextureFormat(imm);
+ }
+ }
+ else
+ {
+ if (byteAddress)
+ {
+ int xIndex = isBindless ? 1 : 0;
+
+ sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(size)));
+ }
+
+ int components = GetComponents(size);
+
+ for (int compIndex = 0; compIndex < components; compIndex++)
+ {
+ sourcesList.Add(Rb());
+ }
+
+ format = GetTextureFormat(size);
+ }
+
+ Operand[] sources = sourcesList.ToArray();
+
+ int handle = imm;
+
+ TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
+
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.ImageStore,
+ type,
+ format,
+ flags,
+ handle,
+ 0,
+ null,
+ sources);
+
+ context.Add(operation);
+ }
+
+ private static int GetComponentSizeInBytesLog2(SuatomSize size)
+ {
+ return size switch
+ {
+ SuatomSize.U32 => 2,
+ SuatomSize.S32 => 2,
+ SuatomSize.U64 => 3,
+ SuatomSize.F32FtzRn => 2,
+ SuatomSize.F16x2FtzRn => 2,
+ SuatomSize.S64 => 3,
+ SuatomSize.Sd32 => 2,
+ SuatomSize.Sd64 => 3,
+ _ => 2
+ };
+ }
+
+ private static TextureFormat GetTextureFormat(SuatomSize size)
+ {
+ return size switch
+ {
+ SuatomSize.U32 => TextureFormat.R32Uint,
+ SuatomSize.S32 => TextureFormat.R32Sint,
+ SuatomSize.U64 => TextureFormat.R32G32Uint,
+ SuatomSize.F32FtzRn => TextureFormat.R32Float,
+ SuatomSize.F16x2FtzRn => TextureFormat.R16G16Float,
+ SuatomSize.S64 => TextureFormat.R32G32Uint,
+ SuatomSize.Sd32 => TextureFormat.R32Uint,
+ SuatomSize.Sd64 => TextureFormat.R32G32Uint,
+ _ => TextureFormat.R32Uint
+ };
+ }
+
+ private static TextureFlags GetAtomicOpFlags(SuatomOp op)
+ {
+ return op switch
+ {
+ SuatomOp.Add => TextureFlags.Add,
+ SuatomOp.Min => TextureFlags.Minimum,
+ SuatomOp.Max => TextureFlags.Maximum,
+ SuatomOp.Inc => TextureFlags.Increment,
+ SuatomOp.Dec => TextureFlags.Decrement,
+ SuatomOp.And => TextureFlags.BitwiseAnd,
+ SuatomOp.Or => TextureFlags.BitwiseOr,
+ SuatomOp.Xor => TextureFlags.BitwiseXor,
+ SuatomOp.Exch => TextureFlags.Swap,
+ _ => TextureFlags.Add
+ };
+ }
+
+ private static int GetComponents(SuSize size)
+ {
+ return size switch
+ {
+ SuSize.B64 => 2,
+ SuSize.B128 => 4,
+ SuSize.UB128 => 4,
+ _ => 1
+ };
+ }
+
+ private static int GetComponentSizeInBytesLog2(SuSize size)
+ {
+ return size switch
+ {
+ SuSize.U8 => 0,
+ SuSize.S8 => 0,
+ SuSize.U16 => 1,
+ SuSize.S16 => 1,
+ SuSize.B32 => 2,
+ SuSize.B64 => 3,
+ SuSize.B128 => 4,
+ SuSize.UB128 => 4,
+ _ => 2
+ };
+ }
+
+ private static TextureFormat GetTextureFormat(SuSize size)
+ {
+ return size switch
+ {
+ SuSize.U8 => TextureFormat.R8Uint,
+ SuSize.S8 => TextureFormat.R8Sint,
+ SuSize.U16 => TextureFormat.R16Uint,
+ SuSize.S16 => TextureFormat.R16Sint,
+ SuSize.B32 => TextureFormat.R32Uint,
+ SuSize.B64 => TextureFormat.R32G32Uint,
+ SuSize.B128 => TextureFormat.R32G32B32A32Uint,
+ SuSize.UB128 => TextureFormat.R32G32B32A32Uint,
+ _ => TextureFormat.R32Uint
+ };
+ }
+
+ private static SamplerType ConvertSamplerType(SuDim target)
+ {
+ return target switch
+ {
+ SuDim._1d => SamplerType.Texture1D,
+ SuDim._1dBuffer => SamplerType.TextureBuffer,
+ SuDim._1dArray => SamplerType.Texture1D | SamplerType.Array,
+ SuDim._2d => SamplerType.Texture2D,
+ SuDim._2dArray => SamplerType.Texture2D | SamplerType.Array,
+ SuDim._3d => SamplerType.Texture3D,
+ _ => SamplerType.None
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
index 88bafd76..ba1fdf17 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs
@@ -4,295 +4,181 @@ using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
-using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
namespace Ryujinx.Graphics.Shader.Instructions
{
static partial class InstEmit
{
- private const bool Sample1DAs2D = true;
-
- public static void Suld(EmitterContext context)
+ private static readonly int[,] _maskLut = new int[,]
{
- context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
-
- OpCodeImage op = (OpCodeImage)context.CurrOp;
-
- SamplerType type = ConvertSamplerType(op.Dimensions);
-
- if (type == SamplerType.None)
- {
- context.Config.GpuAccessor.Log("Invalid image store sampler type.");
-
- return;
- }
-
- // Rb is Rd on the SULD instruction.
- int rdIndex = op.Rb.Index;
- int raIndex = op.Ra.Index;
-
- Operand Ra()
- {
- if (raIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
-
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
- }
-
- List<Operand> sourcesList = new List<Operand>();
-
- if (op.IsBindless)
- {
- sourcesList.Add(context.Copy(Register(op.Rc)));
- }
-
- int coordsCount = type.GetDimensions();
-
- for (int index = 0; index < coordsCount; index++)
- {
- sourcesList.Add(Ra());
- }
-
- if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
- {
- sourcesList.Add(Const(0));
-
- type &= ~SamplerType.Mask;
- type |= SamplerType.Texture2D;
- }
+ { 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 },
+ { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 }
+ };
- if (type.HasFlag(SamplerType.Array))
- {
- sourcesList.Add(Ra());
-
- type |= SamplerType.Array;
- }
-
- Operand[] sources = sourcesList.ToArray();
-
- int handle = !op.IsBindless ? op.HandleOffset : 0;
-
- TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None;
-
- if (op.UseComponents)
- {
- int componentMask = (int)op.Components;
-
- for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
- {
- if ((compMask & 1) == 0)
- {
- continue;
- }
-
- if (rdIndex == RegisterConsts.RegisterZeroIndex)
- {
- break;
- }
-
- Operand rd = Register(rdIndex++, RegisterType.Gpr);
-
- TextureOperation operation = context.CreateTextureOperation(
- Instruction.ImageLoad,
- type,
- flags,
- handle,
- compIndex,
- rd,
- sources);
+ private const bool Sample1DAs2D = true;
- if (!op.IsBindless)
- {
- operation.Format = context.Config.GetTextureFormat(handle);
- }
+ private enum TexsType
+ {
+ Texs,
+ Tlds,
+ Tld4s
+ }
- context.Add(operation);
- }
- }
- else
- {
- if (op.ByteAddress)
- {
- int xIndex = op.IsBindless ? 1 : 0;
+ public static void Tex(EmitterContext context)
+ {
+ InstTex op = context.GetOp<InstTex>();
- sources[xIndex] = context.ShiftRightS32(sources[xIndex], Const(GetComponentSizeInBytesLog2(op.Size)));
- }
+ EmitTex(context, TextureFlags.None, op.Dim, op.Lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffi);
+ }
- int components = GetComponents(op.Size);
+ public static void TexB(EmitterContext context)
+ {
+ InstTexB op = context.GetOp<InstTexB>();
- for (int compIndex = 0; compIndex < components; compIndex++)
- {
- if (rdIndex == RegisterConsts.RegisterZeroIndex)
- {
- break;
- }
+ EmitTex(context, TextureFlags.Bindless, op.Dim, op.Lodb, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, false, op.Dc, op.Aoffib);
+ }
- Operand rd = Register(rdIndex++, RegisterType.Gpr);
+ public static void Texs(EmitterContext context)
+ {
+ InstTexs op = context.GetOp<InstTexs>();
- TextureOperation operation = context.CreateTextureOperation(
- Instruction.ImageLoad,
- type,
- GetTextureFormat(op.Size),
- flags,
- handle,
- compIndex,
- rd,
- sources);
+ EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
- context.Add(operation);
+ public static void TexsF16(EmitterContext context)
+ {
+ InstTexs op = context.GetOp<InstTexs>();
- switch (op.Size)
- {
- case IntegerSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break;
- case IntegerSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break;
- case IntegerSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break;
- case IntegerSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break;
- }
- }
- }
+ EmitTexs(context, TexsType.Texs, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
}
- public static void Sust(EmitterContext context)
+ public static void Tld(EmitterContext context)
{
- OpCodeImage op = (OpCodeImage)context.CurrOp;
+ InstTld op = context.GetOp<InstTld>();
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
- if (type == SamplerType.None)
- {
- context.Config.GpuAccessor.Log("Invalid image store sampler type.");
+ var lod = op.Lod ? Lod.Ll : Lod.Lz;
- return;
- }
+ EmitTex(context, TextureFlags.IntCoords, op.Dim, lod, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
+ }
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
+ public static void TldB(EmitterContext context)
+ {
+ InstTldB op = context.GetOp<InstTldB>();
- Operand Ra()
- {
- if (raIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
+ context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
- }
+ var flags = TextureFlags.IntCoords | TextureFlags.Bindless;
+ var lod = op.Lod ? Lod.Ll : Lod.Lz;
- Operand Rb()
- {
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
+ EmitTex(context, flags, op.Dim, lod, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Ms, false, op.Toff);
+ }
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
- }
+ public static void Tlds(EmitterContext context)
+ {
+ InstTlds op = context.GetOp<InstTlds>();
- List<Operand> sourcesList = new List<Operand>();
+ EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
- if (op.IsBindless)
- {
- sourcesList.Add(context.Copy(Register(op.Rc)));
- }
+ public static void TldsF16(EmitterContext context)
+ {
+ InstTlds op = context.GetOp<InstTlds>();
- int coordsCount = type.GetDimensions();
+ EmitTexs(context, TexsType.Tlds, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
+ }
- for (int index = 0; index < coordsCount; index++)
- {
- sourcesList.Add(Ra());
- }
+ public static void Tld4(EmitterContext context)
+ {
+ InstTld4 op = context.GetOp<InstTld4>();
- if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
- {
- sourcesList.Add(Const(0));
+ EmitTld4(context, op.Dim, op.TexComp, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: false);
+ }
- type &= ~SamplerType.Mask;
- type |= SamplerType.Texture2D;
- }
+ public static void Tld4B(EmitterContext context)
+ {
+ InstTld4B op = context.GetOp<InstTld4B>();
- if (type.HasFlag(SamplerType.Array))
- {
- sourcesList.Add(Ra());
+ EmitTld4(context, op.Dim, op.TexComp, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, op.Dc, isBindless: true);
+ }
- type |= SamplerType.Array;
- }
+ public static void Tld4s(EmitterContext context)
+ {
+ InstTld4s op = context.GetOp<InstTld4s>();
- TextureFormat format = TextureFormat.Unknown;
+ EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: false);
+ }
- if (op.UseComponents)
- {
- int componentMask = (int)op.Components;
+ public static void Tld4sF16(EmitterContext context)
+ {
+ InstTld4s op = context.GetOp<InstTld4s>();
- for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
- {
- if ((compMask & 1) != 0)
- {
- sourcesList.Add(Rb());
- }
- }
+ EmitTexs(context, TexsType.Tld4s, op.TidB, 4, op.SrcA, op.SrcB, op.Dest, op.Dest2, isF16: true);
+ }
- if (!op.IsBindless)
- {
- format = context.Config.GetTextureFormat(op.HandleOffset);
- }
- }
- else
- {
- if (op.ByteAddress)
- {
- int xIndex = op.IsBindless ? 1 : 0;
+ public static void Tmml(EmitterContext context)
+ {
+ InstTmml op = context.GetOp<InstTmml>();
- sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(op.Size)));
- }
+ EmitTmml(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: false);
+ }
- int components = GetComponents(op.Size);
+ public static void TmmlB(EmitterContext context)
+ {
+ InstTmmlB op = context.GetOp<InstTmmlB>();
- for (int compIndex = 0; compIndex < components; compIndex++)
- {
- sourcesList.Add(Rb());
- }
+ EmitTmml(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, isBindless: true);
+ }
- format = GetTextureFormat(op.Size);
- }
+ public static void Txd(EmitterContext context)
+ {
+ InstTxd op = context.GetOp<InstTxd>();
- Operand[] sources = sourcesList.ToArray();
+ EmitTxd(context, op.Dim, op.TidB, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: false);
+ }
- int handle = !op.IsBindless ? op.HandleOffset : 0;
+ public static void TxdB(EmitterContext context)
+ {
+ InstTxdB op = context.GetOp<InstTxdB>();
- TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None;
+ EmitTxd(context, op.Dim, 0, op.WMask, op.SrcA, op.SrcB, op.Dest, op.Toff, isBindless: true);
+ }
- TextureOperation operation = context.CreateTextureOperation(
- Instruction.ImageStore,
- type,
- format,
- flags,
- handle,
- 0,
- null,
- sources);
+ public static void Txq(EmitterContext context)
+ {
+ InstTxq op = context.GetOp<InstTxq>();
- context.Add(operation);
+ EmitTxq(context, op.TexQuery, op.TidB, op.WMask, op.SrcA, op.Dest, isBindless: false);
}
- public static void Sured(EmitterContext context)
+ public static void TxqB(EmitterContext context)
{
- OpCodeSured op = (OpCodeSured)context.CurrOp;
+ InstTxqB op = context.GetOp<InstTxqB>();
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ EmitTxq(context, op.TexQuery, 0, op.WMask, op.SrcA, op.Dest, isBindless: true);
+ }
- if (type == SamplerType.None)
+ private static void EmitTex(
+ EmitterContext context,
+ TextureFlags flags,
+ TexDim dimensions,
+ Lod lodMode,
+ int imm,
+ int componentMask,
+ int raIndex,
+ int rbIndex,
+ int rdIndex,
+ bool isMultisample,
+ bool hasDepthCompare,
+ bool hasOffset)
+ {
+ if (rdIndex == RegisterConsts.RegisterZeroIndex)
{
- context.Config.GpuAccessor.Log("Invalid image reduction sampler type.");
-
return;
}
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
Operand Ra()
{
if (raIndex > RegisterConsts.RegisterZeroIndex)
@@ -313,269 +199,179 @@ namespace Ryujinx.Graphics.Shader.Instructions
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
}
+ SamplerType type = ConvertSamplerType(dimensions);
+
+ bool isArray = type.HasFlag(SamplerType.Array);
+ bool isBindless = flags.HasFlag(TextureFlags.Bindless);
+
+ Operand arrayIndex = isArray ? Ra() : null;
+
List<Operand> sourcesList = new List<Operand>();
- if (op.IsBindless)
+ if (isBindless)
{
- sourcesList.Add(context.Copy(Register(op.Rc)));
+ sourcesList.Add(Rb());
}
- int coordsCount = type.GetDimensions();
+ bool hasLod = lodMode > Lod.Lz;
- for (int index = 0; index < coordsCount; index++)
+ if (type == SamplerType.Texture1D && (flags & ~TextureFlags.Bindless) == TextureFlags.IntCoords && !(
+ hasLod ||
+ hasDepthCompare ||
+ hasOffset ||
+ isArray ||
+ isMultisample))
{
- sourcesList.Add(Ra());
+ // For bindless, we don't have any way to know the texture type,
+ // so we assume it's texture buffer when the sampler type is 1D, since that's more common.
+ bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QuerySamplerType(imm) == SamplerType.TextureBuffer;
+ if (isTypeBuffer)
+ {
+ type = SamplerType.TextureBuffer;
+ }
}
- if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
- {
- sourcesList.Add(Const(0));
-
- type &= ~SamplerType.Mask;
- type |= SamplerType.Texture2D;
- }
+ int coordsCount = type.GetDimensions();
- if (type.HasFlag(SamplerType.Array))
+ for (int index = 0; index < coordsCount; index++)
{
sourcesList.Add(Ra());
-
- type |= SamplerType.Array;
}
- TextureFormat format = TextureFormat.R32Sint;
+ bool is1DTo2D = false;
- if (op.UseType)
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
{
- if (op.ByteAddress)
- {
- int xIndex = op.IsBindless ? 1 : 0;
-
- sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(op.Type)));
- }
+ sourcesList.Add(ConstF(0));
- // TODO: FP and 64-bit formats.
- format = (op.Type == ReductionType.SD32 || op.Type == ReductionType.SD64) ?
- context.Config.GetTextureFormatAtomic(op.HandleOffset) :
- GetTextureFormat(op.Type);
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
+ is1DTo2D = true;
}
- else if (!op.IsBindless)
- {
- format = context.Config.GetTextureFormatAtomic(op.HandleOffset);
- }
-
- sourcesList.Add(Rb());
-
- Operand[] sources = sourcesList.ToArray();
-
- int handle = op.HandleOffset;
-
- TextureFlags flags = GetAtomicOpFlags(op.AtomicOp);
- if (op.IsBindless)
+ if (isArray)
{
- handle = 0;
- flags |= TextureFlags.Bindless;
+ sourcesList.Add(arrayIndex);
}
- TextureOperation operation = context.CreateTextureOperation(
- Instruction.ImageAtomic,
- type,
- format,
- flags,
- handle,
- 0,
- null,
- sources);
-
- context.Add(operation);
- }
-
- public static void Suatom(EmitterContext context)
- {
- OpCodeSuatom op = (OpCodeSuatom)context.CurrOp;
+ Operand lodValue = hasLod ? Rb() : ConstF(0);
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ Operand packedOffs = hasOffset ? Rb() : null;
- if (type == SamplerType.None)
+ if (hasDepthCompare)
{
- context.Config.GpuAccessor.Log("Invalid image atomic sampler type.");
+ sourcesList.Add(Rb());
- return;
+ type |= SamplerType.Shadow;
}
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
- Operand Ra()
+ if ((lodMode == Lod.Lz ||
+ lodMode == Lod.Ll ||
+ lodMode == Lod.Lla) && !isMultisample && type != SamplerType.TextureBuffer)
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
+ sourcesList.Add(lodValue);
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ flags |= TextureFlags.LodLevel;
}
- Operand Rb()
+ if (hasOffset)
{
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ for (int index = 0; index < coordsCount; index++)
{
- return Const(0);
+ sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
}
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
- }
-
- int rdIndex = op.Rd.Index;
-
- Operand GetDest()
- {
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
+ if (is1DTo2D)
{
- return Const(0);
+ sourcesList.Add(Const(0));
}
- return Register(rdIndex++, RegisterType.Gpr);
- }
-
- List<Operand> sourcesList = new List<Operand>();
-
- if (op.IsBindless)
- {
- sourcesList.Add(context.Copy(Register(op.Rc)));
- }
-
- int coordsCount = type.GetDimensions();
-
- for (int index = 0; index < coordsCount; index++)
- {
- sourcesList.Add(Ra());
+ flags |= TextureFlags.Offset;
}
- if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
+ if (lodMode == Lod.Lb || lodMode == Lod.Lba)
{
- sourcesList.Add(Const(0));
+ sourcesList.Add(lodValue);
- type &= ~SamplerType.Mask;
- type |= SamplerType.Texture2D;
+ flags |= TextureFlags.LodBias;
}
- if (type.HasFlag(SamplerType.Array))
+ if (isMultisample)
{
- sourcesList.Add(Ra());
+ sourcesList.Add(Rb());
- type |= SamplerType.Array;
+ type |= SamplerType.Multisample;
}
- TextureFormat format = TextureFormat.R32Sint;
+ Operand[] sources = sourcesList.ToArray();
- if (op.UseType)
+ Operand GetDest()
{
- if (op.ByteAddress)
+ if (rdIndex > RegisterConsts.RegisterZeroIndex)
{
- int xIndex = op.IsBindless ? 1 : 0;
-
- sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(op.Type)));
+ return Const(0);
}
- // TODO: FP and 64-bit formats.
- format = (op.Type == ReductionType.SD32 || op.Type == ReductionType.SD64) ?
- context.Config.GetTextureFormatAtomic(op.HandleOffset) :
- GetTextureFormat(op.Type);
- }
- else if (!op.IsBindless)
- {
- format = context.Config.GetTextureFormatAtomic(op.HandleOffset);
- }
-
- if (op.CompareAndSwap)
- {
- sourcesList.Add(Rb());
+ return Register(rdIndex++, RegisterType.Gpr);
}
- sourcesList.Add(Rb());
-
- Operand[] sources = sourcesList.ToArray();
-
- int handle = op.HandleOffset;
-
- TextureFlags flags = op.CompareAndSwap ? TextureFlags.CAS : GetAtomicOpFlags(op.AtomicOp);
+ int handle = !isBindless ? imm : 0;
- if (op.IsBindless)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
- handle = 0;
- flags |= TextureFlags.Bindless;
- }
-
- TextureOperation operation = context.CreateTextureOperation(
- Instruction.ImageAtomic,
- type,
- format,
- flags,
- handle,
- 0,
- GetDest(),
- sources);
-
- context.Add(operation);
- }
-
- public static void Tex(EmitterContext context)
- {
- EmitTextureSample(context, TextureFlags.None);
- }
-
- public static void TexB(EmitterContext context)
- {
- EmitTextureSample(context, TextureFlags.Bindless);
- }
-
- public static void Tld(EmitterContext context)
- {
- context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
-
- EmitTextureSample(context, TextureFlags.IntCoords);
- }
+ if ((compMask & 1) != 0)
+ {
+ Operand dest = GetDest();
- public static void TldB(EmitterContext context)
- {
- context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
+ TextureOperation operation = context.CreateTextureOperation(
+ Instruction.TextureSample,
+ type,
+ flags,
+ handle,
+ compIndex,
+ dest,
+ sources);
- EmitTextureSample(context, TextureFlags.IntCoords | TextureFlags.Bindless);
+ context.Add(operation);
+ }
+ }
}
- public static void Texs(EmitterContext context)
+ private static void EmitTexs(
+ EmitterContext context,
+ TexsType texsType,
+ int imm,
+ int writeMask,
+ int srcA,
+ int srcB,
+ int dest,
+ int dest2,
+ bool isF16)
{
- OpCodeTextureScalar op = (OpCodeTextureScalar)context.CurrOp;
-
- if (op.Rd0.IsRZ && op.Rd1.IsRZ)
+ if (dest == RegisterConsts.RegisterZeroIndex && dest2 == RegisterConsts.RegisterZeroIndex)
{
return;
}
List<Operand> sourcesList = new List<Operand>();
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
Operand Ra()
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcA > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
}
Operand Rb()
{
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcB > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
}
void AddTextureOffset(int coordsCount, int stride, int size)
@@ -588,17 +384,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- SamplerType type;
+ SamplerType type;
TextureFlags flags;
- if (op is OpCodeTexs texsOp)
+ if (texsType == TexsType.Texs)
{
+ var texsOp = context.GetOp<InstTexs>();
+
type = ConvertSamplerType(texsOp.Target);
if (type == SamplerType.None)
{
context.Config.GpuAccessor.Log("Invalid texture sampler type.");
-
return;
}
@@ -630,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
switch (texsOp.Target)
{
- case TextureTarget.Texture1DLodZero:
+ case TexsTarget.Texture1DLodZero:
sourcesList.Add(Ra());
if (Sample1DAs2D)
@@ -644,36 +441,36 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(ConstF(0));
break;
- case TextureTarget.Texture2D:
+ case TexsTarget.Texture2D:
sourcesList.Add(Ra());
sourcesList.Add(Rb());
break;
- case TextureTarget.Texture2DLodZero:
+ case TexsTarget.Texture2DLodZero:
sourcesList.Add(Ra());
sourcesList.Add(Rb());
sourcesList.Add(ConstF(0));
break;
- case TextureTarget.Texture2DLodLevel:
- case TextureTarget.Texture2DDepthCompare:
- case TextureTarget.Texture3D:
- case TextureTarget.TextureCube:
+ case TexsTarget.Texture2DLodLevel:
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture3D:
+ case TexsTarget.TextureCube:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Rb());
break;
- case TextureTarget.Texture2DLodZeroDepthCompare:
- case TextureTarget.Texture3DLodZero:
+ case TexsTarget.Texture2DLodZeroDepthCompare:
+ case TexsTarget.Texture3DLodZero:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Rb());
sourcesList.Add(ConstF(0));
break;
- case TextureTarget.Texture2DLodLevelDepthCompare:
- case TextureTarget.TextureCubeLodLevel:
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.TextureCubeLodLevel:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Rb());
@@ -682,14 +479,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
}
- else if (op is OpCodeTlds tldsOp)
+ else if (texsType == TexsType.Tlds)
{
+ var tldsOp = context.GetOp<InstTlds>();
+
type = ConvertSamplerType(tldsOp.Target);
if (type == SamplerType.None)
{
context.Config.GpuAccessor.Log("Invalid texel fetch sampler type.");
-
return;
}
@@ -697,15 +495,16 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords;
- if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.GpuAccessor.QuerySamplerType(tldsOp.HandleOffset) == SamplerType.TextureBuffer)
+ if (tldsOp.Target == TldsTarget.Texture1DLodZero &&
+ context.Config.GpuAccessor.QuerySamplerType(tldsOp.TidB) == SamplerType.TextureBuffer)
{
- type = SamplerType.TextureBuffer;
+ type = SamplerType.TextureBuffer;
flags &= ~TextureFlags.LodLevel;
}
switch (tldsOp.Target)
{
- case TexelLoadTarget.Texture1DLodZero:
+ case TldsTarget.Texture1DLodZero:
sourcesList.Add(Ra());
if (type != SamplerType.TextureBuffer)
@@ -722,7 +521,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
break;
- case TexelLoadTarget.Texture1DLodLevel:
+ case TldsTarget.Texture1DLodLevel:
sourcesList.Add(Ra());
if (Sample1DAs2D)
@@ -736,34 +535,34 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Rb());
break;
- case TexelLoadTarget.Texture2DLodZero:
+ case TldsTarget.Texture2DLodZero:
sourcesList.Add(Ra());
sourcesList.Add(Rb());
sourcesList.Add(Const(0));
break;
- case TexelLoadTarget.Texture2DLodZeroOffset:
+ case TldsTarget.Texture2DLodZeroOffset:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Const(0));
break;
- case TexelLoadTarget.Texture2DLodZeroMultisample:
- case TexelLoadTarget.Texture2DLodLevel:
- case TexelLoadTarget.Texture2DLodLevelOffset:
+ case TldsTarget.Texture2DLodZeroMultisample:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodLevelOffset:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Rb());
break;
- case TexelLoadTarget.Texture3DLodZero:
+ case TldsTarget.Texture3DLodZero:
sourcesList.Add(Ra());
sourcesList.Add(Ra());
sourcesList.Add(Rb());
sourcesList.Add(Const(0));
break;
- case TexelLoadTarget.Texture2DArrayLodZero:
+ case TldsTarget.Texture2DArrayLodZero:
sourcesList.Add(Rb());
sourcesList.Add(Rb());
sourcesList.Add(Ra());
@@ -776,9 +575,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
AddTextureOffset(type.GetDimensions(), 4, 4);
}
}
- else if (op is OpCodeTld4s tld4sOp)
+ else if (texsType == TexsType.Tld4s)
{
- if (!(tld4sOp.HasDepthCompare || tld4sOp.HasOffset))
+ var tld4sOp = context.GetOp<InstTld4s>();
+
+ if (!(tld4sOp.Dc || tld4sOp.Aoffi))
{
sourcesList.Add(Ra());
sourcesList.Add(Rb());
@@ -789,28 +590,28 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Ra());
}
- type = SamplerType.Texture2D;
+ type = SamplerType.Texture2D;
flags = TextureFlags.Gather;
- if (tld4sOp.HasDepthCompare)
+ if (tld4sOp.Dc)
{
sourcesList.Add(Rb());
type |= SamplerType.Shadow;
}
- if (tld4sOp.HasOffset)
+ if (tld4sOp.Aoffi)
{
AddTextureOffset(type.GetDimensions(), 8, 6);
flags |= TextureFlags.Offset;
}
- sourcesList.Add(Const(tld4sOp.GatherCompIndex));
+ sourcesList.Add(Const((int)tld4sOp.TexComp));
}
else
{
- throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\".");
+ throw new ArgumentException($"Invalid TEXS type \"{texsType}\".");
}
Operand[] sources = sourcesList.ToArray();
@@ -823,11 +624,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand GetDest()
{
int high = destIncrement >> 1;
- int low = destIncrement & 1;
+ int low = destIncrement & 1;
destIncrement++;
- if (op.IsFp16)
+ if (isF16)
{
return high != 0
? (rd1[low] = Local())
@@ -835,7 +636,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
else
{
- int rdIndex = high != 0 ? op.Rd1.Index : op.Rd0.Index;
+ int rdIndex = high != 0 ? dest2 : dest;
if (rdIndex < RegisterConsts.RegisterZeroIndex)
{
@@ -846,74 +647,85 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- int handle = op.HandleOffset;
+ int handle = imm;
+ int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
- Operand dest = GetDest();
-
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
- dest,
+ GetDest(),
sources);
context.Add(operation);
}
}
- if (op.IsFp16)
+ if (isF16)
{
- context.Copy(Register(op.Rd0), context.PackHalf2x16(rd0[0], rd0[1]));
- context.Copy(Register(op.Rd1), context.PackHalf2x16(rd1[0], rd1[1]));
+ context.Copy(Register(dest, RegisterType.Gpr), context.PackHalf2x16(rd0[0], rd0[1]));
+ context.Copy(Register(dest2, RegisterType.Gpr), context.PackHalf2x16(rd1[0], rd1[1]));
}
}
- public static void Tld4(EmitterContext context)
+ private static void EmitTld4(
+ EmitterContext context,
+ TexDim dimensions,
+ TexComp component,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ TexOffset offset,
+ bool hasDepthCompare,
+ bool isBindless)
{
- IOpCodeTld4 op = (IOpCodeTld4)context.CurrOp;
-
- if (op.Rd.IsRZ)
+ if (dest == RegisterConsts.RegisterZeroIndex)
{
return;
}
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
Operand Ra()
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcA > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
}
Operand Rb()
{
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcB > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
}
- Operand arrayIndex = op.IsArray ? Ra() : null;
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ Operand arrayIndex = isArray ? Ra() : null;
List<Operand> sourcesList = new List<Operand>();
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ SamplerType type = ConvertSamplerType(dimensions);
TextureFlags flags = TextureFlags.Gather;
- if (op.Bindless)
+ if (isBindless)
{
sourcesList.Add(Rb());
@@ -927,37 +739,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Ra());
}
- bool is1DTo2D = Sample1DAs2D && type == SamplerType.Texture1D;
+ bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
if (is1DTo2D)
{
sourcesList.Add(ConstF(0));
- type = SamplerType.Texture2D;
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
}
- if (op.IsArray)
+ if (isArray)
{
sourcesList.Add(arrayIndex);
-
- type |= SamplerType.Array;
}
Operand[] packedOffs = new Operand[2];
- packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null;
- packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null;
+ bool hasAnyOffset = offset == TexOffset.Aoffi || offset == TexOffset.Ptp;
+
+ packedOffs[0] = hasAnyOffset ? Rb() : null;
+ packedOffs[1] = offset == TexOffset.Ptp ? Rb() : null;
- if (op.HasDepthCompare)
+ if (hasDepthCompare)
{
sourcesList.Add(Rb());
type |= SamplerType.Shadow;
}
- if (op.Offset != TextureGatherOffset.None)
+ if (hasAnyOffset)
{
- int offsetTexelsCount = op.Offset == TextureGatherOffset.Offsets ? 4 : 1;
+ int offsetTexelsCount = offset == TexOffset.Ptp ? 4 : 1;
for (int index = 0; index < coordsCount * offsetTexelsCount; index++)
{
@@ -974,42 +786,36 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- flags |= op.Offset == TextureGatherOffset.Offsets
- ? TextureFlags.Offsets
- : TextureFlags.Offset;
+ flags |= offset == TexOffset.Ptp ? TextureFlags.Offsets : TextureFlags.Offset;
}
- sourcesList.Add(Const(op.GatherCompIndex));
+ sourcesList.Add(Const((int)component));
Operand[] sources = sourcesList.ToArray();
- int rdIndex = op.Rd.Index;
-
Operand GetDest()
{
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
+ if (dest > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return Register(rdIndex++, RegisterType.Gpr);
+ return Register(dest++, RegisterType.Gpr);
}
- int handle = op.HandleOffset;
+ int handle = imm;
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
- Operand dest = GetDest();
-
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
- dest,
+ GetDest(),
sources);
context.Add(operation);
@@ -1017,46 +823,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- public static void TmmlB(EmitterContext context)
+ private static void EmitTmml(
+ EmitterContext context,
+ TexDim dimensions,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ bool isBindless)
{
- EmitTextureMipMapLevel(context, true);
- }
-
- public static void Tmml(EmitterContext context)
- {
- EmitTextureMipMapLevel(context, false);
- }
-
- private static void EmitTextureMipMapLevel(EmitterContext context, bool isBindless)
- {
- OpCodeTexture op = (OpCodeTexture)context.CurrOp;
-
- if (op.Rd.IsRZ)
+ if (dest == RegisterConsts.RegisterZeroIndex)
{
return;
}
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
Operand Ra()
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcA > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
}
Operand Rb()
{
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcB > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
}
TextureFlags flags = TextureFlags.None;
@@ -1070,58 +869,58 @@ namespace Ryujinx.Graphics.Shader.Instructions
flags |= TextureFlags.Bindless;
}
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ SamplerType type = ConvertSamplerType(dimensions);
int coordsCount = type.GetDimensions();
- Operand arrayIndex = op.IsArray ? Ra() : null;
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ Operand arrayIndex = isArray ? Ra() : null;
for (int index = 0; index < coordsCount; index++)
{
sourcesList.Add(Ra());
}
- if (Sample1DAs2D && type == SamplerType.Texture1D)
+ if (Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D)
{
sourcesList.Add(ConstF(0));
- type = SamplerType.Texture2D;
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
}
- if (op.IsArray)
+ if (isArray)
{
sourcesList.Add(arrayIndex);
-
- type |= SamplerType.Array;
}
Operand[] sources = sourcesList.ToArray();
- int rdIndex = op.Rd.Index;
-
Operand GetDest()
{
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
+ if (dest > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return Register(rdIndex++, RegisterType.Gpr);
+ return Register(dest++, RegisterType.Gpr);
}
- int handle = !isBindless ? op.HandleOffset : 0;
+ int handle = imm;
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
- Operand dest = GetDest();
-
// Components z and w aren't standard, we return 0 in this case and add a comment.
if (compIndex >= 2)
{
context.Add(new CommentNode("Unsupported component z or w found"));
- context.Copy(dest, Const(0));
+ context.Copy(GetDest(), Const(0));
}
else
{
@@ -1142,56 +941,60 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand fixedPointValue = context.FPConvertToS32(tempDest);
- context.Copy(dest, fixedPointValue);
+ context.Copy(GetDest(), fixedPointValue);
}
}
}
}
- public static void Txd(EmitterContext context)
+ private static void EmitTxd(
+ EmitterContext context,
+ TexDim dimensions,
+ int imm,
+ int componentMask,
+ int srcA,
+ int srcB,
+ int dest,
+ bool hasOffset,
+ bool isBindless)
{
- OpCodeTxd op = (OpCodeTxd)context.CurrOp;
-
- if (op.Rd.IsRZ)
+ if (dest == RegisterConsts.RegisterZeroIndex)
{
return;
}
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
Operand Ra()
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcA > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
}
Operand Rb()
{
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcB > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcB++, RegisterType.Gpr));
}
TextureFlags flags = TextureFlags.Derivatives;
List<Operand> sourcesList = new List<Operand>();
- if (op.IsBindless)
+ if (isBindless)
{
sourcesList.Add(Ra());
flags |= TextureFlags.Bindless;
}
- SamplerType type = ConvertSamplerType(op.Dimensions);
+ SamplerType type = ConvertSamplerType(dimensions);
int coordsCount = type.GetDimensions();
@@ -1200,22 +1003,26 @@ namespace Ryujinx.Graphics.Shader.Instructions
sourcesList.Add(Ra());
}
- bool is1DTo2D = Sample1DAs2D && type == SamplerType.Texture1D;
+ bool is1DTo2D = Sample1DAs2D && (type & SamplerType.Mask) == SamplerType.Texture1D;
if (is1DTo2D)
{
sourcesList.Add(ConstF(0));
- type = SamplerType.Texture2D;
+ type = SamplerType.Texture2D | (type & SamplerType.Array);
}
Operand packedParams = Ra();
- if (op.IsArray)
+ bool isArray =
+ dimensions == TexDim.Array1d ||
+ dimensions == TexDim.Array2d ||
+ dimensions == TexDim.Array3d ||
+ dimensions == TexDim.ArrayCube;
+
+ if (isArray)
{
sourcesList.Add(context.BitwiseAnd(packedParams, Const(0xffff)));
-
- type |= SamplerType.Array;
}
// Derivatives (X and Y).
@@ -1229,7 +1036,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- if (op.HasOffset)
+ if (hasOffset)
{
for (int index = 0; index < coordsCount; index++)
{
@@ -1246,33 +1053,29 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand[] sources = sourcesList.ToArray();
- int rdIndex = op.Rd.Index;
-
Operand GetDest()
{
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
+ if (dest > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return Register(rdIndex++, RegisterType.Gpr);
+ return Register(dest++, RegisterType.Gpr);
}
- int handle = !op.IsBindless ? op.HandleOffset : 0;
+ int handle = imm;
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
- Operand dest = GetDest();
-
TextureOperation operation = context.CreateTextureOperation(
Instruction.TextureSample,
type,
flags,
handle,
compIndex,
- dest,
+ GetDest(),
sources);
context.Add(operation);
@@ -1280,49 +1083,39 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- public static void Txq(EmitterContext context)
- {
- EmitTextureQuery(context, bindless: false);
- }
-
- public static void TxqB(EmitterContext context)
+ private static void EmitTxq(
+ EmitterContext context,
+ TexQuery query,
+ int imm,
+ int componentMask,
+ int srcA,
+ int dest,
+ bool isBindless)
{
- EmitTextureQuery(context, bindless: true);
- }
-
- private static void EmitTextureQuery(EmitterContext context, bool bindless)
- {
- OpCodeTex op = (OpCodeTex)context.CurrOp;
-
- if (op.Rd.IsRZ)
+ if (dest == RegisterConsts.RegisterZeroIndex)
{
return;
}
context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
- TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6);
-
- // TODO: Validate and use property.
+ // TODO: Validate and use query.
Instruction inst = Instruction.TextureSize;
-
- TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None;
-
- int raIndex = op.Ra.Index;
+ TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
Operand Ra()
{
- if (raIndex > RegisterConsts.RegisterZeroIndex)
+ if (srcA > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
+ return context.Copy(Register(srcA++, RegisterType.Gpr));
}
List<Operand> sourcesList = new List<Operand>();
- if (bindless)
+ if (isBindless)
{
sourcesList.Add(Ra());
}
@@ -1331,209 +1124,38 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand[] sources = sourcesList.ToArray();
- int rdIndex = op.Rd.Index;
-
Operand GetDest()
{
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
+ if (dest > RegisterConsts.RegisterZeroIndex)
{
return Const(0);
}
- return Register(rdIndex++, RegisterType.Gpr);
+ return Register(dest++, RegisterType.Gpr);
}
- int handle = !bindless ? op.HandleOffset : 0;
-
SamplerType type;
- if (bindless)
- {
- type = (op.ComponentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
- }
- else
- {
- type = context.Config.GpuAccessor.QuerySamplerType(handle);
- }
-
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
- {
- if ((compMask & 1) != 0)
- {
- Operand dest = GetDest();
-
- TextureOperation operation = context.CreateTextureOperation(
- inst,
- type,
- flags,
- handle,
- compIndex,
- dest,
- sources);
-
- context.Add(operation);
- }
- }
- }
-
- private static void EmitTextureSample(EmitterContext context, TextureFlags flags)
- {
- OpCodeTexture op = (OpCodeTexture)context.CurrOp;
-
- bool isBindless = (flags & TextureFlags.Bindless) != 0;
-
- if (op.Rd.IsRZ)
- {
- return;
- }
-
- int raIndex = op.Ra.Index;
- int rbIndex = op.Rb.Index;
-
- Operand Ra()
- {
- if (raIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
-
- return context.Copy(Register(raIndex++, RegisterType.Gpr));
- }
-
- Operand Rb()
- {
- if (rbIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
-
- return context.Copy(Register(rbIndex++, RegisterType.Gpr));
- }
-
- Operand arrayIndex = op.IsArray ? Ra() : null;
-
- List<Operand> sourcesList = new List<Operand>();
-
if (isBindless)
{
- sourcesList.Add(Rb());
- }
-
- SamplerType type = ConvertSamplerType(op.Dimensions);
-
- bool hasLod = op.LodMode > TextureLodMode.LodZero;
-
- if (type == SamplerType.Texture1D && (flags & ~TextureFlags.Bindless) == TextureFlags.IntCoords && !(hasLod ||
- op.HasDepthCompare ||
- op.HasOffset ||
- op.IsArray ||
- op.IsMultisample))
- {
- // For bindless, we don't have any way to know the texture type,
- // so we assume it's texture buffer when the sampler type is 1D, since that's more common.
- bool isTypeBuffer = isBindless || context.Config.GpuAccessor.QuerySamplerType(op.HandleOffset) == SamplerType.TextureBuffer;
-
- if (isTypeBuffer)
- {
- type = SamplerType.TextureBuffer;
- }
- }
-
- int coordsCount = type.GetDimensions();
-
- for (int index = 0; index < coordsCount; index++)
- {
- sourcesList.Add(Ra());
- }
-
- if (Sample1DAs2D && type == SamplerType.Texture1D)
- {
- sourcesList.Add(ConstF(0));
-
- type = SamplerType.Texture2D;
- }
-
- if (op.IsArray)
- {
- sourcesList.Add(arrayIndex);
-
- type |= SamplerType.Array;
- }
-
- Operand lodValue = hasLod ? Rb() : ConstF(0);
-
- Operand packedOffs = op.HasOffset ? Rb() : null;
-
- if (op.HasDepthCompare)
- {
- sourcesList.Add(Rb());
-
- type |= SamplerType.Shadow;
- }
-
- if ((op.LodMode == TextureLodMode.LodZero ||
- op.LodMode == TextureLodMode.LodLevel ||
- op.LodMode == TextureLodMode.LodLevelA) && !op.IsMultisample && type != SamplerType.TextureBuffer)
- {
- sourcesList.Add(lodValue);
-
- flags |= TextureFlags.LodLevel;
+ type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
}
-
- if (op.HasOffset)
- {
- for (int index = 0; index < coordsCount; index++)
- {
- sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4)));
- }
-
- flags |= TextureFlags.Offset;
- }
-
- if (op.LodMode == TextureLodMode.LodBias ||
- op.LodMode == TextureLodMode.LodBiasA)
- {
- sourcesList.Add(lodValue);
-
- flags |= TextureFlags.LodBias;
- }
-
- if (op.IsMultisample)
- {
- sourcesList.Add(Rb());
-
- type |= SamplerType.Multisample;
- }
-
- Operand[] sources = sourcesList.ToArray();
-
- int rdIndex = op.Rd.Index;
-
- Operand GetDest()
+ else
{
- if (rdIndex > RegisterConsts.RegisterZeroIndex)
- {
- return Const(0);
- }
-
- return Register(rdIndex++, RegisterType.Gpr);
+ type = context.Config.GpuAccessor.QuerySamplerType(imm);
}
- int handle = !isBindless ? op.HandleOffset : 0;
-
- for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
+ for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
{
if ((compMask & 1) != 0)
{
- Operand dest = GetDest();
-
TextureOperation operation = context.CreateTextureOperation(
- Instruction.TextureSample,
+ inst,
type,
flags,
- handle,
+ imm,
compIndex,
- dest,
+ GetDest(),
sources);
context.Add(operation);
@@ -1541,228 +1163,126 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- private static int GetComponents(IntegerSize size)
- {
- return size switch
- {
- IntegerSize.B64 => 2,
- IntegerSize.B128 => 4,
- IntegerSize.UB128 => 4,
- _ => 1
- };
- }
-
- private static int GetComponentSizeInBytesLog2(IntegerSize size)
- {
- return size switch
- {
- IntegerSize.U8 => 0,
- IntegerSize.S8 => 0,
- IntegerSize.U16 => 1,
- IntegerSize.S16 => 1,
- IntegerSize.B32 => 2,
- IntegerSize.B64 => 3,
- IntegerSize.B128 => 4,
- IntegerSize.UB128 => 4,
- _ => 2
- };
- }
-
- private static TextureFormat GetTextureFormat(IntegerSize size)
- {
- return size switch
- {
- IntegerSize.U8 => TextureFormat.R8Uint,
- IntegerSize.S8 => TextureFormat.R8Sint,
- IntegerSize.U16 => TextureFormat.R16Uint,
- IntegerSize.S16 => TextureFormat.R16Sint,
- IntegerSize.B32 => TextureFormat.R32Uint,
- IntegerSize.B64 => TextureFormat.R32G32Uint,
- IntegerSize.B128 => TextureFormat.R32G32B32A32Uint,
- IntegerSize.UB128 => TextureFormat.R32G32B32A32Uint,
- _ => TextureFormat.R32Uint
- };
- }
-
- private static int GetComponentSizeInBytesLog2(ReductionType type)
- {
- return type switch
- {
- ReductionType.U32 => 2,
- ReductionType.S32 => 2,
- ReductionType.U64 => 3,
- ReductionType.FP32FtzRn => 2,
- ReductionType.FP16x2FtzRn => 2,
- ReductionType.S64 => 3,
- ReductionType.SD32 => 2,
- ReductionType.SD64 => 3,
- _ => 2
- };
- }
-
- private static TextureFormat GetTextureFormat(ReductionType type)
- {
- return type switch
- {
- ReductionType.U32 => TextureFormat.R32Uint,
- ReductionType.S32 => TextureFormat.R32Sint,
- ReductionType.U64 => TextureFormat.R32G32Uint,
- ReductionType.FP32FtzRn => TextureFormat.R32Float,
- ReductionType.FP16x2FtzRn => TextureFormat.R16G16Float,
- ReductionType.S64 => TextureFormat.R32G32Uint,
- ReductionType.SD32 => TextureFormat.R32Uint,
- ReductionType.SD64 => TextureFormat.R32G32Uint,
- _ => TextureFormat.R32Uint
- };
- }
-
- private static TextureFlags GetAtomicOpFlags(AtomicOp op)
- {
- return op switch
- {
- AtomicOp.Add => TextureFlags.Add,
- AtomicOp.Minimum => TextureFlags.Minimum,
- AtomicOp.Maximum => TextureFlags.Maximum,
- AtomicOp.Increment => TextureFlags.Increment,
- AtomicOp.Decrement => TextureFlags.Decrement,
- AtomicOp.BitwiseAnd => TextureFlags.BitwiseAnd,
- AtomicOp.BitwiseOr => TextureFlags.BitwiseOr,
- AtomicOp.BitwiseExclusiveOr => TextureFlags.BitwiseXor,
- AtomicOp.Swap => TextureFlags.Swap,
- _ => TextureFlags.Add
- };
- }
-
- private static SamplerType ConvertSamplerType(ImageDimensions target)
- {
- return target switch
- {
- ImageDimensions.Image1D => SamplerType.Texture1D,
- ImageDimensions.ImageBuffer => SamplerType.TextureBuffer,
- ImageDimensions.Image1DArray => SamplerType.Texture1D | SamplerType.Array,
- ImageDimensions.Image2D => SamplerType.Texture2D,
- ImageDimensions.Image2DArray => SamplerType.Texture2D | SamplerType.Array,
- ImageDimensions.Image3D => SamplerType.Texture3D,
- _ => SamplerType.None
- };
- }
-
- private static SamplerType ConvertSamplerType(TextureDimensions dimensions)
+ private static SamplerType ConvertSamplerType(TexDim dimensions)
{
return dimensions switch
{
- TextureDimensions.Texture1D => SamplerType.Texture1D,
- TextureDimensions.Texture2D => SamplerType.Texture2D,
- TextureDimensions.Texture3D => SamplerType.Texture3D,
- TextureDimensions.TextureCube => SamplerType.TextureCube,
+ TexDim._1d => SamplerType.Texture1D,
+ TexDim.Array1d => SamplerType.Texture1D | SamplerType.Array,
+ TexDim._2d => SamplerType.Texture2D,
+ TexDim.Array2d => SamplerType.Texture2D | SamplerType.Array,
+ TexDim._3d => SamplerType.Texture3D,
+ TexDim.Array3d => SamplerType.Texture3D | SamplerType.Array,
+ TexDim.Cube => SamplerType.TextureCube,
+ TexDim.ArrayCube => SamplerType.TextureCube | SamplerType.Array,
_ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".")
};
}
- private static SamplerType ConvertSamplerType(TextureTarget type)
+ private static SamplerType ConvertSamplerType(TexsTarget type)
{
switch (type)
{
- case TextureTarget.Texture1DLodZero:
+ case TexsTarget.Texture1DLodZero:
return SamplerType.Texture1D;
- case TextureTarget.Texture2D:
- case TextureTarget.Texture2DLodZero:
- case TextureTarget.Texture2DLodLevel:
+ case TexsTarget.Texture2D:
+ case TexsTarget.Texture2DLodZero:
+ case TexsTarget.Texture2DLodLevel:
return SamplerType.Texture2D;
- case TextureTarget.Texture2DDepthCompare:
- case TextureTarget.Texture2DLodLevelDepthCompare:
- case TextureTarget.Texture2DLodZeroDepthCompare:
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.Texture2DLodZeroDepthCompare:
return SamplerType.Texture2D | SamplerType.Shadow;
- case TextureTarget.Texture2DArray:
- case TextureTarget.Texture2DArrayLodZero:
+ case TexsTarget.Texture2DArray:
+ case TexsTarget.Texture2DArrayLodZero:
return SamplerType.Texture2D | SamplerType.Array;
- case TextureTarget.Texture2DArrayLodZeroDepthCompare:
+ case TexsTarget.Texture2DArrayLodZeroDepthCompare:
return SamplerType.Texture2D | SamplerType.Array | SamplerType.Shadow;
- case TextureTarget.Texture3D:
- case TextureTarget.Texture3DLodZero:
+ case TexsTarget.Texture3D:
+ case TexsTarget.Texture3DLodZero:
return SamplerType.Texture3D;
- case TextureTarget.TextureCube:
- case TextureTarget.TextureCubeLodLevel:
+ case TexsTarget.TextureCube:
+ case TexsTarget.TextureCubeLodLevel:
return SamplerType.TextureCube;
}
return SamplerType.None;
}
- private static SamplerType ConvertSamplerType(TexelLoadTarget type)
+ private static SamplerType ConvertSamplerType(TldsTarget type)
{
switch (type)
{
- case TexelLoadTarget.Texture1DLodZero:
- case TexelLoadTarget.Texture1DLodLevel:
+ case TldsTarget.Texture1DLodZero:
+ case TldsTarget.Texture1DLodLevel:
return SamplerType.Texture1D;
- case TexelLoadTarget.Texture2DLodZero:
- case TexelLoadTarget.Texture2DLodZeroOffset:
- case TexelLoadTarget.Texture2DLodLevel:
- case TexelLoadTarget.Texture2DLodLevelOffset:
+ case TldsTarget.Texture2DLodZero:
+ case TldsTarget.Texture2DLodZeroOffset:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodLevelOffset:
return SamplerType.Texture2D;
- case TexelLoadTarget.Texture2DLodZeroMultisample:
+ case TldsTarget.Texture2DLodZeroMultisample:
return SamplerType.Texture2D | SamplerType.Multisample;
- case TexelLoadTarget.Texture3DLodZero:
+ case TldsTarget.Texture3DLodZero:
return SamplerType.Texture3D;
- case TexelLoadTarget.Texture2DArrayLodZero:
+ case TldsTarget.Texture2DArrayLodZero:
return SamplerType.Texture2D | SamplerType.Array;
}
return SamplerType.None;
}
- private static TextureFlags ConvertTextureFlags(TextureTarget type)
+ private static TextureFlags ConvertTextureFlags(TexsTarget type)
{
switch (type)
{
- case TextureTarget.Texture1DLodZero:
- case TextureTarget.Texture2DLodZero:
- case TextureTarget.Texture2DLodLevel:
- case TextureTarget.Texture2DLodLevelDepthCompare:
- case TextureTarget.Texture2DLodZeroDepthCompare:
- case TextureTarget.Texture2DArrayLodZero:
- case TextureTarget.Texture2DArrayLodZeroDepthCompare:
- case TextureTarget.Texture3DLodZero:
- case TextureTarget.TextureCubeLodLevel:
+ case TexsTarget.Texture1DLodZero:
+ case TexsTarget.Texture2DLodZero:
+ case TexsTarget.Texture2DLodLevel:
+ case TexsTarget.Texture2DLodLevelDepthCompare:
+ case TexsTarget.Texture2DLodZeroDepthCompare:
+ case TexsTarget.Texture2DArrayLodZero:
+ case TexsTarget.Texture2DArrayLodZeroDepthCompare:
+ case TexsTarget.Texture3DLodZero:
+ case TexsTarget.TextureCubeLodLevel:
return TextureFlags.LodLevel;
- case TextureTarget.Texture2D:
- case TextureTarget.Texture2DDepthCompare:
- case TextureTarget.Texture2DArray:
- case TextureTarget.Texture3D:
- case TextureTarget.TextureCube:
+ case TexsTarget.Texture2D:
+ case TexsTarget.Texture2DDepthCompare:
+ case TexsTarget.Texture2DArray:
+ case TexsTarget.Texture3D:
+ case TexsTarget.TextureCube:
return TextureFlags.None;
}
return TextureFlags.None;
}
- private static TextureFlags ConvertTextureFlags(TexelLoadTarget type)
+ private static TextureFlags ConvertTextureFlags(TldsTarget type)
{
switch (type)
{
- case TexelLoadTarget.Texture1DLodZero:
- case TexelLoadTarget.Texture1DLodLevel:
- case TexelLoadTarget.Texture2DLodZero:
- case TexelLoadTarget.Texture2DLodLevel:
- case TexelLoadTarget.Texture2DLodZeroMultisample:
- case TexelLoadTarget.Texture3DLodZero:
- case TexelLoadTarget.Texture2DArrayLodZero:
+ case TldsTarget.Texture1DLodZero:
+ case TldsTarget.Texture1DLodLevel:
+ case TldsTarget.Texture2DLodZero:
+ case TldsTarget.Texture2DLodLevel:
+ case TldsTarget.Texture2DLodZeroMultisample:
+ case TldsTarget.Texture3DLodZero:
+ case TldsTarget.Texture2DArrayLodZero:
return TextureFlags.LodLevel;
- case TexelLoadTarget.Texture2DLodZeroOffset:
- case TexelLoadTarget.Texture2DLodLevelOffset:
+ case TldsTarget.Texture2DLodZeroOffset:
+ case TldsTarget.Texture2DLodLevelOffset:
return TextureFlags.LodLevel | TextureFlags.Offset;
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs
new file mode 100644
index 00000000..43c0035a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoArithmetic.cs
@@ -0,0 +1,18 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.Translation;
+
+using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
+
+namespace Ryujinx.Graphics.Shader.Instructions
+{
+ static partial class InstEmit
+ {
+ public static void Vmad(EmitterContext context)
+ {
+ InstVmad op = context.GetOp<InstVmad>();
+
+ // TODO: Implement properly.
+ context.Copy(GetDest(op.Dest), GetSrcReg(context, op.SrcC));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs
index 7b3d9ca5..890b31d6 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs
@@ -9,47 +9,46 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
static partial class InstEmit
{
- public static void Vmad(EmitterContext context)
- {
- // TODO: Implement properly.
- context.Copy(GetDest(context), GetSrcC(context));
- }
-
public static void Vmnmx(EmitterContext context)
{
- OpCodeVideo op = (OpCodeVideo)context.CurrOp;
-
- bool max = op.RawOpCode.Extract(56);
+ InstVmnmx op = context.GetOp<InstVmnmx>();
- Operand srcA = Extend(context, GetSrcA(context), op.RaSelection, op.RaType);
- Operand srcC = GetSrcC(context);
+ Operand srcA = Extend(context, GetSrcReg(context, op.SrcA), op.ASelect);
+ Operand srcC = GetSrcReg(context, op.SrcC);
Operand srcB;
- if (op.HasRb)
+ if (op.BVideo)
{
- srcB = Extend(context, Register(op.Rb), op.RbSelection, op.RbType);
+ srcB = Extend(context, GetSrcReg(context, op.SrcB), op.BSelect);
}
else
{
- srcB = Const(op.Immediate);
+ int imm = op.Imm16;
+
+ if ((op.BSelect & VectorSelect.S8B0) != 0)
+ {
+ imm = (imm << 16) >> 16;
+ }
+
+ srcB = Const(imm);
}
Operand res;
bool resSigned;
- if ((op.RaType & VideoType.Signed) != (op.RbType & VideoType.Signed))
+ if ((op.ASelect & VectorSelect.S8B0) != (op.BSelect & VectorSelect.S8B0))
{
// Signedness is different, but for max, result will always fit a U32,
// since one of the inputs can't be negative, and the result is the one
// with highest value. For min, it will always fit on a S32, since
// one of the input can't be greater than INT_MAX and we want the lowest value.
- resSigned = !max;
+ resSigned = !op.Mn;
- res = max ? context.IMaximumU32(srcA, srcB) : context.IMinimumS32(srcA, srcB);
+ res = op.Mn ? context.IMaximumU32(srcA, srcB) : context.IMinimumS32(srcA, srcB);
- if ((op.RaType & VideoType.Signed) != 0)
+ if ((op.ASelect & VectorSelect.S8B0) != 0)
{
Operand isBGtIntMax = context.ICompareLess(srcB, Const(0));
@@ -65,9 +64,9 @@ namespace Ryujinx.Graphics.Shader.Instructions
else
{
// Ra and Rb have the same signedness, so doesn't matter which one we test.
- resSigned = (op.RaType & VideoType.Signed) != 0;
+ resSigned = (op.ASelect & VectorSelect.S8B0) != 0;
- if (max)
+ if (op.Mn)
{
res = resSigned
? context.IMaximumS32(srcA, srcB)
@@ -81,54 +80,62 @@ namespace Ryujinx.Graphics.Shader.Instructions
}
}
- if (op.Saturate)
+ if (op.Sat)
{
- if (op.DstSigned && !resSigned)
+ if (op.DFormat && !resSigned)
{
res = context.IMinimumU32(res, Const(int.MaxValue));
}
- else if (!op.DstSigned && resSigned)
+ else if (!op.DFormat && resSigned)
{
res = context.IMaximumS32(res, Const(0));
}
}
- switch (op.PostOp)
+ switch (op.VideoOp)
{
- case VideoPostOp.Acc:
+ case VideoOp.Acc:
res = context.IAdd(res, srcC);
break;
- case VideoPostOp.Max:
- res = op.DstSigned ? context.IMaximumS32(res, srcC) : context.IMaximumU32(res, srcC);
+ case VideoOp.Max:
+ res = op.DFormat ? context.IMaximumS32(res, srcC) : context.IMaximumU32(res, srcC);
break;
- case VideoPostOp.Min:
- res = op.DstSigned ? context.IMinimumS32(res, srcC) : context.IMinimumU32(res, srcC);
+ case VideoOp.Min:
+ res = op.DFormat ? context.IMinimumS32(res, srcC) : context.IMinimumU32(res, srcC);
break;
- case VideoPostOp.Mrg16h:
+ case VideoOp.Mrg16h:
res = context.BitfieldInsert(srcC, res, Const(16), Const(16));
break;
- case VideoPostOp.Mrg16l:
+ case VideoOp.Mrg16l:
res = context.BitfieldInsert(srcC, res, Const(0), Const(16));
break;
- case VideoPostOp.Mrg8b0:
+ case VideoOp.Mrg8b0:
res = context.BitfieldInsert(srcC, res, Const(0), Const(8));
break;
- case VideoPostOp.Mrg8b2:
+ case VideoOp.Mrg8b2:
res = context.BitfieldInsert(srcC, res, Const(16), Const(8));
break;
}
- context.Copy(GetDest(context), res);
+ context.Copy(GetDest(op.Dest), res);
}
- private static Operand Extend(EmitterContext context, Operand src, int sel, VideoType type)
+ private static Operand Extend(EmitterContext context, Operand src, VectorSelect type)
{
return type switch
{
- VideoType.U8 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(sel * 8)), 8),
- VideoType.U16 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(sel * 16)), 16),
- VideoType.S8 => SignExtendTo32(context, context.ShiftRightU32(src, Const(sel * 8)), 8),
- VideoType.S16 => SignExtendTo32(context, context.ShiftRightU32(src, Const(sel * 16)), 16),
+ VectorSelect.U8B0 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(0)), 8),
+ VectorSelect.U8B1 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(8)), 8),
+ VectorSelect.U8B2 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(16)), 8),
+ VectorSelect.U8B3 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(24)), 8),
+ VectorSelect.U16H0 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(0)), 16),
+ VectorSelect.U16H1 => ZeroExtendTo32(context, context.ShiftRightU32(src, Const(16)), 16),
+ VectorSelect.S8B0 => SignExtendTo32(context, context.ShiftRightU32(src, Const(0)), 8),
+ VectorSelect.S8B1 => SignExtendTo32(context, context.ShiftRightU32(src, Const(8)), 8),
+ VectorSelect.S8B2 => SignExtendTo32(context, context.ShiftRightU32(src, Const(16)), 8),
+ VectorSelect.S8B3 => SignExtendTo32(context, context.ShiftRightU32(src, Const(24)), 8),
+ VectorSelect.S16H0 => SignExtendTo32(context, context.ShiftRightU32(src, Const(0)), 16),
+ VectorSelect.S16H1 => SignExtendTo32(context, context.ShiftRightU32(src, Const(16)), 16),
_ => src
};
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs
deleted file mode 100644
index 5c16770a..00000000
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitVote.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Ryujinx.Graphics.Shader.Decoders;
-using Ryujinx.Graphics.Shader.IntermediateRepresentation;
-using Ryujinx.Graphics.Shader.Translation;
-
-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 Vote(EmitterContext context)
- {
- OpCodeVote op = (OpCodeVote)context.CurrOp;
-
- Operand pred = GetPredicate39(context);
-
- Operand res = null;
-
- switch (op.VoteOp)
- {
- case VoteOp.All:
- res = context.VoteAll(pred);
- break;
- case VoteOp.Any:
- res = context.VoteAny(pred);
- break;
- case VoteOp.AllEqual:
- res = context.VoteAllEqual(pred);
- break;
- }
-
- if (res != null)
- {
- context.Copy(Register(op.Predicate45), res);
- }
- else
- {
- context.Config.GpuAccessor.Log($"Invalid vote operation: {op.VoteOp}.");
- }
-
- if (!op.Rd.IsRZ)
- {
- context.Copy(Register(op.Rd), context.Ballot(pred));
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs
new file mode 100644
index 00000000..3c833613
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitWarp.cs
@@ -0,0 +1,84 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using Ryujinx.Graphics.Shader.Translation;
+
+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 Fswzadd(EmitterContext context)
+ {
+ InstFswzadd op = context.GetOp<InstFswzadd>();
+
+ Operand srcA = GetSrcReg(context, op.SrcA);
+ Operand srcB = GetSrcReg(context, op.SrcB);
+ Operand dest = GetDest(op.Dest);
+
+ context.Copy(dest, context.FPSwizzleAdd(srcA, srcB, op.PnWord));
+
+ InstEmitAluHelper.SetFPZnFlags(context, dest, op.WriteCC);
+ }
+
+ public static void Shfl(EmitterContext context)
+ {
+ InstShfl op = context.GetOp<InstShfl>();
+
+ Operand pred = Register(op.DestPred, RegisterType.Predicate);
+
+ Operand srcA = GetSrcReg(context, op.SrcA);
+
+ Operand srcB = op.BFixShfl ? Const(op.SrcBImm) : GetSrcReg(context, op.SrcB);
+ Operand srcC = op.CFixShfl ? Const(op.SrcCImm) : GetSrcReg(context, op.SrcC);
+
+ (Operand res, Operand valid) = op.ShflMode switch
+ {
+ ShflMode.Idx => context.Shuffle(srcA, srcB, srcC),
+ ShflMode.Up => context.ShuffleUp(srcA, srcB, srcC),
+ ShflMode.Down => context.ShuffleDown(srcA, srcB, srcC),
+ ShflMode.Bfly => context.ShuffleXor(srcA, srcB, srcC),
+ _ => (null, null)
+ };
+
+ context.Copy(GetDest(op.Dest), res);
+ context.Copy(pred, valid);
+ }
+
+ public static void Vote(EmitterContext context)
+ {
+ InstVote op = context.GetOp<InstVote>();
+
+ Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
+ Operand res = null;
+
+ switch (op.VoteMode)
+ {
+ case VoteMode.All:
+ res = context.VoteAll(pred);
+ break;
+ case VoteMode.Any:
+ res = context.VoteAny(pred);
+ break;
+ case VoteMode.Eq:
+ res = context.VoteAllEqual(pred);
+ break;
+ }
+
+ if (res != null)
+ {
+ context.Copy(Register(op.VpDest, RegisterType.Predicate), res);
+ }
+ else
+ {
+ context.Config.GpuAccessor.Log($"Invalid vote operation: {op.VoteMode}.");
+ }
+
+ if (op.Dest != RegisterConsts.RegisterZeroIndex)
+ {
+ context.Copy(GetDest(op.Dest), context.Ballot(pred));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs b/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs
index 67e24957..76a52007 100644
--- a/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs
@@ -7,12 +7,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
static class Lop3Expression
{
- public static Operand GetFromTruthTable(
- EmitterContext context,
- Operand srcA,
- Operand srcB,
- Operand srcC,
- int imm)
+ public static Operand GetFromTruthTable(EmitterContext context, Operand srcA, Operand srcB, Operand srcC, int imm)
{
Operand expr = null;
@@ -45,7 +40,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
int map;
// Encode into gray code.
- map = ((imm >> 0) & 1) << 0;
+ map = ((imm >> 0) & 1) << 0;
map |= ((imm >> 1) & 1) << 4;
map |= ((imm >> 2) & 1) << 1;
map |= ((imm >> 3) & 1) << 5;