[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,
A1B5G5R5 = 0x14,
B5G6R5 = 0x15,
BC1 = 0x24,
BC2 = 0x25,
BC3 = 0x26

View file

@ -55,6 +55,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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)
{
switch (Format)

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Textures = new int[80];
}
public void Set(int Index, GalTexture Tex)
public void Set(int Index, GalTexture Texture)
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
@ -19,29 +19,38 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindTexture(TextureTarget.Texture2D, Handle);
int W = Tex.Width;
int H = Tex.Height;
const int Border = 0;
byte[] Data = Tex.Data;
int Length = Data.Length;
if (IsCompressedTextureFormat(Tex.Format))
if (IsCompressedTextureFormat(Texture.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
{
//TODO: Get those from Texture format.
const PixelInternalFormat Pif = PixelInternalFormat.Rgba;
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
const PixelFormat Pf = PixelFormat.Rgba;
(PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format);
const PixelType Pt = PixelType.UnsignedByte;
GL.TexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Pf, Pt, Data);
GL.TexImage2D(
TextureTarget.Texture2D,
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()
{
FbRenderer.Render();
//FbRenderer.Render();
}
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 TexStartIndex = 8;
public const string PositionOutAttrName = "position";
private const string InAttrName = "in_attr";
private const string OutAttrName = "out_attr";
private const string UniformName = "c";
@ -62,10 +64,11 @@ namespace Ryujinx.Graphics.Gal.Shader
m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>();
//FIXME: Only valid for vertex shaders.
if (ShaderType == GalShaderType.Fragment)
{
m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4));
m_InAttributes.Add(7, new ShaderDeclInfo(PositionOutAttrName, -1, 0, 4));
}
else
{
@ -104,10 +107,9 @@ namespace Ryujinx.Graphics.Gal.Shader
Traverse(Op, Op.OperandB);
Traverse(Op, Op.OperandC);
if (Op.Inst == ShaderIrInst.Texr ||
Op.Inst == ShaderIrInst.Texg ||
Op.Inst == ShaderIrInst.Texb ||
Op.Inst == ShaderIrInst.Texa)
if (Op.Inst == ShaderIrInst.Texq ||
Op.Inst == ShaderIrInst.Texs ||
Op.Inst == ShaderIrInst.Txlf)
{
int Handle = ((ShaderIrOperImm)Op.OperandC).Value;

View file

@ -31,40 +31,51 @@ namespace Ryujinx.Graphics.Gal.Shader
{
InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
{
{ ShaderIrInst.And, GetAndExpr },
{ ShaderIrInst.Asr, GetAsrExpr },
{ ShaderIrInst.Band, GetBandExpr },
{ ShaderIrInst.Bnot, GetBnotExpr },
{ ShaderIrInst.Clt, GetCltExpr },
{ ShaderIrInst.Ceq, GetCeqExpr },
{ ShaderIrInst.Cle, GetCleExpr },
{ ShaderIrInst.Cgt, GetCgtExpr },
{ ShaderIrInst.Cne, GetCneExpr },
{ ShaderIrInst.Cge, GetCgeExpr },
{ ShaderIrInst.Exit, GetExitExpr },
{ ShaderIrInst.Fabs, GetFabsExpr },
{ ShaderIrInst.Fadd, GetFaddExpr },
{ ShaderIrInst.Fcos, GetFcosExpr },
{ ShaderIrInst.Fex2, GetFex2Expr },
{ ShaderIrInst.Ffma, GetFfmaExpr },
{ ShaderIrInst.Flg2, GetFlg2Expr },
{ ShaderIrInst.Fmul, GetFmulExpr },
{ ShaderIrInst.Fneg, GetFnegExpr },
{ ShaderIrInst.Frcp, GetFrcpExpr },
{ ShaderIrInst.Frsq, GetFrsqExpr },
{ ShaderIrInst.Fsin, GetFsinExpr },
{ ShaderIrInst.Ipa, GetIpaExpr },
{ ShaderIrInst.Kil, GetKilExpr },
{ ShaderIrInst.Lsr, GetLsrExpr },
{ ShaderIrInst.Not, GetNotExpr },
{ ShaderIrInst.Or, GetOrExpr },
{ ShaderIrInst.Stof, GetStofExpr },
{ ShaderIrInst.Utof, GetUtofExpr },
{ ShaderIrInst.Texr, GetTexrExpr },
{ ShaderIrInst.Texg, GetTexgExpr },
{ ShaderIrInst.Texb, GetTexbExpr },
{ ShaderIrInst.Texa, GetTexaExpr },
{ ShaderIrInst.Xor, GetXorExpr },
{ ShaderIrInst.And, GetAndExpr },
{ ShaderIrInst.Asr, GetAsrExpr },
{ ShaderIrInst.Band, GetBandExpr },
{ ShaderIrInst.Bnot, GetBnotExpr },
{ ShaderIrInst.Ceil, GetCeilExpr },
{ ShaderIrInst.Ceq, GetCeqExpr },
{ ShaderIrInst.Cge, GetCgeExpr },
{ ShaderIrInst.Cgt, GetCgtExpr },
{ ShaderIrInst.Clamp, GetClampExpr },
{ ShaderIrInst.Cle, GetCleExpr },
{ ShaderIrInst.Clt, GetCltExpr },
{ ShaderIrInst.Cne, GetCneExpr },
{ ShaderIrInst.Exit, GetExitExpr },
{ ShaderIrInst.Fabs, GetFabsExpr },
{ ShaderIrInst.Fadd, GetFaddExpr },
{ ShaderIrInst.Fceq, GetCeqExpr },
{ ShaderIrInst.Fcge, GetCgeExpr },
{ ShaderIrInst.Fcgt, GetCgtExpr },
{ ShaderIrInst.Fcle, GetCleExpr },
{ ShaderIrInst.Fclt, GetCltExpr },
{ ShaderIrInst.Fcne, GetCneExpr },
{ ShaderIrInst.Fcos, GetFcosExpr },
{ ShaderIrInst.Fex2, GetFex2Expr },
{ ShaderIrInst.Ffma, GetFfmaExpr },
{ ShaderIrInst.Flg2, GetFlg2Expr },
{ ShaderIrInst.Floor, GetFloorExpr },
{ ShaderIrInst.Fmul, GetFmulExpr },
{ ShaderIrInst.Fneg, GetFnegExpr },
{ ShaderIrInst.Frcp, GetFrcpExpr },
{ ShaderIrInst.Frsq, GetFrsqExpr },
{ ShaderIrInst.Fsin, GetFsinExpr },
{ ShaderIrInst.Ftos, GetFtosExpr },
{ ShaderIrInst.Ftou, GetFtouExpr },
{ 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()
{
if (Decl.ShaderType == GalShaderType.Fragment)
{
SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";");
}
PrintDeclAttributes(Decl.InAttributes.Values, "in");
}
private void PrintDeclOutAttributes()
{
if (Decl.ShaderType == GalShaderType.Vertex)
{
SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";");
}
PrintDeclAttributes(Decl.OutAttributes.Values, "out");
}
@ -133,7 +154,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
if (DeclInfo.Index >= 0)
{
SB.AppendLine($"layout (location = {DeclInfo.Index}) {InOut} {GetDecl(DeclInfo)};");
SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";");
Count++;
}
@ -222,7 +243,14 @@ namespace Ryujinx.Graphics.Gal.Shader
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);
}
@ -236,6 +264,16 @@ namespace Ryujinx.Graphics.Gal.Shader
}
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) + ";");
}
else
@ -321,10 +359,9 @@ namespace Ryujinx.Graphics.Gal.Shader
return true;
case ShaderIrInst.Ipa:
case ShaderIrInst.Texr:
case ShaderIrInst.Texg:
case ShaderIrInst.Texb:
case ShaderIrInst.Texa:
case ShaderIrInst.Texq:
case ShaderIrInst.Texs:
case ShaderIrInst.Txlf:
return false;
}
@ -349,11 +386,6 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperAbuf Abuf)
{
if (Abuf.Offs == GlslDecl.GlPositionWAttr && Decl.ShaderType == GalShaderType.Fragment)
{
return "(1f / gl_FragCoord.w)";
}
if (Abuf.Offs == GlslDecl.VertexIdAttr)
{
return "gl_VertexID";
@ -437,6 +469,10 @@ namespace Ryujinx.Graphics.Gal.Shader
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 GetCeqExpr(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 GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(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 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 GetKilExpr(ShaderIrOp Op) => "discard";
@ -487,6 +535,54 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
@ -499,6 +595,13 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
return Opr + GetOperExpr(Op, Op.OperandA);
@ -517,16 +620,6 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC;
@ -547,6 +640,12 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
return GetExprWithCast(Op, Oper, GetSrcExpr(Oper));
@ -571,13 +670,31 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new InvalidOperationException();
}
//For integer immediates being used as float,
//it's better (for readability) to just return the float value.
if (Src is ShaderIrOperImm Imm && DstType == OperType.F32)
switch (Src)
{
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)
@ -592,12 +709,20 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
switch (Op.Inst)
{
case ShaderIrInst.Stof: return OperType.F32;
case ShaderIrInst.Utof: return OperType.F32;
case ShaderIrInst.Stof:
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));
}
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)
{
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)
{
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 Np = ((OpCode >> 42) & 1) != 0;
@ -269,17 +294,28 @@ namespace Ryujinx.Graphics.Gal.Shader
switch (Oper)
{
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.RR: OperB = GetOperGpr20 (OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
ShaderIrInst CmpInst = GetCmp(OpCode);
ShaderIrInst CmpInst;
ShaderIrOp Op = new ShaderIrOp(CmpInst,
GetAluAbsNeg(OperA, Aa, Na),
GetAluAbs (OperB, Ab));
if (IsFloat)
{
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 P1Node = GetOperPred0 (OpCode);

View file

@ -60,7 +60,17 @@ namespace Ryujinx.Graphics.Gal.Shader
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;
@ -74,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.Shader
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;
@ -92,16 +102,6 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
@ -130,23 +130,38 @@ namespace Ryujinx.Graphics.Gal.Shader
}
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)
{
case 0x1: return ShaderIrInst.Clt;
case 0x2: return ShaderIrInst.Ceq;
case 0x3: return ShaderIrInst.Cle;
case 0x4: return ShaderIrInst.Cgt;
case 0x5: return ShaderIrInst.Cne;
case 0x6: return ShaderIrInst.Cge;
case 0x7: return ShaderIrInst.Cnum;
case 0x8: return ShaderIrInst.Cnan;
case 0x9: return ShaderIrInst.Cltu;
case 0xa: return ShaderIrInst.Cequ;
case 0xb: return ShaderIrInst.Cleu;
case 0xc: return ShaderIrInst.Cgtu;
case 0xd: return ShaderIrInst.Cneu;
case 0xe: return ShaderIrInst.Cgeu;
case 0x1: return ShaderIrInst.Fclt;
case 0x2: return ShaderIrInst.Fceq;
case 0x3: return ShaderIrInst.Fcle;
case 0x4: return ShaderIrInst.Fcgt;
case 0x5: return ShaderIrInst.Fcne;
case 0x6: return ShaderIrInst.Fcge;
case 0x7: return ShaderIrInst.Fcnum;
case 0x8: return ShaderIrInst.Fcnan;
case 0x9: return ShaderIrInst.Fcltu;
case 0xa: return ShaderIrInst.Fcequ;
case 0xb: return ShaderIrInst.Fcleu;
case 0xc: return ShaderIrInst.Fcgtu;
case 0xd: return ShaderIrInst.Fcneu;
case 0xe: return ShaderIrInst.Fcgeu;
}
throw new ArgumentException(nameof(OpCode));
@ -170,7 +185,9 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Pred.Index != ShaderIrOperPred.UnusedIndex)
{
Node = new ShaderIrCond(Pred, Node);
bool Inv = ((OpCode >> 19) & 1) != 0;
Node = new ShaderIrCond(Pred, Node, Inv);
}
return Node;

View file

@ -36,24 +36,56 @@ 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)
{
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.
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperGpr20 (OpCode);
ShaderIrNode OperC = GetOperGpr28 (OpCode);
ShaderIrNode OperD = GetOperImm13_36(OpCode);
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperGpr20 (OpCode);
ShaderIrNode OperC = GetOperImm13_36(OpCode);
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
}
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)
{
EmitI2f(Block, OpCode, ShaderOper.CR);
@ -40,6 +70,131 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
IntType Type = GetIntType(OpCode);
@ -76,18 +231,16 @@ namespace Ryujinx.Graphics.Gal.Shader
int Size = 8 << ((int)Type & 3);
ulong Mask = ulong.MaxValue >> (64 - Size);
int Mask32 = (int)Mask;
if (Shift != 0)
{
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
@ -99,13 +252,6 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
bool Signed = ((OpCode >> 13) & 1) != 0;
@ -124,5 +270,17 @@ namespace Ryujinx.Graphics.Gal.Shader
{
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)
{
//Ignore scheduling instructions, which are
//written every 32 bytes.
if ((Offset & 7) == 0)
{
Offset += 2;
continue;
}
uint Word0 = (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 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.Child = Child;
this.Not = Not;
}
}
}

View file

@ -2,53 +2,66 @@ namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderIrInst
{
Invalid,
B_Start,
Band,
Bnot,
Bor,
Bxor,
Clt,
Ceq,
Cle,
Cgt,
Cne,
Cge,
Cnum,
Cnan,
Cltu,
Cequ,
Cleu,
Cgtu,
Cneu,
Cgeu,
B_End,
F_Start,
Ceil,
Clamp,
Fabs,
Fadd,
Fceq,
Fcequ,
Fcge,
Fcgeu,
Fcgt,
Fcgtu,
Fcle,
Fcleu,
Fclt,
Fcltu,
Fcnan,
Fcne,
Fcneu,
Fcnum,
Fcos,
Fex2,
Ffma,
Flg2,
Floor,
Fmul,
Fneg,
Frcp,
Frsq,
Fsin,
Ftos,
Ftou,
Ipa,
Texr,
Texg,
Texb,
Texa,
Texs,
Trunc,
F_End,
I_Start,
And,
Asr,
Ceq,
Cge,
Cgt,
Cle,
Clt,
Cne,
Lsr,
Not,
Or,
Stof,
Texq,
Txlf,
Utof,
Xor,
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 OperandB { get; set; }
public ShaderIrNode OperandC { get; set; }
public ShaderIrMeta MetaData { get; set; }
public ShaderIrOp(
ShaderIrInst Inst,
ShaderIrNode OperandA = null,
ShaderIrNode OperandB = null,
ShaderIrNode OperandC = null)
ShaderIrNode OperandC = null,
ShaderIrMeta MetaData = null)
{
this.Inst = Inst;
this.OperandA = OperandA;
this.OperandB = OperandB;
this.OperandC = OperandC;
this.MetaData = MetaData;
}
}
}

