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
parent 2e143365eb
commit 98a81fccdb
10 changed files with 192 additions and 107 deletions

View file

@ -4,9 +4,9 @@ namespace Ryujinx.Graphics.Gal
{
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> GetTextureUsage(long Key);

View file

@ -15,7 +15,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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;
@ -29,61 +33,97 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
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>();
}
public void Create(IGalMemory Memory, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type));
}
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,
public void Create(
long KeyA,
long KeyB,
byte[] BinaryA,
byte[] BinaryB,
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;
GlslDecompiler Decompiler = new GlslDecompiler();
int ShaderDumpIndex = ShaderDumper.DumpIndex;
int ShaderDumpIndex = ShaderHelper.DumpIndex;
if (IsDualVp)
if (BinaryA != null)
{
ShaderDumper.Dump(Memory, Position, Type, "a");
ShaderDumper.Dump(Memory, PositionB, Type, "b");
ShaderHelper.Dump(BinaryA, Type, "a");
ShaderHelper.Dump(BinaryB, Type, "b");
Program = Decompiler.Decompile(Memory, Position, PositionB, Type);
Program = Decompiler.Decompile(BinaryA, BinaryB, Type);
}
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)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.ConstBufferUsage;
}
@ -93,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.TextureUsage;
}
@ -122,7 +162,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Bind(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{
Bind(Stage);
}

View file

@ -1,6 +1,7 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL
{
@ -17,8 +18,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
public int Handle { get; private set; }
public bool IsCompiled { get; private set; }
public GalShaderType Type { 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> TextureUsage { get; private set; }
private byte[] BinaryA;
private byte[] BinaryB;
public OGLShaderStage(
GalShaderType Type,
byte[] BinaryA,
byte[] BinaryB,
string Code,
IEnumerable<ShaderDeclInfo> ConstBufferUsage,
IEnumerable<ShaderDeclInfo> TextureUsage)
{
this.Type = Type;
this.BinaryA = BinaryA;
this.BinaryB = BinaryB;
this.Code = Code;
this.ConstBufferUsage = ConstBufferUsage;
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)
{
GL.ShaderSource(Handle, Code);

View file

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

View file

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

View file

@ -59,13 +59,13 @@ namespace Ryujinx.Graphics.Gal.Shader
public bool OmapSampleMask { 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 CommonWord1 = (uint)Memory.ReadInt32(Position + 4);
uint CommonWord2 = (uint)Memory.ReadInt32(Position + 8);
uint CommonWord3 = (uint)Memory.ReadInt32(Position + 12);
uint CommonWord4 = (uint)Memory.ReadInt32(Position + 16);
uint CommonWord0 = BitConverter.ToUInt32(Binary, 0);
uint CommonWord1 = BitConverter.ToUInt32(Binary, 4);
uint CommonWord2 = BitConverter.ToUInt32(Binary, 8);
uint CommonWord3 = BitConverter.ToUInt32(Binary, 12);
uint CommonWord4 = BitConverter.ToUInt32(Binary, 16);
SphType = ReadBits(CommonWord0, 0, 5);
Version = ReadBits(CommonWord0, 5, 5);
@ -92,8 +92,8 @@ namespace Ryujinx.Graphics.Gal.Shader
StoreReqEnd = ReadBits(CommonWord4, 24, 8);
//Type 2 (fragment?) reading
uint Type2OmapTarget = (uint)Memory.ReadInt32(Position + 72);
uint Type2Omap = (uint)Memory.ReadInt32(Position + 76);
uint Type2OmapTarget = BitConverter.ToUInt32(Binary, 72);
uint Type2Omap = BitConverter.ToUInt32(Binary, 76);
OmapTargets = new OmapTarget[8];

View file

@ -3,13 +3,13 @@ using System.IO;
namespace Ryujinx.Graphics.Gal
{
static class ShaderDumper
public static class ShaderHelper
{
private static string RuntimeDir;
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())
{
@ -23,47 +23,20 @@ namespace Ryujinx.Graphics.Gal
DumpIndex++;
using (FileStream FullFile = File.Create(FullPath))
using (FileStream CodeFile = File.Create(CodePath))
using (BinaryWriter Writer = new BinaryWriter(CodeFile))
{
BinaryWriter FullWriter = new BinaryWriter(FullFile);
BinaryWriter CodeWriter = new BinaryWriter(CodeFile);
long Offset;
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;
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
//Align to meet nvdisasm requirements
while (Offset % 0x20 != 0)
{
FullWriter.Write(0);
CodeWriter.Write(0);
Writer.Write(0);
Offset += 4;
}

View file

@ -289,7 +289,14 @@ namespace Ryujinx.Graphics.Graphics3d
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);
Index = 2;
@ -316,7 +323,14 @@ namespace Ryujinx.Graphics.Graphics3d
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);
}
@ -1010,5 +1024,48 @@ namespace Ryujinx.Graphics.Graphics3d
{
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
{
Shader,
Index,
Vertex,
Texture,

View file

@ -24,14 +24,12 @@ namespace Ryujinx.ShaderTools
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
{