Implement VMNMX shader instruction (#1032)

* Implement VMNMX shader instruction

* No need for the gap on the enum

* Fix typo
This commit is contained in:
gdkchan 2020-03-25 11:49:10 -03:00 committed by GitHub
parent 56374c8633
commit 1586450a38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 232 additions and 4 deletions

View File

@ -205,6 +205,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex));
Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex)); Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex));
Set("01011111xxxxxx", InstEmit.Vmad, typeof(OpCodeVideo)); Set("01011111xxxxxx", InstEmit.Vmad, typeof(OpCodeVideo));
Set("0011101xxxxxxx", InstEmit.Vmnmx, typeof(OpCodeVideo));
Set("0101000011011x", InstEmit.Vote, typeof(OpCodeVote)); Set("0101000011011x", InstEmit.Vote, typeof(OpCodeVote));
Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf)); Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf));
Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm)); Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm));

View File

@ -6,18 +6,97 @@ namespace Ryujinx.Graphics.Shader.Decoders
{ {
public Register Rd { get; } public Register Rd { get; }
public Register Ra { get; } public Register Ra { get; }
public Register Rb { get; }
public Register Rc { get; } public Register Rc { get; }
public bool SetCondCode { get; protected set; } public int Immediate { get; }
public bool Saturate { get; protected set; }
public int RaSelection { get; }
public int RbSelection { get; }
public bool SetCondCode { get; }
public bool HasRb { get; }
public VideoType RaType { get; }
public VideoType RbType { get; }
public VideoPostOp PostOp { get; }
public bool DstSigned { get; }
public bool Saturate { get; }
public OpCodeVideo(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) public OpCodeVideo(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
{ {
Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr); Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
RaSelection = opCode.Extract(36, 2);
RbSelection = opCode.Extract(28, 2);
RaType = opCode.Extract(37, 2) switch
{
2 => VideoType.U16,
3 => VideoType.U32,
_ => VideoType.U8
};
RbType = opCode.Extract(29, 2) switch
{
2 => VideoType.U16,
3 => VideoType.U32,
_ => VideoType.U8
};
if (opCode.Extract(48))
{
RaType |= VideoType.Signed;
}
if (!opCode.Extract(50))
{
// Immediate variant.
Immediate = opCode.Extract(16, 20);
RbType = opCode.Extract(49) ? VideoType.S16 : VideoType.U16;
if (RbType == VideoType.S16)
{
Immediate = (Immediate << 12) >> 12;
}
}
else if (opCode.Extract(49))
{
RbType |= VideoType.Signed;
}
if (RaType == VideoType.U16)
{
RaSelection &= 1;
}
else if (RaType == VideoType.U32)
{
RaSelection = 0;
}
if (RbType == VideoType.U16)
{
RbSelection &= 1;
}
else if (RbType == VideoType.U32)
{
RbSelection = 0;
}
SetCondCode = opCode.Extract(47); SetCondCode = opCode.Extract(47);
HasRb = opCode.Extract(50);
PostOp = (VideoPostOp)opCode.Extract(51, 3);
DstSigned = opCode.Extract(54);
Saturate = opCode.Extract(55); Saturate = opCode.Extract(55);
} }
} }

View File

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Shader.Decoders
{
enum VideoPostOp
{
Mrg16h,
Mrg16l,
Mrg8b0,
Mrg8b2,
Acc,
Min,
Max,
Pass
}
}

View File

@ -0,0 +1,15 @@
namespace Ryujinx.Graphics.Shader.Decoders
{
enum VideoType
{
U8 = 0,
U16 = 1,
U32 = 2,
Signed = 1 << 2,
S8 = Signed | U8,
S16 = Signed | U16,
S32 = Signed | U32
}
}

View File

@ -1,17 +1,136 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
namespace Ryujinx.Graphics.Shader.Instructions namespace Ryujinx.Graphics.Shader.Instructions
{ {
static partial class InstEmit static partial class InstEmit
{ {
public static void Vmad(EmitterContext context) 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; OpCodeVideo op = (OpCodeVideo)context.CurrOp;
context.Copy(GetDest(context), GetSrcC(context)); bool max = op.RawOpCode.Extract(56);
Operand srcA = Extend(context, GetSrcA(context), op.RaSelection, op.RaType);
Operand srcC = GetSrcC(context);
Operand srcB;
if (op.HasRb)
{
srcB = Extend(context, Register(op.Rb), op.RbSelection, op.RbType);
}
else
{
srcB = Const(op.Immediate);
}
Operand res;
bool resSigned;
if ((op.RaType & VideoType.Signed) != (op.RbType & VideoType.Signed))
{
// 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;
res = max ? context.IMaximumU32(srcA, srcB) : context.IMinimumS32(srcA, srcB);
if ((op.RaType & VideoType.Signed) != 0)
{
Operand isBGtIntMax = context.ICompareLess(srcB, Const(0));
res = context.ConditionalSelect(isBGtIntMax, srcB, res);
}
else
{
Operand isAGtIntMax = context.ICompareLess(srcA, Const(0));
res = context.ConditionalSelect(isAGtIntMax, srcA, res);
}
}
else
{
// Ra and Rb have the same signedness, so doesn't matter which one we test.
resSigned = (op.RaType & VideoType.Signed) != 0;
if (max)
{
res = resSigned
? context.IMaximumS32(srcA, srcB)
: context.IMaximumU32(srcA, srcB);
}
else
{
res = resSigned
? context.IMinimumS32(srcA, srcB)
: context.IMinimumU32(srcA, srcB);
}
}
if (op.Saturate)
{
if (op.DstSigned && !resSigned)
{
res = context.IMinimumU32(res, Const(int.MaxValue));
}
else if (!op.DstSigned && resSigned)
{
res = context.IMaximumS32(res, Const(0));
}
}
switch (op.PostOp)
{
case VideoPostOp.Acc:
res = context.IAdd(res, srcC);
break;
case VideoPostOp.Max:
res = op.DstSigned ? context.IMaximumS32(res, srcC) : context.IMaximumU32(res, srcC);
break;
case VideoPostOp.Min:
res = op.DstSigned ? context.IMinimumS32(res, srcC) : context.IMinimumU32(res, srcC);
break;
case VideoPostOp.Mrg16h:
res = context.BitfieldInsert(srcC, res, Const(16), Const(16));
break;
case VideoPostOp.Mrg16l:
res = context.BitfieldInsert(srcC, res, Const(0), Const(16));
break;
case VideoPostOp.Mrg8b0:
res = context.BitfieldInsert(srcC, res, Const(0), Const(8));
break;
case VideoPostOp.Mrg8b2:
res = context.BitfieldInsert(srcC, res, Const(16), Const(8));
break;
}
context.Copy(GetDest(context), res);
}
private static Operand Extend(EmitterContext context, Operand src, int sel, VideoType 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),
_ => src
};
} }
} }
} }