[GPU] Add more shader instructions, add support for rgb565 textures

This commit is contained in:
gdkchan 2018-04-10 16:50:32 -03:00
parent e9cfdef098
commit feb2680a6c
22 changed files with 817 additions and 238 deletions

View file

@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Gal
{ {
A8B8G8R8 = 0x8, A8B8G8R8 = 0x8,
A1B5G5R5 = 0x14, A1B5G5R5 = 0x14,
B5G6R5 = 0x15,
BC1 = 0x24, BC1 = 0x24,
BC2 = 0x25, BC2 = 0x25,
BC3 = 0x26 BC3 = 0x26

View file

@ -55,6 +55,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentException(nameof(Type)); throw new ArgumentException(nameof(Type));
} }
public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format)
{
switch (Format)
{
case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565);
}
throw new NotImplementedException(Format.ToString());
}
public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format) public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
{ {
switch (Format) switch (Format)

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Textures = new int[80]; Textures = new int[80];
} }
public void Set(int Index, GalTexture Tex) public void Set(int Index, GalTexture Texture)
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
@ -19,29 +19,38 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindTexture(TextureTarget.Texture2D, Handle); GL.BindTexture(TextureTarget.Texture2D, Handle);
int W = Tex.Width; const int Border = 0;
int H = Tex.Height;
byte[] Data = Tex.Data; if (IsCompressedTextureFormat(Texture.Format))
int Length = Data.Length;
if (IsCompressedTextureFormat(Tex.Format))
{ {
PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Tex.Format); PixelInternalFormat InternalFmt = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format);
GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data); GL.CompressedTexImage2D(
TextureTarget.Texture2D,
0,
InternalFmt,
Texture.Width,
Texture.Height,
Border,
Texture.Data.Length,
Texture.Data);
} }
else else
{ {
//TODO: Get those from Texture format. const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
const PixelInternalFormat Pif = PixelInternalFormat.Rgba;
const PixelFormat Pf = PixelFormat.Rgba; (PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format);
const PixelType Pt = PixelType.UnsignedByte; GL.TexImage2D(
TextureTarget.Texture2D,
GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Data); 0,
InternalFmt,
Texture.Width,
Texture.Height,
Border,
Format.Item1,
Format.Item2,
Texture.Data);
} }
} }

View file

@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render() public void Render()
{ {
FbRenderer.Render(); //FbRenderer.Render();
} }
public void SetWindowSize(int Width, int Height) public void SetWindowSize(int Width, int Height)

View file

