Add HADD2 and HMUL2 half float shader instructions

This commit is contained in:
gdkchan 2019-04-06 00:34:43 -03:00
parent 67868899e4
commit 3980043cf2
12 changed files with 304 additions and 30 deletions

View file

@ -103,6 +103,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
Add(Instruction.MinimumU32, InstFlags.CallBinary, "min");
Add(Instruction.Multiply, InstFlags.OpBinary, "*", 1);
Add(Instruction.Negate, InstFlags.OpUnary, "-", 0);
Add(Instruction.PackHalf2x16, InstFlags.Call);
Add(Instruction.ReciprocalSquareRoot, InstFlags.CallUnary, "inversesqrt");
Add(Instruction.Return, InstFlags.OpNullary, "return");
Add(Instruction.Sine, InstFlags.CallUnary, "sin");
@ -110,6 +111,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
Add(Instruction.Subtract, InstFlags.OpBinary, "-", 2);
Add(Instruction.TextureSample, InstFlags.Call);
Add(Instruction.Truncate, InstFlags.CallUnary, "trunc");
Add(Instruction.UnpackHalf2x16High, InstFlags.Call);
Add(Instruction.UnpackHalf2x16Low, InstFlags.Call);
}
private static void Add(Instruction inst, InstFlags flags, string opName = null, int precedence = 0)
@ -158,12 +161,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
case Instruction.LoadConstant:
return GetLoadConstantExpr(context, operation);
case Instruction.PackHalf2x16:
return GetPackHalf2x16Expr(context, operation);
case Instruction.TextureSample:
return GetTextureSampleExpr(context, operation);
case Instruction.UnpackHalf2x16High:
return GetUnpackHalf2x16Expr(context, operation, 1);
case Instruction.UnpackHalf2x16Low:
return GetUnpackHalf2x16Expr(context, operation, 0);
}
InstInfo info = _infoTbl[(int)inst];
if (info.OpName == null)
{
throw new InvalidOperationException($"Unknown instruction \"{inst}\".");
}
if ((info.Flags & InstFlags.Call) != 0)
{
int arity = (int)(info.Flags & InstFlags.ArityMask);
@ -248,6 +265,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return OperandManager.GetConstantBufferName(context, operation.GetSource(0), offsetExpr);
}
private static string GetPackHalf2x16Expr(CodeGenContext context, AstOperation operation)
{
IAstNode src0 = operation.GetSource(0);
IAstNode src1 = operation.GetSource(1);
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0));
string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1));
return $"packHalf2x16(vec2({src0Expr}, {src1Expr}))";
}
private static string GetUnpackHalf2x16Expr(CodeGenContext context, AstOperation operation, int elem)
{
IAstNode src = operation.GetSource(0);
string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0));
return $"unpackHalf2x16({srcExpr}).{"xy".Substring(elem, 1)}";
}
private static string GetTextureSampleExpr(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Graphics.Shader.Decoders
{
enum FPHalfSwizzle
{
FP16 = 0,
FP32 = 1,
DupH0 = 2,
DupH1 = 3
}
}

View file

@ -0,0 +1,30 @@
using Ryujinx.Graphics.Shader.Instructions;
namespace Ryujinx.Graphics.Shader.Decoders
{
class OpCodeAluImm2x10 : OpCode, IOpCodeImm
{
public int Immediate { get; }
public OpCodeAluImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
{
int immH0 = opCode.Extract(20, 9);
int immH1 = opCode.Extract(30, 9);
bool negateH0 = opCode.Extract(29);
bool negateH1 = opCode.Extract(56);
if (negateH0)
{
immH0 |= 1 << 10;
}
if (negateH1)
{
immH1 |= 1 << 10;
}
Immediate = immH1 << 16 | immH0;
}
}
}

View file

@ -64,6 +64,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
Set("010010111011xx", InstEmit.Fsetp, typeof(OpCodeSetCbuf));
Set("0011011x1011xx", InstEmit.Fsetp, typeof(OpCodeFsetImm));
Set("010110111011xx", InstEmit.Fsetp, typeof(OpCodeSetReg));
Set("0111101x1xxxxx", InstEmit.Hadd2, typeof(OpCodeAluCbuf));
Set("0111101x0xxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm2x10));
Set("0010110xxxxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm32));
Set("0101110100010x", InstEmit.Hadd2, typeof(OpCodeAluReg));
Set("0111100x1xxxxx", InstEmit.Hmul2, typeof(OpCodeAluCbuf));
Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10));
Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32));
Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg));
Set("0100110010111x", InstEmit.I2F, typeof(OpCodeAluCbuf));
Set("0011100x10111x", InstEmit.I2F, typeof(OpCodeAluImm));
Set("0101110010111x", InstEmit.I2F, typeof(OpCodeAluReg));

View file