View file

@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Gal.Shader
#region Instructions
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("0011100x01011x", ShaderDecode.Fadd_I);
Set("0101110001011x", ShaderDecode.Fadd_R);
@ -31,16 +37,24 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0011100x10111x", ShaderDecode.I2f_I);
Set("0101110010111x", ShaderDecode.I2f_R);
Set("11100000xxxxxx", ShaderDecode.Ipa);
Set("010010110110xx", ShaderDecode.Isetp_C);
Set("0011011x0110xx", ShaderDecode.Isetp_I);
Set("010110110110xx", ShaderDecode.Isetp_R);
Set("111000110011xx", ShaderDecode.Kil);
Set("1110111111011x", ShaderDecode.Ld_A);
Set("000001xxxxxxxx", ShaderDecode.Lop32i);
Set("0100110010011x", ShaderDecode.Mov_C);
Set("0011100x10011x", ShaderDecode.Mov_I);
Set("0101110010011x", ShaderDecode.Mov_R);
Set("000000010000xx", ShaderDecode.Mov32i);
Set("0101000010000x", ShaderDecode.Mufu);
Set("0100110000101x", ShaderDecode.Shr_C);
Set("0011100x00101x", ShaderDecode.Shr_I);
Set("0101110000101x", ShaderDecode.Shr_R);
Set("1110111111110x", ShaderDecode.St_A);
Set("1101111101001x", ShaderDecode.Texq);
Set("1101100xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds);
#endregion
}