@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Gal.Shader
private const int AttrStartIndex = 8; private const int AttrStartIndex = 8;
private const int TexStartIndex = 8; private const int TexStartIndex = 8;
public const string PositionOutAttrName = "position";
private const string InAttrName = "in_attr"; private const string InAttrName = "in_attr";
private const string OutAttrName = "out_attr"; private const string OutAttrName = "out_attr";
private const string UniformName = "c"; private const string UniformName = "c";
@ -62,10 +64,11 @@ namespace Ryujinx.Graphics.Gal.Shader
m_Gprs = new Dictionary<int, ShaderDeclInfo>(); m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>(); m_Preds = new Dictionary<int, ShaderDeclInfo>();
//FIXME: Only valid for vertex shaders.
if (ShaderType == GalShaderType.Fragment) if (ShaderType == GalShaderType.Fragment)
{ {
m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4)); m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4));
m_InAttributes.Add(7, new ShaderDeclInfo(PositionOutAttrName, -1, 0, 4));
} }
else else
{ {
@ -104,10 +107,9 @@ namespace Ryujinx.Graphics.Gal.Shader
Traverse(Op, Op.OperandB); Traverse(Op, Op.OperandB);
Traverse(Op, Op.OperandC); Traverse(Op, Op.OperandC);
if (Op.Inst == ShaderIrInst.Texr || if (Op.Inst == ShaderIrInst.Texq ||
Op.Inst == ShaderIrInst.Texg || Op.Inst == ShaderIrInst.Texs ||
Op.Inst == ShaderIrInst.Texb || Op.Inst == ShaderIrInst.Txlf)
Op.Inst == ShaderIrInst.Texa)
{ {
int Handle = ((ShaderIrOperImm)Op.OperandC).Value; int Handle = ((ShaderIrOperImm)Op.OperandC).Value;

View file

@ -31,40 +31,51 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>() InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
{ {
{ ShaderIrInst.And, GetAndExpr }, { ShaderIrInst.And, GetAndExpr },
{ ShaderIrInst.Asr, GetAsrExpr }, { ShaderIrInst.Asr, GetAsrExpr },
{ ShaderIrInst.Band, GetBandExpr }, { ShaderIrInst.Band, GetBandExpr },
{ ShaderIrInst.Bnot, GetBnotExpr }, { ShaderIrInst.Bnot, GetBnotExpr },
{ ShaderIrInst.Clt, GetCltExpr }, { ShaderIrInst.Ceil, GetCeilExpr },
{ ShaderIrInst.Ceq, GetCeqExpr }, { ShaderIrInst.Ceq, GetCeqExpr },
{ ShaderIrInst.Cle, GetCleExpr }, { ShaderIrInst.Cge, GetCgeExpr },
{ ShaderIrInst.Cgt, GetCgtExpr }, { ShaderIrInst.Cgt, GetCgtExpr },
{ ShaderIrInst.Cne, GetCneExpr }, { ShaderIrInst.Clamp, GetClampExpr },
{ ShaderIrInst.Cge, GetCgeExpr }, { ShaderIrInst.Cle, GetCleExpr },
{ ShaderIrInst.Exit, GetExitExpr }, { ShaderIrInst.Clt, GetCltExpr },
{ ShaderIrInst.Fabs, GetFabsExpr }, { ShaderIrInst.Cne, GetCneExpr },
{ ShaderIrInst.Fadd, GetFaddExpr }, { ShaderIrInst.Exit, GetExitExpr },
{ ShaderIrInst.Fcos, GetFcosExpr }, { ShaderIrInst.Fabs, GetFabsExpr },
{ ShaderIrInst.Fex2, GetFex2Expr }, { ShaderIrInst.Fadd, GetFaddExpr },
{ ShaderIrInst.Ffma, GetFfmaExpr }, { ShaderIrInst.Fceq, GetCeqExpr },
{ ShaderIrInst.Flg2, GetFlg2Expr }, { ShaderIrInst.Fcge, GetCgeExpr },
{ ShaderIrInst.Fmul, GetFmulExpr }, { ShaderIrInst.Fcgt, GetCgtExpr },
{ ShaderIrInst.Fneg, GetFnegExpr }, { ShaderIrInst.Fcle, GetCleExpr },
{ ShaderIrInst.Frcp, GetFrcpExpr }, { ShaderIrInst.Fclt, GetCltExpr },
{ ShaderIrInst.Frsq, GetFrsqExpr }, { ShaderIrInst.Fcne, GetCneExpr },
{ ShaderIrInst.Fsin, GetFsinExpr }, { ShaderIrInst.Fcos, GetFcosExpr },
{ ShaderIrInst.Ipa, GetIpaExpr }, { ShaderIrInst.Fex2, GetFex2Expr },
{ ShaderIrInst.Kil, GetKilExpr }, { ShaderIrInst.Ffma, GetFfmaExpr },
{ ShaderIrInst.Lsr, GetLsrExpr }, { ShaderIrInst.Flg2, GetFlg2Expr },
{ ShaderIrInst.Not, GetNotExpr }, { ShaderIrInst.Floor, GetFloorExpr },
{ ShaderIrInst.Or, GetOrExpr }, { ShaderIrInst.Fmul, GetFmulExpr },
{ ShaderIrInst.Stof, GetStofExpr }, { ShaderIrInst.Fneg, GetFnegExpr },
{ ShaderIrInst.Utof, GetUtofExpr }, { ShaderIrInst.Frcp, GetFrcpExpr },
{ ShaderIrInst.Texr, GetTexrExpr }, { ShaderIrInst.Frsq, GetFrsqExpr },
{ ShaderIrInst.Texg, GetTexgExpr }, { ShaderIrInst.Fsin, GetFsinExpr },
{ ShaderIrInst.Texb, GetTexbExpr }, { ShaderIrInst.Ftos, GetFtosExpr },
{ ShaderIrInst.Texa, GetTexaExpr }, { ShaderIrInst.Ftou, GetFtouExpr },
{ ShaderIrInst.Xor, GetXorExpr }, { ShaderIrInst.Ipa, GetIpaExpr },
{ ShaderIrInst.Kil, GetKilExpr },
{ ShaderIrInst.Lsr, GetLsrExpr },
{ ShaderIrInst.Not, GetNotExpr },
{ ShaderIrInst.Or, GetOrExpr },
{ ShaderIrInst.Stof, GetStofExpr },
{ ShaderIrInst.Texq, GetTexqExpr },
{ ShaderIrInst.Texs, GetTexsExpr },
{ ShaderIrInst.Trunc, GetTruncExpr },
{ ShaderIrInst.Txlf, GetTxlfExpr },
{ ShaderIrInst.Utof, GetUtofExpr },
{ ShaderIrInst.Xor, GetXorExpr }
}; };
} }
@ -117,11 +128,21 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclInAttributes() private void PrintDeclInAttributes()
{ {
if (Decl.ShaderType == GalShaderType.Fragment)
{
SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";");
}
PrintDeclAttributes(Decl.InAttributes.Values, "in"); PrintDeclAttributes(Decl.InAttributes.Values, "in");
} }
private void PrintDeclOutAttributes() private void PrintDeclOutAttributes()
{ {
if (Decl.ShaderType == GalShaderType.Vertex)
{
SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";");
}
PrintDeclAttributes(Decl.OutAttributes.Values, "out"); PrintDeclAttributes(Decl.OutAttributes.Values, "out");
} }
@ -133,7 +154,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
if (DeclInfo.Index >= 0) if (DeclInfo.Index >= 0)
{ {
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};"); SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";");
Count++; Count++;
} }
@ -222,7 +243,14 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Node is ShaderIrCond Cond) if (Node is ShaderIrCond Cond)
{ {
string SubScopeName = "if (" + GetSrcExpr(Cond.Pred, true) + ")"; string IfExpr = GetSrcExpr(Cond.Pred, true);
if (Cond.Not)
{
IfExpr = "!(" + IfExpr + ")";
}
string SubScopeName = "if (" + IfExpr + ")";
PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child); PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
} }
@ -236,6 +264,16 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
else if (Node is ShaderIrOp Op) else if (Node is ShaderIrOp Op)
{ {
if (Op.Inst == ShaderIrInst.Exit)
{
//Do everything that needs to be done before
//the shader ends here.
if (Decl.ShaderType == GalShaderType.Vertex)
{
SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;");
}
}
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
} }
else else
@ -321,10 +359,9 @@ namespace Ryujinx.Graphics.Gal.Shader
return true; return true;
case ShaderIrInst.Ipa: case ShaderIrInst.Ipa:
case ShaderIrInst.Texr: case ShaderIrInst.Texq:
case ShaderIrInst.Texg: case ShaderIrInst.Texs:
case ShaderIrInst.Texb: case ShaderIrInst.Txlf:
case ShaderIrInst.Texa:
return false; return false;
} }
@ -349,11 +386,6 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperAbuf Abuf) private string GetName(ShaderIrOperAbuf Abuf)
{ {
if (Abuf.Offs == GlslDecl.GlPositionWAttr && Decl.ShaderType == GalShaderType.Fragment)
{
return "(1f / gl_FragCoord.w)";
}
if (Abuf.Offs == GlslDecl.VertexIdAttr) if (Abuf.Offs == GlslDecl.VertexIdAttr)
{ {
return "gl_VertexID"; return "gl_VertexID";
@ -437,6 +469,10 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!");
private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil");
private string GetClampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp");
private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
@ -458,6 +494,8 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
@ -468,6 +506,16 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin");
private string GetFtosExpr(ShaderIrOp Op)
{
return "int(" + GetOperExpr(Op, Op.OperandA) + ")";
}
private string GetFtouExpr(ShaderIrOp Op)
{
return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
}
private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA); private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
private string GetKilExpr(ShaderIrOp Op) => "discard"; private string GetKilExpr(ShaderIrOp Op) => "discard";
@ -487,6 +535,54 @@ namespace Ryujinx.Graphics.Gal.Shader
return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; return "float(" + GetOperExpr(Op, Op.OperandA) + ")";
} }
private string GetTexqExpr(ShaderIrOp Op)
{
ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData;
string Ch = "xyzw".Substring(Meta.Elem, 1);
if (Meta.Info == ShaderTexqInfo.Dimension)
{
string Sampler = GetTexSamplerName(Op);
string Lod = GetOperExpr(Op, Op.OperandA); //???
return "textureSize(" + Sampler + ", " + Lod + ")." + Ch;
}
else
{
throw new NotImplementedException(Meta.Info.ToString());
}
}
private string GetTexsExpr(ShaderIrOp Op)
{
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
string Sampler = GetTexSamplerName(Op);
string Coords = GetTexSamplerCoords(Op);
string Ch = "rgba".Substring(Meta.Elem, 1);
return "texture(" + Sampler + ", " + Coords + ")." + Ch;
}
private string GetTxlfExpr(ShaderIrOp Op)
{
ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
string Sampler = GetTexSamplerName(Op);
string Coords = GetITexSamplerCoords(Op);
string Ch = "rgba".Substring(Meta.Elem, 1);
return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch;
}
private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc");
private string GetUtofExpr(ShaderIrOp Op) private string GetUtofExpr(ShaderIrOp Op)
{ {
return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
@ -499,6 +595,13 @@ namespace Ryujinx.Graphics.Gal.Shader
return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")";
} }
private string GetTernaryCall(ShaderIrOp Op, string FuncName)
{
return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " +
GetOperExpr(Op, Op.OperandB) + ", " +
GetOperExpr(Op, Op.OperandC) + ")";
}
private string GetUnaryExpr(ShaderIrOp Op, string Opr) private string GetUnaryExpr(ShaderIrOp Op, string Opr)
{ {
return Opr + GetOperExpr(Op, Op.OperandA); return Opr + GetOperExpr(Op, Op.OperandA);
@ -517,16 +620,6 @@ namespace Ryujinx.Graphics.Gal.Shader
GetOperExpr(Op, Op.OperandC); GetOperExpr(Op, Op.OperandC);
} }
private string GetTexrExpr(ShaderIrOp Op) => GetTexExpr(Op, 'r');
private string GetTexgExpr(ShaderIrOp Op) => GetTexExpr(Op, 'g');
private string GetTexbExpr(ShaderIrOp Op) => GetTexExpr(Op, 'b');
private string GetTexaExpr(ShaderIrOp Op) => GetTexExpr(Op, 'a');
private string GetTexExpr(ShaderIrOp Op, char Ch)
{
return $"texture({GetTexSamplerName(Op)}, {GetTexSamplerCoords(Op)}).{Ch}";
}
private string GetTexSamplerName(ShaderIrOp Op) private string GetTexSamplerName(ShaderIrOp Op)
{ {
ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC;
@ -547,6 +640,12 @@ namespace Ryujinx.Graphics.Gal.Shader
GetOperExpr(Op, Op.OperandB) + ")"; GetOperExpr(Op, Op.OperandB) + ")";
} }
private string GetITexSamplerCoords(ShaderIrOp Op)
{
return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
GetOperExpr(Op, Op.OperandB) + ")";
}
private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
{ {
return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); return GetExprWithCast(Op, Oper, GetSrcExpr(Oper));
@ -571,13 +670,31 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
//For integer immediates being used as float, switch (Src)
//it's better (for readability) to just return the float value.
if (Src is ShaderIrOperImm Imm && DstType == OperType.F32)
{ {
float Value = BitConverter.Int32BitsToSingle(Imm.Value); case ShaderIrOperGpr Gpr:
{
//When the Gpr is ZR, just return the 0 value directly,
//since the float encoding for 0 is 0.
if (Gpr.IsConst)
{
return "0";
}
break;
}
return Value.ToString(CultureInfo.InvariantCulture) + "f"; case ShaderIrOperImm Imm:
{
//For integer immediates being used as float,
//it's better (for readability) to just return the float value.
if (DstType == OperType.F32)
{
float Value = BitConverter.Int32BitsToSingle(Imm.Value);
return Value.ToString(CultureInfo.InvariantCulture) + "f";
}
break;
}
} }
switch (DstType) switch (DstType)
@ -592,12 +709,20 @@ namespace Ryujinx.Graphics.Gal.Shader
private static OperType GetDstNodeType(ShaderIrNode Node) private static OperType GetDstNodeType(ShaderIrNode Node)
{ {
//Special case instructions with the result type different
//from the input types (like integer <-> float conversion) here.
if (Node is ShaderIrOp Op) if (Node is ShaderIrOp Op)
{ {
switch (Op.Inst) switch (Op.Inst)
{ {
case ShaderIrInst.Stof: return OperType.F32; case ShaderIrInst.Stof:
case ShaderIrInst.Utof: return OperType.F32; case ShaderIrInst.Txlf:
case ShaderIrInst.Utof:
return OperType.F32;
case ShaderIrInst.Ftos:
case ShaderIrInst.Ftou:
return OperType.I32;
} }
} }

View file

@ -81,6 +81,21 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
} }
public static void Isetp_C(ShaderIrBlock Block, long OpCode)
{
EmitIsetp(Block, OpCode, ShaderOper.CR);
}
public static void Isetp_I(ShaderIrBlock Block, long OpCode)
{
EmitIsetp(Block, OpCode, ShaderOper.Imm);
}
public static void Isetp_R(ShaderIrBlock Block, long OpCode)
{
EmitIsetp(Block, OpCode, ShaderOper.RR);
}
public static void Lop32i(ShaderIrBlock Block, long OpCode) public static void Lop32i(ShaderIrBlock Block, long OpCode)
{ {
int SubOp = (int)(OpCode >> 53) & 3; int SubOp = (int)(OpCode >> 53) & 3;
@ -258,6 +273,16 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
EmitSetp(Block, OpCode, true, Oper);
}
private static void EmitIsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
EmitSetp(Block, OpCode, false, Oper);
}
private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
{ {
bool Aa = ((OpCode >> 7) & 1) != 0; bool Aa = ((OpCode >> 7) & 1) != 0;
bool Np = ((OpCode >> 42) & 1) != 0; bool Np = ((OpCode >> 42) & 1) != 0;
@ -269,17 +294,28 @@ namespace Ryujinx.Graphics.Gal.Shader
switch (Oper) switch (Oper)
{ {
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
case ShaderOper.Imm: OperB = GetOperImm19_20 (OpCode); break;
case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper)); default: throw new ArgumentException(nameof(Oper));
} }
ShaderIrInst CmpInst = GetCmp(OpCode); ShaderIrInst CmpInst;
ShaderIrOp Op = new ShaderIrOp(CmpInst, if (IsFloat)
GetAluAbsNeg(OperA, Aa, Na), {
GetAluAbs (OperB, Ab)); OperA = GetAluAbsNeg(OperA, Aa, Na);
OperB = GetAluAbs (OperB, Ab);
CmpInst = GetCmpF(OpCode);
}
else
{
CmpInst = GetCmp(OpCode);
}
ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
ShaderIrOperPred P0Node = GetOperPred3 (OpCode); ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
ShaderIrOperPred P1Node = GetOperPred0 (OpCode); ShaderIrOperPred P1Node = GetOperPred0 (OpCode);

View file

@ -60,7 +60,17 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff); return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff);
} }
public static ShaderIrNode GetOperImm19_20(long OpCode) public static ShaderIrOperImm GetOperImm13_36(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
}
public static ShaderIrOperImm GetOperImm32_20(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 20));
}
public static ShaderIrOperImm GetOperImm19_20(long OpCode)
{ {
int Value = (int)(OpCode >> 20) & 0x7ffff; int Value = (int)(OpCode >> 20) & 0x7ffff;
@ -74,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperImm((int)Value); return new ShaderIrOperImm((int)Value);
} }
public static ShaderIrNode GetOperImmf19_20(long OpCode) public static ShaderIrOperImmf GetOperImmf19_20(long OpCode)
{ {
uint Imm = (uint)(OpCode >> 20) & 0x7ffff; uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
@ -92,16 +102,6 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperImmf(Value); return new ShaderIrOperImmf(Value);
} }
public static ShaderIrOperImm GetOperImm13_36(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
}
public static ShaderIrOperImm GetOperImm32_20(long OpCode)
{
return new ShaderIrOperImm((int)(OpCode >> 20));
}
public static ShaderIrOperPred GetOperPred3(long OpCode) public static ShaderIrOperPred GetOperPred3(long OpCode)
{ {
return new ShaderIrOperPred((int)(OpCode >> 3) & 7); return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
@ -130,23 +130,38 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
public static ShaderIrInst GetCmp(long OpCode) public static ShaderIrInst GetCmp(long OpCode)
{
switch ((int)(OpCode >> 49) & 7)
{
case 1: return ShaderIrInst.Clt;
case 2: return ShaderIrInst.Ceq;
case 3: return ShaderIrInst.Cle;
case 4: return ShaderIrInst.Cgt;
case 5: return ShaderIrInst.Cne;
case 6: return ShaderIrInst.Cge;
}
throw new ArgumentException(nameof(OpCode));
}
public static ShaderIrInst GetCmpF(long OpCode)
{ {
switch ((int)(OpCode >> 48) & 0xf) switch ((int)(OpCode >> 48) & 0xf)
{ {
case 0x1: return ShaderIrInst.Clt; case 0x1: return ShaderIrInst.Fclt;
case 0x2: return ShaderIrInst.Ceq; case 0x2: return ShaderIrInst.Fceq;
case 0x3: return ShaderIrInst.Cle; case 0x3: return ShaderIrInst.Fcle;
case 0x4: return ShaderIrInst.Cgt; case 0x4: return ShaderIrInst.Fcgt;
case 0x5: return ShaderIrInst.Cne; case 0x5: return ShaderIrInst.Fcne;
case 0x6: return ShaderIrInst.Cge; case 0x6: return ShaderIrInst.Fcge;
case 0x7: return ShaderIrInst.Cnum; case 0x7: return ShaderIrInst.Fcnum;
case 0x8: return ShaderIrInst.Cnan; case 0x8: return ShaderIrInst.Fcnan;
case 0x9: return ShaderIrInst.Cltu; case 0x9: return ShaderIrInst.Fcltu;
case 0xa: return ShaderIrInst.Cequ; case 0xa: return ShaderIrInst.Fcequ;
case 0xb: return ShaderIrInst.Cleu; case 0xb: return ShaderIrInst.Fcleu;
case 0xc: return ShaderIrInst.Cgtu; case 0xc: return ShaderIrInst.Fcgtu;
case 0xd: return ShaderIrInst.Cneu; case 0xd: return ShaderIrInst.Fcneu;
case 0xe: return ShaderIrInst.Cgeu; case 0xe: return ShaderIrInst.Fcgeu;
} }
throw new ArgumentException(nameof(OpCode)); throw new ArgumentException(nameof(OpCode));
@ -170,7 +185,9 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Pred.Index != ShaderIrOperPred.UnusedIndex) if (Pred.Index != ShaderIrOperPred.UnusedIndex)
{ {
Node = new ShaderIrCond(Pred, Node); bool Inv = ((OpCode >> 19) & 1) != 0;
Node = new ShaderIrCond(Pred, Node, Inv);
} }
return Node; return Node;

View file

@ -36,23 +36,55 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
} }
public static void Texq(ShaderIrBlock Block, long OpCode)
{
ShaderIrNode OperD = GetOperGpr0(OpCode);
ShaderIrNode OperA = GetOperGpr8(OpCode);
ShaderTexqInfo Info = (ShaderTexqInfo)((OpCode >> 22) & 0x1f);
ShaderIrMetaTexq Meta0 = new ShaderIrMetaTexq(Info, 0);
ShaderIrMetaTexq Meta1 = new ShaderIrMetaTexq(Info, 1);
ShaderIrNode OperC = GetOperImm13_36(OpCode);
ShaderIrOp Op0 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta0);
ShaderIrOp Op1 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta1);
Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Op0), OpCode));
Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
}
public static void Texs(ShaderIrBlock Block, long OpCode) public static void Texs(ShaderIrBlock Block, long OpCode)
{
EmitTex(Block, OpCode, ShaderIrInst.Texs);
}
public static void Tlds(ShaderIrBlock Block, long OpCode)
{
EmitTex(Block, OpCode, ShaderIrInst.Txlf);
}
private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
{ {
//TODO: Support other formats. //TODO: Support other formats.
ShaderIrNode OperA = GetOperGpr8 (OpCode); ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperGpr20 (OpCode); ShaderIrNode OperB = GetOperGpr20 (OpCode);
ShaderIrNode OperC = GetOperGpr28 (OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode);
ShaderIrNode OperD = GetOperImm13_36(OpCode);
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
{ {
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texr + Ch, OperA, OperB, OperD); ShaderIrOperGpr Dst = (Ch >> 1) != 0
? GetOperGpr28(OpCode)
: GetOperGpr0 (OpCode);
ShaderIrOperGpr Dst = GetOperGpr0(OpCode); Dst.Index += Ch & 1;
Dst.Index += Ch; ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
Block.AddNode(new ShaderIrAsg(Dst, Op)); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
} }
} }
} }