@ -3,8 +3,8 @@ 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
@ -175,6 +175,44 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(Register(op.Predicate0), p1Res);
}
public static void Hadd2(EmitterContext context)
{
Hadd2Hmul2Impl(context, isAdd: true);
}
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 OpCodeAluImm32 ? 52 : 32);
Operand[] srcA = GetHalfSrcA(context);
Operand[] srcB = GetHalfSrcB(context);
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 Mufu(EmitterContext context)
{
IOpCodeFArith op = (IOpCodeFArith)context.CurrOp;
@ -221,6 +259,124 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(GetDest(context), context.FPSaturate(res, op.Saturate));
}
private static Operand[] GetHalfSrcA(EmitterContext context)
{
OpCode op = context.CurrOp;
bool absoluteA = false, negateA = false;
if (op is IOpCodeCbuf || op is IOpCodeImm)
{
negateA = op.RawOpCode.Extract(43);
absoluteA = op.RawOpCode.Extract(44);
}
else if (op is IOpCodeReg)
{
absoluteA = op.RawOpCode.Extract(44);
}
else if (op is OpCodeAluImm32 && op.Emitter == Hadd2)
{
negateA = op.RawOpCode.Extract(56);
}
FPHalfSwizzle swizzle = (FPHalfSwizzle)context.CurrOp.RawOpCode.Extract(47, 2);
Operand[] operands = GetHalfSources(context, GetSrcA(context), swizzle);
return FPAbsNeg(context, operands, absoluteA, negateA);
}
private static Operand[] GetHalfSrcB(EmitterContext context)
{
OpCode op = context.CurrOp;
FPHalfSwizzle swizzle = FPHalfSwizzle.FP16;
if (!(op is OpCodeAluImm32))
{
swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(28, 2);
}
bool absoluteB = false, negateB = false;
if (op is IOpCodeReg)
{
absoluteB = op.RawOpCode.Extract(30);
negateB = op.RawOpCode.Extract(31);
}
else if (op is IOpCodeCbuf)
{
absoluteB = op.RawOpCode.Extract(54);
}
Operand[] operands = GetHalfSources(context, GetSrcB(context), swizzle);
return FPAbsNeg(context, operands, absoluteB, negateB);
}
private static Operand[] FPAbsNeg(EmitterContext context, Operand[] operands, bool abs, bool neg)
{
for (int index = 0; index < operands.Length; index++)
{
operands[index] = context.FPAbsNeg(operands[index], abs, neg);
}
return operands;
}
private static Operand[] GetHalfSources(EmitterContext context, Operand src, FPHalfSwizzle swizzle)
{
switch (swizzle)
{
case FPHalfSwizzle.FP16:
return new Operand[]
{
context.UnpackHalf2x16Low (src),
context.UnpackHalf2x16High(src)
};
case FPHalfSwizzle.FP32: return new Operand[] { src, src };
case FPHalfSwizzle.DupH0:
return new Operand[]
{
context.UnpackHalf2x16Low(src),
context.UnpackHalf2x16Low(src)
};
case FPHalfSwizzle.DupH1:
return new Operand[]
{
context.UnpackHalf2x16High(src),
context.UnpackHalf2x16High(src)
};
}
throw new ArgumentException($"Invalid swizzle \"{swizzle}\".");
}
private static Operand GetHalfPacked(EmitterContext context, Operand[] results)
{
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 FPHalfSwizzle.FP32: return results[0];
case FPHalfSwizzle.DupH0: return context.PackHalf2x16(results[0], results[0]);
case FPHalfSwizzle.DupH1: return context.PackHalf2x16(results[1], results[1]);
}
throw new ArgumentException($"Invalid swizzle \"{swizzle}\".");
}
private static Operand GetFPComparison(
EmitterContext context,
Condition cond,

View file

@ -11,22 +11,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
{
public static Operand GetZF(EmitterContext context)
{
return Register(new Register(0, RegisterType.Flag));
return Register(0, RegisterType.Flag);
}
public static Operand GetNF(EmitterContext context)
{
return Register(new Register(1, RegisterType.Flag));
return Register(1, RegisterType.Flag);
}
public static Operand GetCF(EmitterContext context)
{
return Register(new Register(2, RegisterType.Flag));
return Register(2, RegisterType.Flag);
}
public static Operand GetVF(EmitterContext context)
{
return Register(new Register(3, RegisterType.Flag));
return Register(3, RegisterType.Flag);
}
public static Operand GetDest(EmitterContext context)

View file

@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return context.Copy(Register(new Register(raIndex++, RegisterType.Gpr)));
return context.Copy(Register(raIndex++, RegisterType.Gpr));
}
Operand Rb()
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return context.Copy(Register(new Register(rbIndex++, RegisterType.Gpr)));
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
}
Operand arrayIndex = op.IsArray ? Ra() : null;
@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return Register(new Register(rdIndex++, RegisterType.Gpr));
return Register(rdIndex++, RegisterType.Gpr);
}
int textureHandle = op.Immediate;
@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
regIndex += index & 1;
}
return context.Copy(Register(new Register(regIndex, RegisterType.Gpr)));
return context.Copy(Register(regIndex, RegisterType.Gpr));
}
switch (op.Type)
@ -363,28 +363,35 @@ namespace Ryujinx.Graphics.Shader.Instructions
TextureType type = GetTextureType (op.Type);
TextureFlags flags = GetTextureFlags(op.Type);
Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) };
Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
int destIncrement = 0;
Operand GetDest()
{
int rdIndex;
int high = destIncrement >> 1;
int low = destIncrement & 1;
if (op.Rd1.IsRZ)
destIncrement++;
if (op.IsFp16)
{
rdIndex = op.Rd0.Index;
}
else if (op.Rd0.IsRZ)
{
rdIndex = op.Rd1.Index;
return high != 0
? (rd1[low] = Local())
: (rd0[low] = Local());
}
else
{
rdIndex = (destIncrement >> 1) != 0 ? op.Rd1.Index : op.Rd0.Index;
int rdIndex = high != 0 ? op.Rd1.Index : op.Rd0.Index;
if (rdIndex < RegisterConsts.RegisterZeroIndex)
{
rdIndex += low;
}
return Register(rdIndex, RegisterType.Gpr);
}
rdIndex += destIncrement++ & 1;
return Register(new Register(rdIndex, RegisterType.Gpr));
}
int textureHandle = op.Immediate;
@ -407,6 +414,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Add(operation);
}
}
if (op.IsFp16)
{
context.Copy(Register(op.Rd0), context.PackHalf2x16(rd0[0], rd0[1]));
context.Copy(Register(op.Rd1), context.PackHalf2x16(rd1[0], rd1[1]));
}
}
public static void Tld4(EmitterContext context)
@ -432,7 +445,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return context.Copy(Register(new Register(raIndex++, RegisterType.Gpr)));
return context.Copy(Register(raIndex++, RegisterType.Gpr));
}
Operand Rb()
@ -442,7 +455,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return context.Copy(Register(new Register(rbIndex++, RegisterType.Gpr)));
return context.Copy(Register(rbIndex++, RegisterType.Gpr));
}
Operand arrayIndex = op.IsArray ? Ra() : null;
@ -506,7 +519,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return Const(0);
}
return Register(new Register(rdIndex++, RegisterType.Gpr));
return Register(rdIndex++, RegisterType.Gpr);
}
int textureHandle = op.Immediate;