View file

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

View file

@ -7,13 +7,22 @@ namespace Ryujinx.Graphics.Gal.Shader
{
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.Cond = Cond;
this.UseIndex = UseIndex;
this.OperIndex = OperIndex;
}
}
@ -24,7 +33,9 @@ namespace Ryujinx.Graphics.Gal.Shader
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;
@ -35,6 +46,11 @@ namespace Ryujinx.Graphics.Gal.Shader
public void AddUseSite(UseSite Site)
{
if (LastSiteIndex < Site.UseIndex)
{
LastSiteIndex = Site.UseIndex;
}
Sites.Add(Site);
}
@ -42,14 +58,27 @@ namespace Ryujinx.Graphics.Gal.Shader
{
//This happens when a untiliazied register is used,
//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
//the expression (since we can't propagate what doesn't yet exist).
if (Asg == null || !Propagate)
if (Asg == null)
{
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)
{
foreach (UseSite Site in Sites)
@ -89,11 +118,13 @@ namespace Ryujinx.Graphics.Gal.Shader
return true;
}
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate)
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
{
this.Asg = Asg;
this.AsgIndex = AsgIndex;
this.Propagate = Propagate;
this.Asg = Asg;
this.AsgIndex = AsgIndex;
this.Cond = Cond;
LastSiteIndex = 0;
Sites.Clear();
}
@ -137,38 +168,52 @@ namespace Ryujinx.Graphics.Gal.Shader
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);
}
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)));
throw new InvalidOperationException();
}
}
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)>();
FindRegUses(UseList, null, Node);
FindRegUses(UseList, null, Node, CondNode, UseIndex);
foreach ((int Key, UseSite Site) in UseList)
{
@ -190,10 +235,22 @@ namespace Ryujinx.Graphics.Gal.Shader
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)
{
//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)
{
return false;
@ -203,13 +260,18 @@ namespace Ryujinx.Graphics.Gal.Shader
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];
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)
{
@ -223,7 +285,7 @@ namespace Ryujinx.Graphics.Gal.Shader
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);
}
@ -232,16 +294,22 @@ namespace Ryujinx.Graphics.Gal.Shader
Use = GetPredUse(Pred.Index);
}
if (!IsConditional && TryPropagate(Use))
{
Nodes.Remove(Use.Asg);
bool CanRemoveAsg = CondNode == null;
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--;
}
//All nodes inside conditional nodes can't be propagated,
//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)
@ -258,9 +326,41 @@ namespace Ryujinx.Graphics.Gal.Shader
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.A1B5G5R5: return Read2Bpp (Memory, Texture);
case GalTextureFormat.B5G6R5: return Read2Bpp (Memory, Texture);
case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture);
case GalTextureFormat.BC2: 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());
}
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)
{
int Width = Texture.Width;
@ -56,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 2];
ISwizzle Swizzle = GetSwizzle(Texture, 2);
ISwizzle Swizzle = GetSwizzle(Texture, Width, 2);
fixed (byte* BuffPtr = Output)
{
@ -78,6 +50,35 @@ namespace Ryujinx.Graphics.Gpu
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)
{
int Width = (Texture.Width + 3) / 4;
@ -85,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 8];
ISwizzle Swizzle = GetSwizzle(Texture, 8);
ISwizzle Swizzle = GetSwizzle(Texture, Width, 8);
fixed (byte* BuffPtr = Output)
{
@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Output = new byte[Width * Height * 16];
ISwizzle Swizzle = GetSwizzle(Texture, 16);
ISwizzle Swizzle = GetSwizzle(Texture, Width, 16);
fixed (byte* BuffPtr = Output)
{
@ -138,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu
return Output;
}
private static ISwizzle GetSwizzle(Texture Texture, int Bpp)
private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
{
switch (Texture.Swizzle)
{
@ -148,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu
case TextureSwizzle.BlockLinear:
case TextureSwizzle.BlockLinearColorKey:
return new BlockLinearSwizzle(Texture.Width, Bpp, Texture.BlockHeight);
return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
}
throw new NotImplementedException(Texture.Swizzle.ToString());