View file

@ -25,6 +25,36 @@ namespace Ryujinx.Graphics.Gal.Shader
F64 = 3 F64 = 3
} }
public static void F2f_C(ShaderIrBlock Block, long OpCode)
{
EmitF2f(Block, OpCode, ShaderOper.CR);
}
public static void F2f_I(ShaderIrBlock Block, long OpCode)
{
EmitF2f(Block, OpCode, ShaderOper.Immf);
}
public static void F2f_R(ShaderIrBlock Block, long OpCode)
{
EmitF2f(Block, OpCode, ShaderOper.RR);
}
public static void F2i_C(ShaderIrBlock Block, long OpCode)
{
EmitF2i(Block, OpCode, ShaderOper.CR);
}
public static void F2i_I(ShaderIrBlock Block, long OpCode)
{
EmitF2i(Block, OpCode, ShaderOper.Immf);
}
public static void F2i_R(ShaderIrBlock Block, long OpCode)
{
EmitF2i(Block, OpCode, ShaderOper.RR);
}
public static void I2f_C(ShaderIrBlock Block, long OpCode) public static void I2f_C(ShaderIrBlock Block, long OpCode)
{ {
EmitI2f(Block, OpCode, ShaderOper.CR); EmitI2f(Block, OpCode, ShaderOper.CR);
@ -40,6 +70,131 @@ namespace Ryujinx.Graphics.Gal.Shader
EmitI2f(Block, OpCode, ShaderOper.RR); EmitI2f(Block, OpCode, ShaderOper.RR);
} }
public static void Mov_C(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode));
}
public static void Mov_I(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperImm Imm = GetOperImm19_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
public static void Mov_R(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
}
public static void Mov32i(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
bool Na = ((OpCode >> 45) & 1) != 0;
bool Aa = ((OpCode >> 49) & 1) != 0;
ShaderIrNode OperA;
switch (Oper)
{
case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break;
case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
OperA = GetAluAbsNeg(OperA, Aa, Na);
ShaderIrInst RoundInst = GetRoundInst(OpCode);
if (RoundInst != ShaderIrInst.Invalid)
{
OperA = new ShaderIrOp(RoundInst, OperA);
}
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
}
private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
IntType Type = GetIntType(OpCode);
if (Type == IntType.U64 ||
Type == IntType.S64)
{
//TODO: 64-bits support.
//Note: GLSL doesn't support 64-bits integers.
throw new NotImplementedException();
}
bool Na = ((OpCode >> 45) & 1) != 0;
bool Aa = ((OpCode >> 49) & 1) != 0;
ShaderIrNode OperA;
switch (Oper)
{
case ShaderOper.CR: OperA = GetOperCbuf34 (OpCode); break;
case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
case ShaderOper.RR: OperA = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
OperA = GetAluAbsNeg(OperA, Aa, Na);
ShaderIrInst RoundInst = GetRoundInst(OpCode);
if (RoundInst != ShaderIrInst.Invalid)
{
OperA = new ShaderIrOp(RoundInst, OperA);
}
bool Signed = Type >= IntType.S8;
int Size = 8 << ((int)Type & 3);
if (Size < 32)
{
uint Mask = uint.MaxValue >> (32 - Size);
float CMin = 0;
float CMax = Mask;
if (Signed)
{
uint HalfMask = Mask >> 1;
CMin -= HalfMask + 1;
CMax = HalfMask;
}
ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin);
ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax);
OperA = new ShaderIrOp(ShaderIrInst.Clamp, OperA, IMin, IMax);
}
ShaderIrInst Inst = Signed
? ShaderIrInst.Ftos
: ShaderIrInst.Ftou;
ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{ {
IntType Type = GetIntType(OpCode); IntType Type = GetIntType(OpCode);
@ -76,18 +231,16 @@ namespace Ryujinx.Graphics.Gal.Shader
int Size = 8 << ((int)Type & 3); int Size = 8 << ((int)Type & 3);
ulong Mask = ulong.MaxValue >> (64 - Size);
int Mask32 = (int)Mask;
if (Shift != 0) if (Shift != 0)
{ {
OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift)); OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift));
} }
if (Mask != uint.MaxValue) if (Size < 32)
{ {
OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm(Mask32)); uint Mask = uint.MaxValue >> (32 - Size);
OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask));
} }
ShaderIrInst Inst = Signed ShaderIrInst Inst = Signed
@ -99,13 +252,6 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
} }
public static void Mov32i(ShaderIrBlock Block, long OpCode)
{
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
private static IntType GetIntType(long OpCode) private static IntType GetIntType(long OpCode)
{ {
bool Signed = ((OpCode >> 13) & 1) != 0; bool Signed = ((OpCode >> 13) & 1) != 0;
@ -124,5 +270,17 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
return (FloatType)((OpCode >> 8) & 3); return (FloatType)((OpCode >> 8) & 3);
} }
private static ShaderIrInst GetRoundInst(long OpCode)
{
switch ((OpCode >> 39) & 3)
{
case 1: return ShaderIrInst.Floor;
case 2: return ShaderIrInst.Ceil;
case 3: return ShaderIrInst.Trunc;
}
return ShaderIrInst.Invalid;
}
} }
} }

