aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChocolArm64/Decoder/ABlock.cs2
-rw-r--r--ChocolArm64/Decoder/ADecoder.cs2
-rw-r--r--Ryujinx.Core/Gpu/TextureReader.cs197
-rw-r--r--Ryujinx.Graphics/Gal/GalTextureFormat.cs21
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs20
-rw-r--r--Ryujinx.Graphics/Gal/Shader/GlslDecl.cs13
-rw-r--r--Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs245
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs164
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs6
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs170
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs40
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs2
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs4
-rw-r--r--Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs7
14 files changed, 744 insertions, 149 deletions
diff --git a/ChocolArm64/Decoder/ABlock.cs b/ChocolArm64/Decoder/ABlock.cs
index 32974c1a..7a0fc607 100644
--- a/ChocolArm64/Decoder/ABlock.cs
+++ b/ChocolArm64/Decoder/ABlock.cs
@@ -5,7 +5,7 @@ namespace ChocolArm64.Decoder
class ABlock
{
public long Position { get; set; }
- public long EndPosition { get; set; }
+ public long EndPosition { get; set; }
public ABlock Next { get; set; }
public ABlock Branch { get; set; }
diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs
index 32a68ad3..b154a54c 100644
--- a/ChocolArm64/Decoder/ADecoder.cs
+++ b/ChocolArm64/Decoder/ADecoder.cs
@@ -94,7 +94,7 @@ namespace ChocolArm64.Decoder
}
}
- //If we have on the tree two blocks with the same end position,
+ //If we have on the graph two blocks with the same end position,
//then we need to split the bigger block and have two small blocks,
//the end position of the bigger "Current" block should then be == to
//the position of the "Smaller" block.
diff --git a/Ryujinx.Core/Gpu/TextureReader.cs b/Ryujinx.Core/Gpu/TextureReader.cs
index f3e41046..0c1c83d5 100644
--- a/Ryujinx.Core/Gpu/TextureReader.cs
+++ b/Ryujinx.Core/Gpu/TextureReader.cs
@@ -10,19 +10,132 @@ namespace Ryujinx.Core.Gpu
{
switch (Texture.Format)
{
- 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);
- case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture);
- case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture);
+ case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture);
+ case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture);
+ case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture);
+ case GalTextureFormat.R32: return Read4Bpp (Memory, Texture);
+ case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture);
+ case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture);
+ case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture);
+ case GalTextureFormat.R8: return Read1Bpp (Memory, Texture);
+ case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture);
+ case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture);
+ case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture);
+ case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture);
+ case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture);
}
throw new NotImplementedException(Texture.Format.ToString());
}
+ private unsafe static byte[] Read1Bpp(IAMemory Memory, Texture Texture)
+ {
+ int Width = Texture.Width;
+ int Height = Texture.Height;
+
+ byte[] Output = new byte[Width * Height];
+
+ ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 1);
+
+ (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
+ Memory,
+ Texture.Position);
+
+ 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);
+
+ byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset);
+
+ *(BuffPtr + OutOffs) = Pixel;
+
+ OutOffs++;
+ }
+ }
+
+ return Output;
+ }
+
+ private unsafe static byte[] Read5551(IAMemory Memory, Texture Texture)
+ {
+ int Width = Texture.Width;
+ int Height = Texture.Height;
+
+ byte[] Output = new byte[Width * Height * 2];
+
+ ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2);
+
+ (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
+ Memory,
+ Texture.Position);
+
+ 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);
+
+ uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
+
+ Pixel = (Pixel & 0x001f) << 11 |
+ (Pixel & 0x03e0) << 1 |
+ (Pixel & 0x7c00) >> 9 |
+ (Pixel & 0x8000) >> 15;
+
+ *(short*)(BuffPtr + OutOffs) = (short)Pixel;
+
+ OutOffs += 2;
+ }
+ }
+
+ return Output;
+ }
+
+ private unsafe static byte[] Read565(IAMemory Memory, Texture Texture)
+ {
+ int Width = Texture.Width;
+ int Height = Texture.Height;
+
+ byte[] Output = new byte[Width * Height * 2];
+
+ ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2);
+
+ (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
+ Memory,
+ Texture.Position);
+
+ 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);
+
+ uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
+
+ Pixel = (Pixel & 0x001f) << 11 |
+ (Pixel & 0x07e0) |
+ (Pixel & 0xf800) >> 11;
+
+ *(short*)(BuffPtr + OutOffs) = (short)Pixel;
+
+ OutOffs += 2;
+ }
+ }
+
+ return Output;
+ }
+
private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture)
{
int Width = Texture.Width;
@@ -89,6 +202,74 @@ namespace Ryujinx.Core.Gpu
return Output;
}
+ private unsafe static byte[] Read8Bpp(IAMemory Memory, Texture Texture)
+ {
+ int Width = Texture.Width;
+ int Height = Texture.Height;
+
+ byte[] Output = new byte[Width * Height * 8];
+
+ ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8);
+
+ (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
+ Memory,
+ Texture.Position);
+
+ 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);
+
+ long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset);
+
+ *(long*)(BuffPtr + OutOffs) = Pixel;
+
+ OutOffs += 8;
+ }
+ }
+
+ return Output;
+ }
+
+ private unsafe static byte[] Read16Bpp(IAMemory Memory, Texture Texture)
+ {
+ int Width = Texture.Width;
+ int Height = Texture.Height;
+
+ byte[] Output = new byte[Width * Height * 16];
+
+ ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16);
+
+ (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition(
+ Memory,
+ Texture.Position);
+
+ 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);
+
+ long PxLow = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
+ long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
+
+ *(long*)(BuffPtr + OutOffs + 0) = PxLow;
+ *(long*)(BuffPtr + OutOffs + 8) = PxHigh;
+
+ OutOffs += 16;
+ }
+ }
+
+ return Output;
+ }
+
private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture)
{
int Width = (Texture.Width + 3) / 4;
diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
index 37291e18..b3d8b03d 100644
--- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs
+++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
@@ -2,13 +2,18 @@ namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFormat
{
- A8B8G8R8 = 0x8,
- A1B5G5R5 = 0x14,
- B5G6R5 = 0x15,
- BC1 = 0x24,
- BC2 = 0x25,
- BC3 = 0x26,
- BC4 = 0x27,
- BC5 = 0x28
+ R32G32B32A32 = 0x1,
+ R16G16B16A16 = 0x3,
+ A8B8G8R8 = 0x8,
+ R32 = 0xf,
+ A1B5G5R5 = 0x14,
+ B5G6R5 = 0x15,
+ G8R8 = 0x18,
+ R8 = 0x1d,
+ BC1 = 0x24,
+ BC2 = 0x25,
+ BC3 = 0x26,
+ BC4 = 0x27,
+ BC5 = 0x28
}
}
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index d266a87a..ee697097 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -59,9 +59,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
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);
+ case GalTextureFormat.R32G32B32A32: return (PixelFormat.Rgba, PixelType.Float);
+ case GalTextureFormat.R16G16B16A16: return (PixelFormat.Rgba, PixelType.HalfFloat);
+ case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
+ case GalTextureFormat.R32: return (PixelFormat.Red, PixelType.Float);
+ case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
+ case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565);
+ case GalTextureFormat.G8R8: return (PixelFormat.Rg, PixelType.UnsignedByte);
+ case GalTextureFormat.R8: return (PixelFormat.Red, PixelType.UnsignedByte);
}
throw new NotImplementedException(Format.ToString());
@@ -145,20 +150,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
switch (BlendEquation)
{
+ default:
case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd;
case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract;
case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
case GalBlendEquation.Min: return BlendEquationMode.Min;
case GalBlendEquation.Max: return BlendEquationMode.Max;
}
-
- throw new ArgumentException(nameof(BlendEquation));
}
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
{
switch (BlendFactor)
{
+ default:
case GalBlendFactor.Zero: return BlendingFactorSrc.Zero;
case GalBlendFactor.One: return BlendingFactorSrc.One;
case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor;
@@ -179,14 +184,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha;
}
-
- throw new ArgumentException(nameof(BlendFactor));
}
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
{
switch (BlendFactor)
{
+ default:
case GalBlendFactor.Zero: return BlendingFactorDest.Zero;
case GalBlendFactor.One: return BlendingFactorDest.One;
case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor;
@@ -207,8 +211,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha;
}
-
- throw new ArgumentException(nameof(BlendFactor));
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
index 2650569e..43a04813 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
@@ -4,6 +4,10 @@ namespace Ryujinx.Graphics.Gal.Shader
{
class GlslDecl
{
+ public const int TessCoordAttrX = 0x2f0;
+ public const int TessCoordAttrY = 0x2f4;
+ public const int TessCoordAttrZ = 0x2f8;
+ public const int InstanceIdAttr = 0x2f8;
public const int VertexIdAttr = 0x2fc;
public const int GlPositionWAttr = 0x7c;
@@ -48,7 +52,7 @@ namespace Ryujinx.Graphics.Gal.Shader
public GalShaderType ShaderType { get; private set; }
- public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType)
+ public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType)
{
this.ShaderType = ShaderType;
@@ -75,9 +79,12 @@ namespace Ryujinx.Graphics.Gal.Shader
m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4));
}
- foreach (ShaderIrNode Node in Nodes)
+ foreach (ShaderIrBlock Block in Blocks)
{
- Traverse(null, Node);
+ foreach (ShaderIrNode Node in Block.GetNodes())
+ {
+ Traverse(null, Node);
+ }
}
}
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 88e46243..5c4537fe 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Gal.Shader
private GlslDecl Decl;
+ private ShaderIrBlock[] Blocks;
+
private StringBuilder SB;
public GlslDecompiler()
@@ -37,6 +39,8 @@ namespace Ryujinx.Graphics.Gal.Shader
{ ShaderIrInst.Asr, GetAsrExpr },
{ ShaderIrInst.Band, GetBandExpr },
{ ShaderIrInst.Bnot, GetBnotExpr },
+ { ShaderIrInst.Bor, GetBorExpr },
+ { ShaderIrInst.Bxor, GetBxorExpr },
{ ShaderIrInst.Ceil, GetCeilExpr },
{ ShaderIrInst.Ceq, GetCeqExpr },
{ ShaderIrInst.Cge, GetCgeExpr },
@@ -50,19 +54,27 @@ namespace Ryujinx.Graphics.Gal.Shader
{ ShaderIrInst.Fabs, GetAbsExpr },
{ ShaderIrInst.Fadd, GetAddExpr },
{ ShaderIrInst.Fceq, GetCeqExpr },
+ { ShaderIrInst.Fcequ, GetCequExpr },
{ ShaderIrInst.Fcge, GetCgeExpr },
+ { ShaderIrInst.Fcgeu, GetCgeuExpr },
{ ShaderIrInst.Fcgt, GetCgtExpr },
+ { ShaderIrInst.Fcgtu, GetCgtuExpr },
{ ShaderIrInst.Fclamp, GetFclampExpr },
{ ShaderIrInst.Fcle, GetCleExpr },
+ { ShaderIrInst.Fcleu, GetCleuExpr },
{ ShaderIrInst.Fclt, GetCltExpr },
+ { ShaderIrInst.Fcltu, GetCltuExpr },
+ { ShaderIrInst.Fcnan, GetCnanExpr },
{ ShaderIrInst.Fcne, GetCneExpr },
+ { ShaderIrInst.Fcneu, GetCneuExpr },
+ { ShaderIrInst.Fcnum, GetCnumExpr },
{ ShaderIrInst.Fcos, GetFcosExpr },
{ ShaderIrInst.Fex2, GetFex2Expr },
{ ShaderIrInst.Ffma, GetFfmaExpr },
{ ShaderIrInst.Flg2, GetFlg2Expr },
{ ShaderIrInst.Floor, GetFloorExpr },
- { ShaderIrInst.Fmax, GetFmaxExpr },
- { ShaderIrInst.Fmin, GetFminExpr },
+ { ShaderIrInst.Fmax, GetMaxExpr },
+ { ShaderIrInst.Fmin, GetMinExpr },
{ ShaderIrInst.Fmul, GetMulExpr },
{ ShaderIrInst.Fneg, GetNegExpr },
{ ShaderIrInst.Frcp, GetFrcpExpr },
@@ -74,6 +86,8 @@ namespace Ryujinx.Graphics.Gal.Shader
{ ShaderIrInst.Kil, GetKilExpr },
{ ShaderIrInst.Lsl, GetLslExpr },
{ ShaderIrInst.Lsr, GetLsrExpr },
+ { ShaderIrInst.Max, GetMaxExpr },
+ { ShaderIrInst.Min, GetMinExpr },
{ ShaderIrInst.Mul, GetMulExpr },
{ ShaderIrInst.Neg, GetNegExpr },
{ ShaderIrInst.Not, GetNotExpr },
@@ -91,11 +105,9 @@ namespace Ryujinx.Graphics.Gal.Shader
public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType)
{
- ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Memory, Position);
-
- ShaderIrNode[] Nodes = Block.GetNodes();
+ Blocks = ShaderDecoder.Decode(Memory, Position);
- Decl = new GlslDecl(Nodes, ShaderType);
+ Decl = new GlslDecl(Blocks, ShaderType);
SB = new StringBuilder();
@@ -108,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintDeclGprs();
PrintDeclPreds();
- PrintBlockScope(Nodes, 0, Nodes.Length, "void main()", 1);
+ PrintBlockScope(Blocks[0], null, null, "void main()", IdentationStr);
string GlslCode = SB.ToString();
@@ -230,37 +242,75 @@ namespace Ryujinx.Graphics.Gal.Shader
}
private void PrintBlockScope(
- ShaderIrNode[] Nodes,
- int Start,
- int Count,
- string ScopeName,
- int IdentationLevel)
+ ShaderIrBlock Block,
+ ShaderIrBlock EndBlock,
+ ShaderIrBlock LoopBlock,
+ string ScopeName,
+ string Identation,
+ bool IsDoWhile = false)
{
- string Identation = string.Empty;
+ string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length);
- for (int Index = 0; Index < IdentationLevel - 1; Index++)
+ if (IsDoWhile)
{
- Identation += IdentationStr;
+ SB.AppendLine(UpIdent + "do {");
}
-
- if (ScopeName != string.Empty)
+ else
{
- ScopeName += " ";
+ SB.AppendLine(UpIdent + ScopeName + " {");
}
- SB.AppendLine(Identation + ScopeName + "{");
+ while (Block != null && Block != EndBlock)
+ {
+ ShaderIrNode[] Nodes = Block.GetNodes();
- string LastLine = Identation + "}";
+ Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes);
+ }
- if (IdentationLevel > 0)
+ if (IsDoWhile)
{
- Identation += IdentationStr;
+ SB.AppendLine(UpIdent + "} " + ScopeName + ";");
}
+ else
+ {
+ SB.AppendLine(UpIdent + "}");
+ }
+ }
- for (int Index = Start; Index < Start + Count; Index++)
+ private ShaderIrBlock PrintNodes(
+ ShaderIrBlock Block,
+ ShaderIrBlock EndBlock,
+ ShaderIrBlock LoopBlock,
+ string Identation,
+ params ShaderIrNode[] Nodes)
+ {
+ /*
+ * Notes about control flow and if-else/loop generation:
+ * The code assumes that the program has sane control flow,
+ * that is, there's no jumps to a location after another jump or
+ * jump target (except for the end of an if-else block), and backwards
+ * jumps to a location before the last loop dominator.
+ * Such cases needs to be transformed on a step before the GLSL code
+ * generation to ensure that we have sane graphs to work with.
+ * TODO: Such transformation is not yet implemented.
+ */
+ string NewIdent = Identation + IdentationStr;
+
+ ShaderIrBlock LoopTail = GetLoopTailBlock(Block);
+
+ if (LoopTail != null && LoopBlock != Block)
{
- ShaderIrNode Node = Nodes[Index];
+ //Shoock! kuma shock! We have a loop here!
+ //The entire sequence needs to be inside a do-while block.
+ ShaderIrBlock LoopEnd = GetDownBlock(LoopTail);
+ PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true);
+
+ return LoopEnd;
+ }
+
+ foreach (ShaderIrNode Node in Nodes)
+ {
if (Node is ShaderIrCond Cond)
{
string IfExpr = GetSrcExpr(Cond.Pred, true);
@@ -272,42 +322,41 @@ namespace Ryujinx.Graphics.Gal.Shader
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
{
- ShaderIrLabel Label = (ShaderIrLabel)Op.OperandA;
-
- int Target = FindLabel(Nodes, Label, Index + 1);
+ //Branch is a loop branch and would result in infinite recursion.
+ if (Block.Branch.Position <= Block.Position)
+ {
+ SB.AppendLine(Identation + "if (" + IfExpr + ") {");
- int IfCount = Target - Index - 1;
+ SB.AppendLine(Identation + IdentationStr + "continue;");
- string SubScopeName = "if (!" + IfExpr + ")";
+ SB.AppendLine(Identation + "}");
- if (Nodes[Index + IfCount] is ShaderIrOp LastOp && LastOp.Inst == ShaderIrInst.Bra)
- {
- Target = FindLabel(Nodes, (ShaderIrLabel)LastOp.OperandA, Index + 1);
+ continue;
+ }
- int ElseCount = Target - (Index + 1 + IfCount);
+ string SubScopeName = "if (!" + IfExpr + ")";
- PrintBlockScope(Nodes, Index + 1, IfCount - 1, SubScopeName, IdentationLevel + 1);
+ PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent);
- PrintBlockScope(Nodes, Index + 1 + IfCount, ElseCount, "else", IdentationLevel + 1);
+ ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch;
- Index += IfCount + ElseCount;
- }
- else
+ if (IfElseEnd?.Position > Block.Branch.Position)
{
- PrintBlockScope(Nodes, Index + 1, IfCount, SubScopeName, IdentationLevel + 1);
+ PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent);
- Index += IfCount;
+ return IfElseEnd;
}
+
+ return Block.Branch;
}
else
{
- string SubScopeName = "if (" + IfExpr + ")";
+ SB.AppendLine(Identation + "if (" + IfExpr + ") {");
- ShaderIrNode[] Child = new ShaderIrNode[] { Cond.Child };
+ PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child);
- PrintBlockScope(Child, 0, 1, SubScopeName, IdentationLevel + 1);
+ SB.AppendLine(Identation + "}");
}
-
}
else if (Node is ShaderIrAsg Asg)
{
@@ -322,7 +371,16 @@ namespace Ryujinx.Graphics.Gal.Shader
}
else if (Node is ShaderIrOp Op)
{
- if (Op.Inst == ShaderIrInst.Exit)
+ if (Op.Inst == ShaderIrInst.Bra)
+ {
+ if (Block.Branch.Position <= Block.Position)
+ {
+ SB.AppendLine(Identation + "continue;");
+ }
+
+ continue;
+ }
+ else if (Op.Inst == ShaderIrInst.Exit)
{
//Do everything that needs to be done before
//the shader ends here.
@@ -336,10 +394,6 @@ namespace Ryujinx.Graphics.Gal.Shader
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
}
- else if (Node is ShaderIrLabel Label)
- {
- //TODO: Add support for loops here.
- }
else if (Node is ShaderIrCmnt Cmnt)
{
SB.AppendLine(Identation + "// " + Cmnt.Comment);
@@ -350,22 +404,35 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
- SB.AppendLine(LastLine);
+ return Block.Next;
+ }
+
+ private ShaderIrBlock GetUpBlock(ShaderIrBlock Block)
+ {
+ return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position);
+ }
+
+ private ShaderIrBlock GetDownBlock(ShaderIrBlock Block)
+ {
+ return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition);
}
- private int FindLabel(ShaderIrNode[] Nodes, ShaderIrLabel Label, int Start)
+ private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead)
{
- int Target;
+ ShaderIrBlock Tail = null;
- for (Target = Start; Target < Nodes.Length; Target++)
+ foreach (ShaderIrBlock Block in LoopHead.Sources)
{
- if (Nodes[Target] == Label)
+ if (Block.Position >= LoopHead.Position)
{
- return Target;
+ if (Tail == null || Tail.Position < Block.Position)
+ {
+ Tail = Block;
+ }
}
}
- throw new InvalidOperationException();
+ return Tail;
}
private bool IsValidOutOper(ShaderIrNode Node)
@@ -480,9 +547,22 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperAbuf Abuf)
{
- if (Abuf.Offs == GlslDecl.VertexIdAttr)
+ if (Decl.ShaderType == GalShaderType.Vertex)
+ {
+ switch (Abuf.Offs)
+ {
+ case GlslDecl.VertexIdAttr: return "gl_VertexID";
+ case GlslDecl.InstanceIdAttr: return "gl_InstanceID";
+ }
+ }
+ else if (Decl.ShaderType == GalShaderType.TessEvaluation)
{
- return "gl_VertexID";
+ switch (Abuf.Offs)
+ {
+ case GlslDecl.TessCoordAttrX: return "gl_TessCoord.x";
+ case GlslDecl.TessCoordAttrY: return "gl_TessCoord.y";
+ case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z";
+ }
}
return GetName(Decl.InAttributes, Abuf);
@@ -567,6 +647,10 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!");
+ private string GetBorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "||");
+
+ private string GetBxorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^^");
+
private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil");
private string GetClampsExpr(ShaderIrOp Op)
@@ -583,12 +667,33 @@ namespace Ryujinx.Graphics.Gal.Shader
"uint(" + GetOperExpr(Op, Op.OperandC) + ")))";
}
- private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
- private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
+
+ private string GetCequExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "==");
+
+ private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">=");
+
+ private string GetCgeuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">=");
+
private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">");
+
+ private string GetCgtuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">");
+
+ private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
+
+ private string GetCleuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<=");
+
+ private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
+
+ private string GetCltuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<");
+
+ private string GetCnanExpr(ShaderIrOp Op) => GetUnaryCall(Op, "isnan");
+
private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!=");
- private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">=");
+
+ private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!=");
+
+ private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan");
private string GetExitExpr(ShaderIrOp Op) => "return";
@@ -604,9 +709,6 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
- private string GetFmaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max");
- private string GetFminExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min");
-
private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / ");
private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt");
@@ -634,6 +736,9 @@ namespace Ryujinx.Graphics.Gal.Shader
GetOperExpr(Op, Op.OperandB) + ")";
}
+ private string GetMaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max");
+ private string GetMinExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min");
+
private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
@@ -733,6 +838,18 @@ namespace Ryujinx.Graphics.Gal.Shader
GetOperExpr(Op, Op.OperandB);
}
+ private string GetBinaryExprWithNaN(ShaderIrOp Op, string Opr)
+ {
+ string A = GetOperExpr(Op, Op.OperandA);
+ string B = GetOperExpr(Op, Op.OperandB);
+
+ string NaNCheck =
+ " || isnan(" + A + ")" +
+ " || isnan(" + B + ")";
+
+ return A + " " + Opr + " " + B + NaNCheck;
+ }
+
private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2)
{
return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " +
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
index 1bb5f478..ddd3e3e8 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -126,6 +126,21 @@ namespace Ryujinx.Graphics.Gal.Shader
EmitFsetp(Block, OpCode, ShaderOper.RR);
}
+ public static void Imnmx_C(ShaderIrBlock Block, long OpCode)
+ {
+ EmitImnmx(Block, OpCode, ShaderOper.CR);
+ }
+
+ public static void Imnmx_I(ShaderIrBlock Block, long OpCode)
+ {
+ EmitImnmx(Block, OpCode, ShaderOper.Imm);
+ }
+
+ public static void Imnmx_R(ShaderIrBlock Block, long OpCode)
+ {
+ EmitImnmx(Block, OpCode, ShaderOper.RR);
+ }
+
public static void Ipa(ShaderIrBlock Block, long OpCode)
{
ShaderIrNode OperA = GetOperAbuf28(OpCode);
@@ -265,6 +280,26 @@ namespace Ryujinx.Graphics.Gal.Shader
return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
}
+ public static void Xmad_CR(ShaderIrBlock Block, long OpCode)
+ {
+ EmitXmad(Block, OpCode, ShaderOper.CR);
+ }
+
+ public static void Xmad_I(ShaderIrBlock Block, long OpCode)
+ {
+ EmitXmad(Block, OpCode, ShaderOper.Imm);
+ }
+
+ public static void Xmad_RC(ShaderIrBlock Block, long OpCode)
+ {
+ EmitXmad(Block, OpCode, ShaderOper.RC);
+ }
+
+ public static void Xmad_RR(ShaderIrBlock Block, long OpCode)
+ {
+ EmitXmad(Block, OpCode, ShaderOper.RR);
+ }
+
private static void EmitAluBinary(
ShaderIrBlock Block,
long OpCode,
@@ -412,6 +447,16 @@ namespace Ryujinx.Graphics.Gal.Shader
private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
+ EmitMnmx(Block, OpCode, true, Oper);
+ }
+
+ private static void EmitImnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+ {
+ EmitMnmx(Block, OpCode, false, Oper);
+ }
+
+ private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
+ {
bool NegB = ((OpCode >> 45) & 1) != 0;
bool AbsA = ((OpCode >> 46) & 1) != 0;
bool NegA = ((OpCode >> 48) & 1) != 0;
@@ -419,30 +464,48 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
- OperA = GetAluFabsFneg(OperA, AbsA, NegA);
+ if (IsFloat)
+ {
+ OperA = GetAluFabsFneg(OperA, AbsA, NegA);
+ }
+ else
+ {
+ OperA = GetAluIabsIneg(OperA, AbsA, NegA);
+ }
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));
}
- OperB = GetAluFabsFneg(OperB, AbsB, NegB);
+ if (IsFloat)
+ {
+ OperB = GetAluFabsFneg(OperB, AbsB, NegB);
+ }
+ else
+ {
+ OperB = GetAluIabsIneg(OperB, AbsB, NegB);
+ }
ShaderIrOperPred Pred = GetOperPred39(OpCode);
ShaderIrOp Op;
+ ShaderIrInst MaxInst = IsFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max;
+ ShaderIrInst MinInst = IsFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min;
+
if (Pred.IsConst)
{
bool IsMax = ((OpCode >> 42) & 1) != 0;
Op = new ShaderIrOp(IsMax
- ? ShaderIrInst.Fmax
- : ShaderIrInst.Fmin, OperA, OperB);
+ ? MaxInst
+ : MinInst, OperA, OperB);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
@@ -450,8 +513,8 @@ namespace Ryujinx.Graphics.Gal.Shader
{
ShaderIrNode PredN = GetOperPred39N(OpCode);
- ShaderIrOp OpMax = new ShaderIrOp(ShaderIrInst.Fmax, OperA, OperB);
- ShaderIrOp OpMin = new ShaderIrOp(ShaderIrInst.Fmin, OperA, OperB);
+ ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB);
+ ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB);
ShaderIrAsg AsgMax = new ShaderIrAsg(GetOperGpr0(OpCode), OpMax);
ShaderIrAsg AsgMin = new ShaderIrAsg(GetOperGpr0(OpCode), OpMin);
@@ -646,5 +709,94 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
}
+
+ private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+ {
+ //TODO: Confirm SignAB/C, it is just a guess.
+ //TODO: Implement Mode 3 (CSFU), what it does?
+ bool SignAB = ((OpCode >> 48) & 1) != 0;
+ bool SignC = ((OpCode >> 49) & 1) != 0;
+ bool HighB = ((OpCode >> 52) & 1) != 0;
+ bool HighA = ((OpCode >> 53) & 1) != 0;
+
+ int Mode = (int)(OpCode >> 50) & 7;
+
+ ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
+
+ ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);
+ ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff);
+
+ ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
+ ShaderIrInst ShiftC = SignC ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
+
+ if (HighA)
+ {
+ OperA = new ShaderIrOp(ShiftAB, OperA, Imm16);
+ }
+
+ switch (Oper)
+ {
+ case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
+ case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
+ case ShaderOper.RC: OperB = GetOperGpr39 (OpCode); break;
+ case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
+
+ default: throw new ArgumentException(nameof(Oper));
+ }
+
+ bool ProductShiftLeft = false, Merge = false;
+
+ if (Oper == ShaderOper.RC)
+ {
+ OperC = GetOperCbuf34(OpCode);
+ }
+ else
+ {
+ OperC = GetOperGpr39(OpCode);
+
+ ProductShiftLeft = ((OpCode >> 36) & 1) != 0;
+ Merge = ((OpCode >> 37) & 1) != 0;
+ }
+
+ switch (Mode)
+ {
+ //CLO.
+ case 1: OperC = ExtendTo32(OperC, SignC, 16); break;
+
+ //CHI.
+ case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break;
+ }
+
+ ShaderIrNode OperBH = OperB;
+
+ if (HighB)
+ {
+ OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16);
+ }
+
+ ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH);
+
+ if (ProductShiftLeft)
+ {
+ MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16);
+ }
+
+ ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC);
+
+ if (Merge)
+ {
+ AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk);
+ OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
+ AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
+ }
+
+ if (Mode == 4)
+ {
+ OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
+ AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
+ }
+
+ Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
index 6f48d1a8..89949d62 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
@@ -15,11 +15,11 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new NotImplementedException();
}
- long Target = ((int)(OpCode >> 20) << 8) >> 8;
+ int Target = ((int)(OpCode >> 20) << 8) >> 8;
- Target += Block.Position + 8;
+ ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
- Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Block.GetLabel(Target)), OpCode));
+ Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm), OpCode));
}
public static void Exit(ShaderIrBlock Block, long OpCode)
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
index 024fa364..85522ff9 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
@@ -1,19 +1,133 @@
+using System.Collections.Generic;
+
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecoder
{
private const bool AddDbgComments = true;
- public static ShaderIrBlock DecodeBasicBlock(IGalMemory Memory, long Position)
+ public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start)
{
- ShaderIrBlock Block = new ShaderIrBlock();
+ Dictionary<long, ShaderIrBlock> Visited = new Dictionary<long, ShaderIrBlock>();
+ Dictionary<long, ShaderIrBlock> VisitedEnd = new Dictionary<long, ShaderIrBlock>();
+
+ Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>();
+
+ ShaderIrBlock Enqueue(long Position, ShaderIrBlock Source = null)
+ {
+ if (!Visited.TryGetValue(Position, out ShaderIrBlock Output))
+ {
+ Output = new ShaderIrBlock(Position);
+
+ Blocks.Enqueue(Output);
+
+ Visited.Add(Position, Output);
+ }
+
+ if (Source != null)
+ {
+ Output.Sources.Add(Source);
+ }
+
+ return Output;
+ }
+
+ ShaderIrBlock Entry = Enqueue(Start);
+
+ while (Blocks.Count > 0)
+ {
+ ShaderIrBlock Current = Blocks.Dequeue();
+
+ FillBlock(Memory, Current);
+
+ //Set child blocks. "Branch" is the block the branch instruction
+ //points to (when taken), "Next" is the block at the next address,
+ //executed when the branch is not taken. For Unconditional Branches
+ //or end of shader, Next is null.
+ if (Current.Nodes.Count > 0)
+ {
+ ShaderIrNode LastNode = Current.GetLastNode();
+
+ ShaderIrOp Op = GetInnermostOp(LastNode);
+
+ if (Op?.Inst == ShaderIrInst.Bra)
+ {
+ int Offset = ((ShaderIrOperImm)Op.OperandA).Value;
+
+ long Target = Current.EndPosition + Offset;
+
+ Current.Branch = Enqueue(Target, Current);
+ }
+
+ if (NodeHasNext(LastNode))
+ {
+ Current.Next = Enqueue(Current.EndPosition);
+ }
+ }
+
+ //If we have on the graph two blocks with the same end position,
+ //then we need to split the bigger block and have two small blocks,
+ //the end position of the bigger "Current" block should then be == to
+ //the position of the "Smaller" block.
+ while (VisitedEnd.TryGetValue(Current.EndPosition, out ShaderIrBlock Smaller))
+ {
+ if (Current.Position > Smaller.Position)
+ {
+ ShaderIrBlock Temp = Smaller;
+
+ Smaller = Current;
+ Current = Temp;
+ }
+
+ Current.EndPosition = Smaller.Position;
+ Current.Next = Smaller;
+ Current.Branch = null;
+
+ Current.Nodes.RemoveRange(
+ Current.Nodes.Count - Smaller.Nodes.Count,
+ Smaller.Nodes.Count);
+
+ VisitedEnd[Smaller.EndPosition] = Smaller;
+ }
+
+ VisitedEnd.Add(Current.EndPosition, Current);
+ }
+
+ //Make and sort Graph blocks array by position.
+ ShaderIrBlock[] Graph = new ShaderIrBlock[Visited.Count];
- while (true)
+ while (Visited.Count > 0)
{
- Block.Position = Position;
+ ulong FirstPos = ulong.MaxValue;
+
+ foreach (ShaderIrBlock Block in Visited.Values)
+ {
+ if (FirstPos > (ulong)Block.Position)
+ FirstPos = (ulong)Block.Position;
+ }
- Block.MarkLabel(Position);
+ ShaderIrBlock Current = Visited[(long)FirstPos];
+ do
+ {
+ Graph[Graph.Length - Visited.Count] = Current;
+
+ Visited.Remove(Current.Position);
+
+ Current = Current.Next;
+ }
+ while (Current != null);
+ }
+
+ return Graph;
+ }
+
+ private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block)
+ {
+ long Position = Block.Position;
+
+ do
+ {
//Ignore scheduling instructions, which are written every 32 bytes.
if ((Position & 0x1f) == 0)
{
@@ -33,9 +147,20 @@ namespace Ryujinx.Graphics.Gal.Shader
if (AddDbgComments)
{
- string DbgOpCode = $"0x{Position:x16}: 0x{OpCode:x16} ";
+ string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} ";
+
+ DbgOpCode += (Decode?.Method.Name ?? "???");
- Block.AddNode(new ShaderIrCmnt(DbgOpCode + (Decode?.Method.Name ?? "???")));
+ if (Decode == ShaderDecode.Bra)
+ {
+ int Offset = ((int)(OpCode >> 20) << 8) >> 8;
+
+ long Target = Position + Offset;
+
+ DbgOpCode += " (0x" + Target.ToString("x16") + ")";
+ }
+
+ Block.AddNode(new ShaderIrCmnt(DbgOpCode));
}
if (Decode == null)
@@ -44,19 +169,36 @@ namespace Ryujinx.Graphics.Gal.Shader
}
Decode(Block, OpCode);
+ }
+ while (!IsFlowChange(Block.GetLastNode()));
- if (Block.GetLastNode() is ShaderIrOp Op && Op.Inst == ShaderIrInst.Exit)
- {
- break;
- }
+ Block.EndPosition = Position;
+ }
+
+ private static bool IsFlowChange(ShaderIrNode Node)
+ {
+ return !NodeHasNext(GetInnermostOp(Node));
+ }
+
+ private static ShaderIrOp GetInnermostOp(ShaderIrNode Node)
+ {
+ if (Node is ShaderIrCond Cond)
+ {
+ Node = Cond.Child;
}
- return Block;
+ return Node is ShaderIrOp Op ? Op : null;
}
- private static bool IsFlowChange(ShaderIrInst Inst)
+ private static bool NodeHasNext(ShaderIrNode Node)
{
- return Inst == ShaderIrInst.Exit;
+ if (!(Node is ShaderIrOp Op))
+ {
+ return true;
+ }
+
+ return Op.Inst != ShaderIrInst.Exit &&
+ Op.Inst != ShaderIrInst.Bra;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
index 31d72169..50e563b8 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
@@ -4,44 +4,28 @@ namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrBlock
{
- private List<ShaderIrNode> Nodes;
+ public long Position { get; set; }
+ public long EndPosition { get; set; }
- private Dictionary<long, ShaderIrLabel> LabelsToInsert;
+ public ShaderIrBlock Next { get; set; }
+ public ShaderIrBlock Branch { get; set; }
- public long Position;
+ public List<ShaderIrBlock> Sources { get; private set; }
- public ShaderIrBlock()
- {
- Nodes = new List<ShaderIrNode>();
+ public List<ShaderIrNode> Nodes { get; private set; }
- LabelsToInsert = new Dictionary<long, ShaderIrLabel>();
- }
-
- public void AddNode(ShaderIrNode Node)
+ public ShaderIrBlock(long Position)
{
- Nodes.Add(Node);
- }
+ this.Position = Position;
- public ShaderIrLabel GetLabel(long Position)
- {
- if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label))
- {
- return Label;
- }
-
- Label = new ShaderIrLabel();
+ Sources = new List<ShaderIrBlock>();
- LabelsToInsert.Add(Position, Label);
-
- return Label;
+ Nodes = new List<ShaderIrNode>();
}
- public void MarkLabel(long Position)
+ public void AddNode(ShaderIrNode Node)
{
- if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label))
- {
- Nodes.Add(Label);
- }
+ Nodes.Add(Node);
}
public ShaderIrNode[] GetNodes()
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
index af2ccc3b..2de50a4a 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
@@ -65,6 +65,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Cne,
Lsl,
Lsr,
+ Max,
+ Min,
Mul,
Neg,
Not,
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs
deleted file mode 100644
index f5b3585a..00000000
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace Ryujinx.Graphics.Gal.Shader
-{
- class ShaderIrLabel : ShaderIrNode { }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
index acfcc147..51197fd4 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -63,6 +63,9 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0100110011100x", ShaderDecode.I2i_C);
Set("0011100x11100x", ShaderDecode.I2i_I);
Set("0101110011100x", ShaderDecode.I2i_R);
+ Set("0100110000100x", ShaderDecode.Imnmx_C);
+ Set("0011100x00100x", ShaderDecode.Imnmx_I);
+ Set("0101110000100x", ShaderDecode.Imnmx_R);
Set("11100000xxxxxx", ShaderDecode.Ipa);
Set("0100110000011x", ShaderDecode.Iscadd_C);
Set("0011100x00011x", ShaderDecode.Iscadd_I);
@@ -89,6 +92,10 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("1101111101001x", ShaderDecode.Texq);
Set("1101100xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds);
+ Set("0100111xxxxxxx", ShaderDecode.Xmad_CR);
+ Set("0011011x00xxxx", ShaderDecode.Xmad_I);
+ Set("010100010xxxxx", ShaderDecode.Xmad_RC);
+ Set("0101101100xxxx", ShaderDecode.Xmad_RR);
#endregion
}