[GPU] Add more shader instructions, add support for rgb565 textures
This commit is contained in:
parent
e9cfdef098
commit
feb2680a6c
22 changed files with 817 additions and 238 deletions
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
A8B8G8R8 = 0x8,
|
||||
A1B5G5R5 = 0x14,
|
||||
B5G6R5 = 0x15,
|
||||
BC1 = 0x24,
|
||||
BC2 = 0x25,
|
||||
BC3 = 0x26
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public void Render()
|
||||
{
|
||||
FbRenderer.Render();
|
||||
//FbRenderer.Render();
|
||||
}
|
||||
|
||||
public void SetWindowSize(int Width, int Height)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -35,36 +35,47 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{ ShaderIrInst.Asr, GetAsrExpr },
|
||||
{ ShaderIrInst.Band, GetBandExpr },
|
||||
{ ShaderIrInst.Bnot, GetBnotExpr },
|
||||
{ ShaderIrInst.Clt, GetCltExpr },
|
||||
{ ShaderIrInst.Ceil, GetCeilExpr },
|
||||
{ ShaderIrInst.Ceq, GetCeqExpr },
|
||||
{ ShaderIrInst.Cle, GetCleExpr },
|
||||
{ ShaderIrInst.Cgt, GetCgtExpr },
|
||||
{ ShaderIrInst.Cne, GetCneExpr },
|
||||
{ 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.Texr, GetTexrExpr },
|
||||
{ ShaderIrInst.Texg, GetTexgExpr },
|
||||
{ ShaderIrInst.Texb, GetTexbExpr },
|
||||
{ ShaderIrInst.Texa, GetTexaExpr },
|
||||
{ ShaderIrInst.Xor, GetXorExpr },
|
||||
{ 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,14 +670,32 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
switch (Src)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
case ShaderIrOperImm Imm:
|
||||
{
|
||||
//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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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++];
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
4
Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs
Normal file
4
Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class ShaderIrMeta { }
|
||||
}
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs
Normal file
15
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
enum ShaderOper
|
||||
{
|
||||
CR,
|
||||
RC,
|
||||
RR,
|
||||
Imm,
|
||||
Immf
|
||||
Immf,
|
||||
RC,
|
||||
RR
|
||||
}
|
||||
}
|
|
@ -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.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 (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
|
||||
}
|
||||
else if (Node is ShaderIrCond Cond)
|
||||
{
|
||||
FindRegUses(UseList, Cond, Cond.Pred, 0);
|
||||
FindRegUses(UseList, Cond, Cond.Child, 1);
|
||||
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, 0);
|
||||
FindRegUses(UseList, Op, Op.OperandB, 1);
|
||||
FindRegUses(UseList, Op, Op.OperandC, 2);
|
||||
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.Index != ShaderIrOperGpr.ZRIndex)
|
||||
else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
|
||||
{
|
||||
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex)));
|
||||
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, OperIndex)));
|
||||
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
void TryAddRegUseSite(ShaderIrNode Node)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs
Normal file
13
Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
Reference in a new issue