View file

@ -8,6 +8,15 @@ namespace Ryujinx.Graphics.Gal.Shader
while (Offset + 2 <= Code.Length) while (Offset + 2 <= Code.Length)
{ {
//Ignore scheduling instructions, which are
//written every 32 bytes.
if ((Offset & 7) == 0)
{
Offset += 2;
continue;
}
uint Word0 = (uint)Code[Offset++]; uint Word0 = (uint)Code[Offset++];
uint Word1 = (uint)Code[Offset++]; uint Word1 = (uint)Code[Offset++];

View file

@ -5,10 +5,13 @@ namespace Ryujinx.Graphics.Gal.Shader
public ShaderIrNode Pred { get; set; } public ShaderIrNode Pred { get; set; }
public ShaderIrNode Child { get; set; } public ShaderIrNode Child { get; set; }
public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child) public bool Not { get; private set; }
public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child, bool Not)
{ {
this.Pred = Pred; this.Pred = Pred;
this.Child = Child; this.Child = Child;
this.Not = Not;
} }
} }
} }

View file

@ -2,53 +2,66 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
enum ShaderIrInst enum ShaderIrInst
{ {
Invalid,
B_Start, B_Start,
Band, Band,
Bnot, Bnot,
Bor, Bor,
Bxor, Bxor,
Clt,
Ceq,
Cle,
Cgt,
Cne,
Cge,
Cnum,
Cnan,
Cltu,
Cequ,
Cleu,
Cgtu,
Cneu,
Cgeu,
B_End, B_End,
F_Start, F_Start,
Ceil,
Clamp,
Fabs, Fabs,
Fadd, Fadd,
Fceq,
Fcequ,
Fcge,
Fcgeu,
Fcgt,
Fcgtu,
Fcle,
Fcleu,
Fclt,
Fcltu,
Fcnan,
Fcne,
Fcneu,
Fcnum,
Fcos, Fcos,
Fex2, Fex2,
Ffma, Ffma,
Flg2, Flg2,
Floor,
Fmul, Fmul,
Fneg, Fneg,
Frcp, Frcp,
Frsq, Frsq,
Fsin, Fsin,
Ftos,
Ftou,
Ipa, Ipa,
Texr, Texs,
Texg, Trunc,
Texb,
Texa,
F_End, F_End,
I_Start, I_Start,
And, And,
Asr, Asr,
Ceq,
Cge,
Cgt,
Cle,
Clt,
Cne,
Lsr, Lsr,
Not, Not,
Or, Or,
Stof, Stof,
Texq,
Txlf,
Utof, Utof,
Xor, Xor,
I_End, I_End,

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrMeta { }
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrMetaTex : ShaderIrMeta
{
public int Elem { get; private set; }
public ShaderIrMetaTex(int Elem)
{
this.Elem = Elem;
}
}
}

View file

@ -0,0 +1,15 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrMetaTexq : ShaderIrMeta
{
public ShaderTexqInfo Info { get; private set; }
public int Elem { get; private set; }
public ShaderIrMetaTexq(ShaderTexqInfo Info, int Elem)
{
this.Info = Info;
this.Elem = Elem;
}
}
}

