Initial Commit

Based off ReinUsesLisp's PR: https://github.com/Ryujinx/Ryujinx/pull/394
This commit is contained in:
John Clemis 2018-12-13 17:04:17 -06:00
commit 98a81fccdb
10 changed files with 192 additions and 107 deletions

View file

@ -4,9 +4,9 @@ namespace Ryujinx.Graphics.Gal
{ {
public interface IGalShader public interface IGalShader
{ {
void Create(IGalMemory Memory, long Key, GalShaderType Type); void Create(long KeyA, long KeyB, byte[] BinaryA, byte[] BinaryB, GalShaderType Type);
void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type); bool TryGetSize(long Key, out long Size);
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key); IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key); IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);

View file

@ -15,7 +15,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public OGLShaderProgram Current; public OGLShaderProgram Current;
private ConcurrentDictionary<long, OGLShaderStage> Stages; private Dictionary<long, List<OGLShaderStage>> Stages;
private Dictionary<long, OGLShaderStage> TopStages;
private Dictionary<long, long> TopStageSizes;
private Dictionary<OGLShaderProgram, int> Programs; private Dictionary<OGLShaderProgram, int> Programs;
@ -29,61 +33,97 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
this.Buffer = Buffer; this.Buffer = Buffer;
Stages = new ConcurrentDictionary<long, OGLShaderStage>(); Stages = new Dictionary<long, List<OGLShaderStage>>();
TopStages = new Dictionary<long, OGLShaderStage>();
TopStageSizes = new Dictionary<long, long>();
Programs = new Dictionary<OGLShaderProgram, int>(); Programs = new Dictionary<OGLShaderProgram, int>();
} }
public void Create(IGalMemory Memory, long Key, GalShaderType Type) public void Create(
{ long KeyA,
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type)); long KeyB,
} byte[] BinaryA,
byte[] BinaryB,
public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
}
private OGLShaderStage ShaderStageFactory(
IGalMemory Memory,
long Position,
long PositionB,
bool IsDualVp,
GalShaderType Type) GalShaderType Type)
{ {
long Key = KeyB;
if (Stages.TryGetValue(Key, out List<OGLShaderStage> Cache))
{
OGLShaderStage CachedStage = Cache.Find((OGLShaderStage Stage) => Stage.EqualsBinary(BinaryA, BinaryB));
if (CachedStage != null)
{
TopStages[Key] = CachedStage;
return;
}
}
else
{
Cache = new List<OGLShaderStage>();
Stages.Add(Key, Cache);
}
GlslProgram Program; GlslProgram Program;
GlslDecompiler Decompiler = new GlslDecompiler(); GlslDecompiler Decompiler = new GlslDecompiler();
int ShaderDumpIndex = ShaderDumper.DumpIndex; int ShaderDumpIndex = ShaderHelper.DumpIndex;
if (IsDualVp) if (BinaryA != null)
{ {
ShaderDumper.Dump(Memory, Position, Type, "a"); ShaderHelper.Dump(BinaryA, Type, "a");
ShaderDumper.Dump(Memory, PositionB, Type, "b"); ShaderHelper.Dump(BinaryB, Type, "b");
Program = Decompiler.Decompile(Memory, Position, PositionB, Type); Program = Decompiler.Decompile(BinaryA, BinaryB, Type);
} }
else else
{ {
ShaderDumper.Dump(Memory, Position, Type); ShaderHelper.Dump(BinaryB, Type);
Program = Decompiler.Decompile(Memory, Position, Type); Program = Decompiler.Decompile(BinaryB, Type);
} }
string Code = Program.Code; OGLShaderStage NewStage = new OGLShaderStage(
Type,
BinaryA,
BinaryB,
Program.Code,
Program.Uniforms,
Program.Textures);
if (ShaderDumper.IsDumpEnabled()) Cache.Add(NewStage);
TopStages[Key] = NewStage;
if (BinaryA != null)
{ {
Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code; TopStageSizes[KeyA] = BinaryA.Length;
} }
return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures); TopStageSizes[KeyB] = BinaryB.Length;
}
public bool TryGetSize(long Key, out long Size)
{
if (TopStageSizes.TryGetValue(Key, out Size))
{
return true;
}
Size = 0;
return false;
} }
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key) public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
{ {
if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{ {
return Stage.ConstBufferUsage; return Stage.ConstBufferUsage;
} }
@ -93,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key) public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{ {
if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{ {
return Stage.TextureUsage; return Stage.TextureUsage;
} }
@ -122,7 +162,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Bind(long Key) public void Bind(long Key)
{ {
if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{ {
Bind(Stage); Bind(Stage);
} }

View file

@ -1,6 +1,7 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
@ -17,8 +18,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
public int Handle { get; private set; } public int Handle { get; private set; }
public bool IsCompiled { get; private set; }
public GalShaderType Type { get; private set; } public GalShaderType Type { get; private set; }
public string Code { get; private set; } public string Code { get; private set; }
@ -26,13 +25,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; } public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; } public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
private byte[] BinaryA;
private byte[] BinaryB;
public OGLShaderStage( public OGLShaderStage(
GalShaderType Type, GalShaderType Type,
byte[] BinaryA,
byte[] BinaryB,
string Code, string Code,
IEnumerable<ShaderDeclInfo> ConstBufferUsage, IEnumerable<ShaderDeclInfo> ConstBufferUsage,
IEnumerable<ShaderDeclInfo> TextureUsage) IEnumerable<ShaderDeclInfo> TextureUsage)
{ {
this.Type = Type; this.Type = Type;
this.BinaryA = BinaryA;
this.BinaryB = BinaryB;
this.Code = Code; this.Code = Code;
this.ConstBufferUsage = ConstBufferUsage; this.ConstBufferUsage = ConstBufferUsage;
this.TextureUsage = TextureUsage; this.TextureUsage = TextureUsage;
@ -63,6 +69,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public bool EqualsBinary(byte[] BinaryA, byte[] BinaryB)
{
if (!BinaryB.SequenceEqual(this.BinaryB))
{
return false;
}
if (BinaryA != null)
{
return BinaryA.SequenceEqual(this.BinaryA);
}
return true;
}
public static void CompileAndCheck(int Handle, string Code) public static void CompileAndCheck(int Handle, string Code)
{ {
GL.ShaderSource(Handle, Code); GL.ShaderSource(Handle, Code);

View file

@ -109,16 +109,15 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
public GlslProgram Decompile( public GlslProgram Decompile(
IGalMemory Memory, byte[] BinaryA,
long VpAPosition, byte[] BinaryB,
long VpBPosition,
GalShaderType ShaderType) GalShaderType ShaderType)
{ {
Header = new ShaderHeader(Memory, VpAPosition); Header = new ShaderHeader(BinaryA);
HeaderB = new ShaderHeader(Memory, VpBPosition); HeaderB = new ShaderHeader(BinaryB);
Blocks = ShaderDecoder.Decode(Memory, VpAPosition); Blocks = ShaderDecoder.Decode(BinaryA);
BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); BlocksB = ShaderDecoder.Decode(BinaryB);
GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType, Header); GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType, Header);
GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType, HeaderB); GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType, HeaderB);
@ -128,12 +127,12 @@ namespace Ryujinx.Graphics.Gal.Shader
return Decompile(); return Decompile();
} }
public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) public GlslProgram Decompile(byte[] Binary, GalShaderType ShaderType)
{ {
Header = new ShaderHeader(Memory, Position); Header = new ShaderHeader(Binary);
HeaderB = null; HeaderB = null;
Blocks = ShaderDecoder.Decode(Memory, Position); Blocks = ShaderDecoder.Decode(Binary);
BlocksB = null; BlocksB = null;
Decl = new GlslDecl(Blocks, ShaderType, Header); Decl = new GlslDecl(Blocks, ShaderType, Header);

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader namespace Ryujinx.Graphics.Gal.Shader
@ -8,15 +9,13 @@ namespace Ryujinx.Graphics.Gal.Shader
private const bool AddDbgComments = true; private const bool AddDbgComments = true;
public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start) public static ShaderIrBlock[] Decode(byte[] Binary)
{ {
Dictionary<int, ShaderIrBlock> Visited = new Dictionary<int, ShaderIrBlock>(); Dictionary<int, ShaderIrBlock> Visited = new Dictionary<int, ShaderIrBlock>();
Dictionary<int, ShaderIrBlock> VisitedEnd = new Dictionary<int, ShaderIrBlock>(); Dictionary<int, ShaderIrBlock> VisitedEnd = new Dictionary<int, ShaderIrBlock>();
Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>(); Queue<ShaderIrBlock> Blocks = new Queue<ShaderIrBlock>();
long Beginning = Start + HeaderSize;
ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null) ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null)
{ {
if (!Visited.TryGetValue(Position, out ShaderIrBlock Output)) if (!Visited.TryGetValue(Position, out ShaderIrBlock Output))
@ -36,13 +35,13 @@ namespace Ryujinx.Graphics.Gal.Shader
return Output; return Output;
} }
ShaderIrBlock Entry = Enqueue(0); ShaderIrBlock Entry = Enqueue((int)HeaderSize);
while (Blocks.Count > 0) while (Blocks.Count > 0)
{ {
ShaderIrBlock Current = Blocks.Dequeue(); ShaderIrBlock Current = Blocks.Dequeue();
FillBlock(Memory, Current, Beginning); FillBlock(Binary, Current, HeaderSize);
//Set child blocks. "Branch" is the block the branch instruction //Set child blocks. "Branch" is the block the branch instruction
//points to (when taken), "Next" is the block at the next address, //points to (when taken), "Next" is the block at the next address,
@ -136,27 +135,24 @@ namespace Ryujinx.Graphics.Gal.Shader
return Graph; return Graph;
} }
private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning) private static void FillBlock(byte[] Binary, ShaderIrBlock Block, long Beginning)
{ {
int Position = Block.Position; int Position = Block.Position;
do do
{ {
//Ignore scheduling instructions, which are written every 32 bytes. //Ignore scheduling instructions, which are written every 32 bytes.
if ((Position & 0x1f) == 0) if (((Position - Beginning) & 0x1f) == 0)
{ {
Position += 8; Position += 8;
continue; continue;
} }
uint Word0 = (uint)Memory.ReadInt32(Position + Beginning + 0); long OpCode = BitConverter.ToInt64(Binary, Position);
uint Word1 = (uint)Memory.ReadInt32(Position + Beginning + 4);
Position += 8; Position += 8;
long OpCode = Word0 | (long)Word1 << 32;
ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode); ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
if (AddDbgComments) if (AddDbgComments)

View file

@ -59,13 +59,13 @@ namespace Ryujinx.Graphics.Gal.Shader
public bool OmapSampleMask { get; private set; } public bool OmapSampleMask { get; private set; }
public bool OmapDepth { get; private set; } public bool OmapDepth { get; private set; }
public ShaderHeader(IGalMemory Memory, long Position) public unsafe ShaderHeader(byte[] Binary)
{ {
uint CommonWord0 = (uint)Memory.ReadInt32(Position + 0); uint CommonWord0 = BitConverter.ToUInt32(Binary, 0);
uint CommonWord1 = (uint)Memory.ReadInt32(Position + 4); uint CommonWord1 = BitConverter.ToUInt32(Binary, 4);
uint CommonWord2 = (uint)Memory.ReadInt32(Position + 8); uint CommonWord2 = BitConverter.ToUInt32(Binary, 8);
uint CommonWord3 = (uint)Memory.ReadInt32(Position + 12); uint CommonWord3 = BitConverter.ToUInt32(Binary, 12);
uint CommonWord4 = (uint)Memory.ReadInt32(Position + 16); uint CommonWord4 = BitConverter.ToUInt32(Binary, 16);
SphType = ReadBits(CommonWord0, 0, 5); SphType = ReadBits(CommonWord0, 0, 5);
Version = ReadBits(CommonWord0, 5, 5); Version = ReadBits(CommonWord0, 5, 5);
@ -92,8 +92,8 @@ namespace Ryujinx.Graphics.Gal.Shader
StoreReqEnd = ReadBits(CommonWord4, 24, 8); StoreReqEnd = ReadBits(CommonWord4, 24, 8);
//Type 2 (fragment?) reading //Type 2 (fragment?) reading
uint Type2OmapTarget = (uint)Memory.ReadInt32(Position + 72); uint Type2OmapTarget = BitConverter.ToUInt32(Binary, 72);
uint Type2Omap = (uint)Memory.ReadInt32(Position + 76); uint Type2Omap = BitConverter.ToUInt32(Binary, 76);
OmapTargets = new OmapTarget[8]; OmapTargets = new OmapTarget[8];

View file

@ -3,13 +3,13 @@ using System.IO;
namespace Ryujinx.Graphics.Gal namespace Ryujinx.Graphics.Gal
{ {
static class ShaderDumper public static class ShaderHelper
{ {
private static string RuntimeDir; private static string RuntimeDir;
public static int DumpIndex { get; private set; } = 1; public static int DumpIndex { get; private set; } = 1;
public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "") public static void Dump(byte[] Binary, GalShaderType Type, string ExtSuffix = "")
{ {
if (!IsDumpEnabled()) if (!IsDumpEnabled())
{ {
@ -23,47 +23,20 @@ namespace Ryujinx.Graphics.Gal
DumpIndex++; DumpIndex++;
using (FileStream FullFile = File.Create(FullPath))
using (FileStream CodeFile = File.Create(CodePath)) using (FileStream CodeFile = File.Create(CodePath))
using (BinaryWriter Writer = new BinaryWriter(CodeFile))
{ {
BinaryWriter FullWriter = new BinaryWriter(FullFile); long Offset;
BinaryWriter CodeWriter = new BinaryWriter(CodeFile);
for (long i = 0; i < 0x50; i += 4) for (Offset = 0; Offset + 0x50 < Binary.LongLength; Offset++)
{ {
FullWriter.Write(Memory.ReadInt32(Position + i)); Writer.Write(Binary[Offset + 0x50]);
} }
long Offset = 0; //Align to meet nvdisasm requirements
ulong Instruction = 0;
//Dump until a NOP instruction is found
while ((Instruction >> 52 & 0xfff8) != 0x50b0)
{
uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0);
uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4);
Instruction = Word0 | (ulong)Word1 << 32;
//Zero instructions (other kind of NOP) stop immediatly,
//this is to avoid two rows of zeroes
if (Instruction == 0)
{
break;
}
FullWriter.Write(Instruction);
CodeWriter.Write(Instruction);
Offset += 8;
}
//Align to meet nvdisasm requeriments
while (Offset % 0x20 != 0) while (Offset % 0x20 != 0)
{ {
FullWriter.Write(0); Writer.Write(0);
CodeWriter.Write(0);
Offset += 4; Offset += 4;
} }

View file

@ -289,7 +289,14 @@ namespace Ryujinx.Graphics.Graphics3d
Keys[(int)GalShaderType.Vertex] = VpBPos; Keys[(int)GalShaderType.Vertex] = VpBPos;
Gpu.Renderer.Shader.Create(Vmm, VpAPos, VpBPos, GalShaderType.Vertex); if (IsShaderModified(Vmm, VpAPos) || IsShaderModified(Vmm, VpBPos))
{
byte[] BinaryA = ReadShaderBinary(Vmm, VpAPos);
byte[] BinaryB = ReadShaderBinary(Vmm, VpBPos);
Gpu.Renderer.Shader.Create(VpAPos, VpBPos, BinaryA, BinaryB, GalShaderType.Vertex);
}
Gpu.Renderer.Shader.Bind(VpBPos); Gpu.Renderer.Shader.Bind(VpBPos);
Index = 2; Index = 2;
@ -316,7 +323,14 @@ namespace Ryujinx.Graphics.Graphics3d
Keys[(int)Type] = Key; Keys[(int)Type] = Key;
Gpu.Renderer.Shader.Create(Vmm, Key, Type); if (IsShaderModified(Vmm, Key))
{
byte[] Binary = ReadShaderBinary(Vmm, Key);
Gpu.Renderer.Shader.Create(0, Key, null, Binary, Type);
}
Gpu.Renderer.Shader.Bind(Key); Gpu.Renderer.Shader.Bind(Key);
} }
@ -1010,5 +1024,48 @@ namespace Ryujinx.Graphics.Graphics3d
{ {
Registers[(int)Reg] = Value; Registers[(int)Reg] = Value;
} }
private bool IsShaderModified(NvGpuVmm Vmm, long Key)
{
long Address = Vmm.GetPhysicalAddress(Key);
if (Gpu.Renderer.Shader.TryGetSize(Address, out long Size))
{
return Vmm.IsRegionModified(Address, Size, NvGpuBufferType.Shader);
}
return true;
}
private byte[] ReadShaderBinary(NvGpuVmm Vmm, long Key)
{
long Size = GetShaderSize(Vmm, Key);
long Address = Vmm.GetPhysicalAddress(Key);
return Vmm.ReadBytes(Key, Size);
}
private static long GetShaderSize(NvGpuVmm Vmm, long Position)
{
const int NopInst = 0x50b0;
long Offset = 0x50;
ulong OpCode = 0;
do
{
uint Word0 = (uint)Vmm.ReadInt32(Position + Offset + 0);
uint Word1 = (uint)Vmm.ReadInt32(Position + Offset + 4);
OpCode = Word0 | (ulong)Word1 << 32;
Offset += 8;
}
while ((OpCode >> 52 & 0xfff8) != NopInst && OpCode != 0);
return Offset - 8;
}
} }
} }

View file

@ -2,6 +2,7 @@ namespace Ryujinx.Graphics.Memory
{ {
public enum NvGpuBufferType public enum NvGpuBufferType
{ {
Shader,
Index, Index,
Vertex, Vertex,
Texture, Texture,

View file

@ -24,14 +24,12 @@ namespace Ryujinx.ShaderTools
case "f": ShaderType = GalShaderType.Fragment; break; case "f": ShaderType = GalShaderType.Fragment; break;
} }
using (FileStream FS = new FileStream(args[1], FileMode.Open, FileAccess.Read))
{
Memory Mem = new Memory(FS);
GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType); byte[] Binary = File.ReadAllBytes(args[1]);
Console.WriteLine(Program.Code); GlslProgram Program = Decompiler.Decompile(Binary, ShaderType);
}
Console.WriteLine(Program.Code);
} }
else else
{ {