diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2021-10-12 17:35:31 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-12 22:35:31 +0200 |
| commit | a7109c767bdc014327b574012794156c92174495 (patch) | |
| tree | d7d7db6eaa63d4e3e0396a3182d0267b6ad31dd7 /Ryujinx.Graphics.Shader/Instructions | |
| parent | 0510fde25ae66ec0b55091746a52931248d75b89 (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')
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; |