View file

@ -6,17 +6,20 @@ namespace Ryujinx.Graphics.Gal.Shader
public ShaderIrNode OperandA { get; set; } public ShaderIrNode OperandA { get; set; }
public ShaderIrNode OperandB { get; set; } public ShaderIrNode OperandB { get; set; }
public ShaderIrNode OperandC { get; set; } public ShaderIrNode OperandC { get; set; }
public ShaderIrMeta MetaData { get; set; }
public ShaderIrOp( public ShaderIrOp(
ShaderIrInst Inst, ShaderIrInst Inst,
ShaderIrNode OperandA = null, ShaderIrNode OperandA = null,
ShaderIrNode OperandB = null, ShaderIrNode OperandB = null,
ShaderIrNode OperandC = null) ShaderIrNode OperandC = null,
ShaderIrMeta MetaData = null)
{ {
this.Inst = Inst; this.Inst = Inst;
this.OperandA = OperandA; this.OperandA = OperandA;
this.OperandB = OperandB; this.OperandB = OperandB;
this.OperandC = OperandC; this.OperandC = OperandC;
this.MetaData = MetaData;
} }
} }
} }

View file

@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Gal.Shader
#region Instructions #region Instructions
Set("111000110000xx", ShaderDecode.Exit); Set("111000110000xx", ShaderDecode.Exit);
Set("0100110010101x", ShaderDecode.F2f_C);
Set("0011100x10101x", ShaderDecode.F2f_I);
Set("0101110010101x", ShaderDecode.F2f_R);
Set("0100110010110x", ShaderDecode.F2i_C);
Set("0011100x10110x", ShaderDecode.F2i_I);
Set("0101110010110x", ShaderDecode.F2i_R);
Set("0100110001011x", ShaderDecode.Fadd_C); Set("0100110001011x", ShaderDecode.Fadd_C);
Set("0011100x01011x", ShaderDecode.Fadd_I); Set("0011100x01011x", ShaderDecode.Fadd_I);
Set("0101110001011x", ShaderDecode.Fadd_R); Set("0101110001011x", ShaderDecode.Fadd_R);
@ -31,16 +37,24 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0011100x10111x", ShaderDecode.I2f_I); Set("0011100x10111x", ShaderDecode.I2f_I);
Set("0101110010111x", ShaderDecode.I2f_R); Set("0101110010111x", ShaderDecode.I2f_R);
Set("11100000xxxxxx", ShaderDecode.Ipa); Set("11100000xxxxxx", ShaderDecode.Ipa);
Set("010010110110xx", ShaderDecode.Isetp_C);
Set("0011011x0110xx", ShaderDecode.Isetp_I);
Set("010110110110xx", ShaderDecode.Isetp_R);
Set("111000110011xx", ShaderDecode.Kil); Set("111000110011xx", ShaderDecode.Kil);
Set("1110111111011x", ShaderDecode.Ld_A); Set("1110111111011x", ShaderDecode.Ld_A);
Set("000001xxxxxxxx", ShaderDecode.Lop32i); Set("000001xxxxxxxx", ShaderDecode.Lop32i);
Set("0100110010011x", ShaderDecode.Mov_C);
Set("0011100x10011x", ShaderDecode.Mov_I);
Set("0101110010011x", ShaderDecode.Mov_R);
Set("000000010000xx", ShaderDecode.Mov32i); Set("000000010000xx", ShaderDecode.Mov32i);
Set("0101000010000x", ShaderDecode.Mufu); Set("0101000010000x", ShaderDecode.Mufu);
Set("0100110000101x", ShaderDecode.Shr_C); Set("0100110000101x", ShaderDecode.Shr_C);
Set("0011100x00101x", ShaderDecode.Shr_I); Set("0011100x00101x", ShaderDecode.Shr_I);
Set("0101110000101x", ShaderDecode.Shr_R); Set("0101110000101x", ShaderDecode.Shr_R);
Set("1110111111110x", ShaderDecode.St_A); Set("1110111111110x", ShaderDecode.St_A);
Set("1101111101001x", ShaderDecode.Texq);
Set("1101100xxxxxxx", ShaderDecode.Texs); Set("1101100xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds);
#endregion #endregion
} }