View file

@ -76,8 +76,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Subtract,
TextureSample,
Truncate,
UnpackDouble2x32,
UnpackHalf2x16,
UnpackDouble2x32High,
UnpackDouble2x32Low,
UnpackHalf2x16High,
UnpackHalf2x16Low,
Count,
FP = 1 << 16,

View file

@ -35,9 +35,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
return new Operand(OperandType.LocalVariable);
}
public static Operand Register(Register reg, int increment)
public static Operand Register(int index, RegisterType type)
{
return new Operand(new Register(reg.Index + increment, reg.Type));
return Register(new Register(index, type));
}
public static Operand Register(Register reg)

View file

@ -74,11 +74,14 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.MinimumU32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.Multiply, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Negate, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32);
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Truncate, VariableType.F32, VariableType.F32);
Add(Instruction.UnpackHalf2x16High, VariableType.F32, VariableType.U32);
Add(Instruction.UnpackHalf2x16Low, VariableType.F32, VariableType.U32);
}
private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes)

View file

@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
Operand src = Register(new Register(_header.DepthRegister, RegisterType.Gpr));
Operand src = Register(_header.DepthRegister, RegisterType.Gpr);
this.Copy(dest, src);
}
@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
Operand dest = Attribute(AttributeConsts.FragmentOutputColorBase + regIndex * 4);
Operand src = Register(new Register(regIndex, RegisterType.Gpr));
Operand src = Register(regIndex, RegisterType.Gpr);
this.Copy(dest, src);

View file

@ -371,6 +371,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.LoadConstant, Local(), a, b);
}
public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b)
{
return context.Add(Instruction.PackHalf2x16, Local(), a, b);
}
public static Operand Return(this EmitterContext context)
{
context.PrepareForReturn();
@ -392,5 +397,15 @@ namespace Ryujinx.Graphics.Shader.Translation
{
return context.Add(Instruction.ShiftRightU32, Local(), a, b);
}
public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a)
{
return context.Add(Instruction.UnpackHalf2x16High, Local(), a);
}
public static Operand UnpackHalf2x16Low(this EmitterContext context, Operand a)
{
return context.Add(Instruction.UnpackHalf2x16Low, Local(), a);
}
}
}