Fix XMAD shader instruction implementation

This commit is contained in:
gdkchan 2019-02-13 15:10:11 -02:00
commit aa298596a5
2 changed files with 109 additions and 45 deletions

View file

@ -1148,41 +1148,87 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
} }
private enum XmadMode
{
Cfull = 0,
Clo = 1,
Chi = 2,
Csfu = 3,
Cbcc = 4
}
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{ {
//TODO: Confirm SignAB/C, it is just a guess. bool SignedA = OpCode.Read(48);
//TODO: Implement Mode 3 (CSFU), what it does? bool SignedB = OpCode.Read(49);
bool SignAB = OpCode.Read(48); bool HighB = OpCode.Read(52);
bool SignC = OpCode.Read(49); bool HighA = OpCode.Read(53);
bool HighB = OpCode.Read(52);
bool HighA = OpCode.Read(53);
int Mode = OpCode.Read(50, 7); int Mode = OpCode.Read(50, 7);
ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;
ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);
ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff);
ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
ShaderIrInst ShiftC = SignC ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
if (HighA)
{
OperA = new ShaderIrOp(ShiftAB, OperA, Imm16);
}
switch (Oper) switch (Oper)
{ {
case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.CR: OperB = OpCode.Cbuf34(); break;
case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.Imm: OperB = OpCode.ImmU16_20(); break;
case ShaderOper.RC: OperB = OpCode.Gpr39(); break; case ShaderOper.RC: OperB = OpCode.Gpr39(); break;
case ShaderOper.RR: OperB = OpCode.Gpr20(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break;
default: throw new ArgumentException(nameof(Oper)); default: throw new ArgumentException(nameof(Oper));
} }
bool ProductShiftLeft = false, Merge = false; ShaderIrNode OperB2 = OperB;
if (Oper == ShaderOper.Imm)
{
int Imm = ((ShaderIrOperImm)OperB2).Value;
if (!HighB)
{
Imm <<= 16;
}
if (SignedB)
{
Imm >>= 16;
}
else
{
Imm = (int)((uint)Imm >> 16);
}
OperB2 = new ShaderIrOperImm(Imm);
}
ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);
//If we are working with the lower 16-bits of the A/B operands,
//we need to shift the lower 16-bits to the top 16-bits. Later,
//they will be right shifted. For U16 types, this will be a logical
//right shift, and for S16 types, a arithmetic right shift.
if (!HighA)
{
OperA = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Imm16);
}
if (!HighB && Oper != ShaderOper.Imm)
{
OperB2 = new ShaderIrOp(ShaderIrInst.Lsl, OperB2, Imm16);
}
ShaderIrInst ShiftA = SignedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
ShaderIrInst ShiftB = SignedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
OperA = new ShaderIrOp(ShiftA, OperA, Imm16);
if (Oper != ShaderOper.Imm)
{
OperB2 = new ShaderIrOp(ShiftB, OperB2, Imm16);
}
bool ProductShiftLeft = false;
bool Merge = false;
if (Oper == ShaderOper.RC) if (Oper == ShaderOper.RC)
{ {
@ -1196,40 +1242,53 @@ namespace Ryujinx.Graphics.Gal.Shader
Merge = OpCode.Read(37); Merge = OpCode.Read(37);
} }
switch (Mode) ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB2);
{
//CLO.
case 1: OperC = ExtendTo32(OperC, SignC, 16); break;
//CHI.
case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break;
}
ShaderIrNode OperBH = OperB;
if (HighB)
{
OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16);
}
ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH);
if (ProductShiftLeft) if (ProductShiftLeft)
{ {
MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16); MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16);
} }
switch ((XmadMode)Mode)
{
case XmadMode.Clo: OperC = ExtendTo32(OperC, Signed: false, Size: 16); break;
case XmadMode.Chi: OperC = new ShaderIrOp(ShaderIrInst.Lsr, OperC, Imm16); break;
case XmadMode.Cbcc:
{
ShaderIrOp OperBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
OperC = new ShaderIrOp(ShaderIrInst.Add, OperC, OperBLsh16);
break;
}
case XmadMode.Csfu:
{
ShaderIrOperImm Imm31 = new ShaderIrOperImm(31);
ShaderIrOp SignAjustA = new ShaderIrOp(ShaderIrInst.Lsr, OperA, Imm31);
ShaderIrOp SignAjustB = new ShaderIrOp(ShaderIrInst.Lsr, OperB2, Imm31);
SignAjustA = new ShaderIrOp(ShaderIrInst.Lsl, SignAjustA, Imm16);
SignAjustB = new ShaderIrOp(ShaderIrInst.Lsl, SignAjustB, Imm16);
ShaderIrOp SignAdjust = new ShaderIrOp(ShaderIrInst.Add, SignAjustA, SignAjustB);
OperC = new ShaderIrOp(ShaderIrInst.Sub, OperC, SignAdjust);
break;
}
}
ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC); ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC);
if (Merge) if (Merge)
{ {
AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk); ShaderIrOperImm Imm16Mask = new ShaderIrOperImm(0xffff);
OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
}
if (Mode == 4) AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, Imm16Mask);
{
OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
} }

View file

@ -138,6 +138,11 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20))); return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
} }
private static ShaderIrOperImm ImmU16_20(this long OpCode)
{
return new ShaderIrOperImm(OpCode.Read(20, 0xffff));
}
private static ShaderIrOperImm Imm19_20(this long OpCode) private static ShaderIrOperImm Imm19_20(this long OpCode)
{ {
int Value = OpCode.Read(20, 0x7ffff); int Value = OpCode.Read(20, 0x7ffff);