View file

@ -3,9 +3,9 @@ namespace Ryujinx.Graphics.Gal.Shader
enum ShaderOper enum ShaderOper
{ {
CR, CR,
RC,
RR,
Imm, Imm,
Immf Immf,
RC,
RR
} }
} }

View file

@ -7,13 +7,22 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
private struct UseSite private struct UseSite
{ {
public object Parent; public ShaderIrNode Parent { get; private set; }
public ShaderIrCond Cond { get; private set; }
public int OperIndex; public int UseIndex { get; private set; }
public UseSite(object Parent, int OperIndex) public int OperIndex { get; private set; }
public UseSite(
ShaderIrNode Parent,
ShaderIrCond Cond,
int UseIndex,
int OperIndex)
{ {
this.Parent = Parent; this.Parent = Parent;
this.Cond = Cond;
this.UseIndex = UseIndex;
this.OperIndex = OperIndex; this.OperIndex = OperIndex;
} }
} }
@ -24,7 +33,9 @@ namespace Ryujinx.Graphics.Gal.Shader
public int AsgIndex { get; private set; } public int AsgIndex { get; private set; }
private bool Propagate; public int LastSiteIndex { get; private set; }
public ShaderIrCond Cond { get; private set; }
private List<UseSite> Sites; private List<UseSite> Sites;
@ -35,6 +46,11 @@ namespace Ryujinx.Graphics.Gal.Shader
public void AddUseSite(UseSite Site) public void AddUseSite(UseSite Site)
{ {
if (LastSiteIndex < Site.UseIndex)
{
LastSiteIndex = Site.UseIndex;
}
Sites.Add(Site); Sites.Add(Site);
} }
@ -42,14 +58,27 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
//This happens when a untiliazied register is used, //This happens when a untiliazied register is used,
//this usually indicates a decoding error, but may also //this usually indicates a decoding error, but may also
//be cased by bogus programs (?). In any case, we just //be caused by bogus programs (?). In any case, we just
//keep the unitialized access and avoid trying to propagate //keep the unitialized access and avoid trying to propagate
//the expression (since we can't propagate what doesn't yet exist). //the expression (since we can't propagate what doesn't yet exist).
if (Asg == null || !Propagate) if (Asg == null)
{ {
return false; return false;
} }
if (Cond != null)
{
//If the assignment is conditional, we can only propagate
//to the use sites that shares the same condition of the assignment.
foreach (UseSite Site in Sites)
{
if (!IsSameCondition(Cond, Site.Cond))
{
return false;
}
}
}
if (Sites.Count > 0) if (Sites.Count > 0)
{ {
foreach (UseSite Site in Sites) foreach (UseSite Site in Sites)
@ -89,11 +118,13 @@ namespace Ryujinx.Graphics.Gal.Shader
return true; return true;
} }
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate) public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
{ {
this.Asg = Asg; this.Asg = Asg;
this.AsgIndex = AsgIndex; this.AsgIndex = AsgIndex;
this.Propagate = Propagate; this.Cond = Cond;
LastSiteIndex = 0;
Sites.Clear(); Sites.Clear();
} }
@ -137,38 +168,52 @@ namespace Ryujinx.Graphics.Gal.Shader
return GetUse(GetPredKey(PredIndex)); return GetUse(GetPredKey(PredIndex));
} }
void FindRegUses(List<(int, UseSite)> UseList, object Parent, ShaderIrNode Node, int OperIndex = 0) void RemoveUse(RegUse Use)
{ {
if (Node is ShaderIrAsg Asg) if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
{ {
FindRegUses(UseList, Asg, Asg.Src); throw new InvalidOperationException();
}
else if (Node is ShaderIrCond Cond)
{
FindRegUses(UseList, Cond, Cond.Pred, 0);
FindRegUses(UseList, Cond, Cond.Child, 1);
}
else if (Node is ShaderIrOp Op)
{
FindRegUses(UseList, Op, Op.OperandA, 0);
FindRegUses(UseList, Op, Op.OperandB, 1);
FindRegUses(UseList, Op, Op.OperandC, 2);
}
else if (Node is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex)
{
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex)));
}
else if (Node is ShaderIrOperPred Pred)
{
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, OperIndex)));
} }
} }
void TryAddRegUseSite(ShaderIrNode Node) void FindRegUses(
List<(int, UseSite)> UseList,
ShaderIrNode Parent,
ShaderIrNode Node,
ShaderIrCond CondNode,
int UseIndex,
int OperIndex = 0)
{
if (Node is ShaderIrAsg Asg)
{
FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
}
else if (Node is ShaderIrCond Cond)
{
FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0);
FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
}
else if (Node is ShaderIrOp Op)
{
FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
}
else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
{
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
}
else if (Node is ShaderIrOperPred Pred)
{
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
}
}
void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
{ {
List<(int, UseSite)> UseList = new List<(int, UseSite)>(); List<(int, UseSite)> UseList = new List<(int, UseSite)>();
FindRegUses(UseList, null, Node); FindRegUses(UseList, null, Node, CondNode, UseIndex);
foreach ((int Key, UseSite Site) in UseList) foreach ((int Key, UseSite Site) in UseList)
{ {
@ -190,10 +235,22 @@ namespace Ryujinx.Graphics.Gal.Shader
List<(int, UseSite)> UseList = new List<(int, UseSite)>(); List<(int, UseSite)> UseList = new List<(int, UseSite)>();
FindRegUses(UseList, Use.Asg, Use.Asg.Src); if (Use.Cond != null)
{
FindRegUses(UseList, null, Use.Cond, null, 0);
}
else
{
FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
}
foreach ((int Key, UseSite Site) in UseList) foreach ((int Key, UseSite Site) in UseList)
{ {
//TODO: Build an assignment list inside RegUse,
//and check if there is an assignment inside the
//range of Use.AsgIndex and Use.LastSiteIndex,
//and if that's the case, then we should return false.
//The current method is too conservative.
if (GetUse(Key).AsgIndex >= Use.AsgIndex) if (GetUse(Key).AsgIndex >= Use.AsgIndex)
{ {
return false; return false;
@ -203,13 +260,18 @@ namespace Ryujinx.Graphics.Gal.Shader
return Use.TryPropagate(); return Use.TryPropagate();
} }
for (int Index = 0, AsgIndex = 0; Index < Nodes.Count; Index++, AsgIndex++) for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
{ {
ShaderIrNode Node = Nodes[Index]; ShaderIrNode Node = Nodes[Index];
bool IsConditional = Node is ShaderIrCond; ShaderIrCond CondNode = null;
TryAddRegUseSite(Node); if (Node is ShaderIrCond)
{
CondNode = (ShaderIrCond)Node;
}
TryAddRegUseSite(Node, CondNode, IterCount);;
while (Node is ShaderIrCond Cond) while (Node is ShaderIrCond Cond)
{ {
@ -223,7 +285,7 @@ namespace Ryujinx.Graphics.Gal.Shader
RegUse Use = null; RegUse Use = null;
if (Asg.Dst is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
{ {
Use = GetGprUse(Gpr.Index); Use = GetGprUse(Gpr.Index);
} }
@ -232,16 +294,22 @@ namespace Ryujinx.Graphics.Gal.Shader
Use = GetPredUse(Pred.Index); Use = GetPredUse(Pred.Index);
} }
if (!IsConditional && TryPropagate(Use)) bool CanRemoveAsg = CondNode == null;
{
Nodes.Remove(Use.Asg);
CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
if (CanRemoveAsg && TryPropagate(Use))
{
RemoveUse(Use);
//Note: Only decrement if the removal was successful.
//RemoveUse throws when this is not the case so we should be good.
Index--; Index--;
} }
//All nodes inside conditional nodes can't be propagated, //All nodes inside conditional nodes can't be propagated,
//as we don't even know if they will be executed to begin with. //as we don't even know if they will be executed to begin with.
Use?.SetNewAsg(Asg, AsgIndex, !IsConditional); Use?.SetNewAsg(Asg, IterCount, CondNode);
} }
foreach (RegUse Use in Uses.Values) foreach (RegUse Use in Uses.Values)
@ -258,9 +326,41 @@ namespace Ryujinx.Graphics.Gal.Shader
if (TryPropagate(Use)) if (TryPropagate(Use))
{ {
Nodes.Remove(Use.Asg); RemoveUse(Use);
} }
} }
} }
private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
{
if (CondA == null || CondB == null)
{
return CondA == CondB;
}
if (CondA.Not != CondB.Not)
{
return false;
}
if (CondA.Pred is ShaderIrOperPred PredA)
{
if (!(CondB.Pred is ShaderIrOperPred PredB))
{
return false;
}
if (PredA.Index != PredB.Index)
{
return false;
}
}
else if (CondA.Pred != CondB.Pred)
{
return false;
}
return true;
}
} }
} }

View file

@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderTexqInfo
{
Dimension = 1,
TextureType = 2,
SamplePos = 5,
Filter = 16,
Lod = 18,
Wrap = 20,
BorderColor = 22
}
}

View file

@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture);
case GalTextureFormat.A1B5G5R5: return Read2Bpp (Memory, Texture); case GalTextureFormat.A1B5G5R5: return Read2Bpp (Memory, Texture);
case GalTextureFormat.B5G6R5: return Read2Bpp (Memory, Texture);
case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture);
case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture);
case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture);
@ -20,35 +21,6 @@ namespace Ryujinx.Graphics.Gpu
throw new NotImplementedException(Texture.Format.ToString()); throw new NotImplementedException(Texture.Format.ToString());
} }
private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 4];
ISwizzle Swizzle = GetSwizzle(Texture, 4);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 4;
}
}
return Output;
}
private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture) private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
@ -56,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 2]; byte[] Output = new byte[Width * Height * 2];
ISwizzle Swizzle = GetSwizzle(Texture, 2); ISwizzle Swizzle = GetSwizzle(Texture, Width, 2);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
@ -78,6 +50,35 @@ namespace Ryujinx.Graphics.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture)
{
int Width = Texture.Width;
int Height = Texture.Height;
byte[] Output = new byte[Width * Height * 4];
ISwizzle Swizzle = GetSwizzle(Texture, Width, 4);
fixed (byte* BuffPtr = Output)
{
long OutOffs = 0;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel;
OutOffs += 4;
}
}
return Output;
}
private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture) private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture)
{ {
int Width = (Texture.Width + 3) / 4; int Width = (Texture.Width + 3) / 4;
@ -85,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 8]; byte[] Output = new byte[Width * Height * 8];
ISwizzle Swizzle = GetSwizzle(Texture, 8); ISwizzle Swizzle = GetSwizzle(Texture, Width, 8);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 16]; byte[] Output = new byte[Width * Height * 16];
ISwizzle Swizzle = GetSwizzle(Texture, 16); ISwizzle Swizzle = GetSwizzle(Texture, Width, 16);
fixed (byte* BuffPtr = Output) fixed (byte* BuffPtr = Output)
{ {
@ -138,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu
return Output; return Output;
} }
private static ISwizzle GetSwizzle(Texture Texture, int Bpp) private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
{ {
switch (Texture.Swizzle) switch (Texture.Swizzle)
{ {
@ -148,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu
case TextureSwizzle.BlockLinear: case TextureSwizzle.BlockLinear:
case TextureSwizzle.BlockLinearColorKey: case TextureSwizzle.BlockLinearColorKey:
return new BlockLinearSwizzle(Texture.Width, Bpp, Texture.BlockHeight); return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
} }
throw new NotImplementedException(Texture.Swizzle.ToString()); throw new NotImplementedException(Texture.Swizzle.ToString());