Shader invalidation and cache

This commit is contained in:
ReinUsesLisp 2018-08-24 02:39:35 -03:00
parent 27023e7afc
commit 68f47db918
10 changed files with 192 additions and 104 deletions

View file

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

View file

@ -15,7 +15,9 @@ 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<OGLShaderProgram, int> Programs;
@ -29,60 +31,71 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
this.Buffer = Buffer;
Stages = new ConcurrentDictionary<long, OGLShaderStage>();
Stages = new Dictionary<long, List<OGLShaderStage>>();
TopStages = new Dictionary<long, OGLShaderStage>();
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 Key,
byte[] BinaryA,
byte[] BinaryB,
GalShaderType Type)
{
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();
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);
}
return new OGLShaderStage(
OGLShaderStage NewStage = new OGLShaderStage(
Type,
BinaryA,
BinaryB,
Program.Code,
Program.Uniforms,
Program.Textures);
Cache.Add(NewStage);
TopStages[Key] = NewStage;
}
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
if (TopStages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.ConstBufferUsage;
}
@ -92,7 +105,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;
}
@ -121,7 +134,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

@ -8,7 +8,7 @@ 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<long, ShaderIrBlock> Visited = new Dictionary<long, ShaderIrBlock>();
Dictionary<long, ShaderIrBlock> VisitedEnd = new Dictionary<long, ShaderIrBlock>();
@ -34,13 +34,13 @@ namespace Ryujinx.Graphics.Gal.Shader
return Output;
}
ShaderIrBlock Entry = Enqueue(Start + HeaderSize);
ShaderIrBlock Entry = Enqueue(HeaderSize);
while (Blocks.Count > 0)
{
ShaderIrBlock Current = Blocks.Dequeue();
FillBlock(Memory, Current, Start + HeaderSize);
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,
@ -124,7 +124,7 @@ 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)
{
long Position = Block.Position;
@ -138,13 +138,10 @@ namespace Ryujinx.Graphics.Gal.Shader
continue;
}
uint Word0 = (uint)Memory.ReadInt32(Position + 0);
uint Word1 = (uint)Memory.ReadInt32(Position + 4);
long OpCode = ReadQWord(Binary, Position);
Position += 8;
long OpCode = Word0 | (long)Word1 << 32;
ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
if (AddDbgComments)
@ -202,5 +199,18 @@ namespace Ryujinx.Graphics.Gal.Shader
return Op.Inst != ShaderIrInst.Exit &&
Op.Inst != ShaderIrInst.Bra;
}
private static unsafe long ReadQWord(byte[] Bytes, long Position)
{
if (Position >= Bytes.Length)
{
throw new System.InvalidOperationException();
}
fixed (byte* Pointer = Bytes)
{
return *((long*)(Pointer + Position));
}
}
}
}

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 = ReadWord(Binary, 0);
uint CommonWord1 = ReadWord(Binary, 4);
uint CommonWord2 = ReadWord(Binary, 8);
uint CommonWord3 = ReadWord(Binary, 12);
uint CommonWord4 = ReadWord(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 = ReadWord(Binary, 72);
uint Type2Omap = ReadWord(Binary, 76);
OmapTargets = new OmapTarget[8];
@ -136,6 +136,14 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
private static unsafe uint ReadWord(byte[] Bytes, long Position)
{
fixed (byte* Pointer = Bytes)
{
return *((uint*)(Pointer + Position));
}
}
private static int ReadBits(uint Word, int Offset, int BitWidth)
{
uint Mask = (1u << BitWidth) - 1u;

View file

@ -3,13 +3,13 @@ using System.IO;
namespace Ryujinx.Graphics.Gal
{
static class ShaderDumper
public static class ShaderHelper
{
private static string RuntimeDir;
private static int DumpIndex = 1;
public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "")
public static void Dump(byte[] Binary, GalShaderType Type, string ExtSuffix = "")
{
if (string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath))
{
@ -23,46 +23,22 @@ namespace Ryujinx.Graphics.Gal
DumpIndex++;
using (FileStream FullFile = File.Create(FullPath))
File.WriteAllBytes(FullPath, Binary);
using (FileStream CodeFile = File.Create(CodePath))
using (BinaryWriter FullWriter = new BinaryWriter(FullFile))
using (BinaryWriter CodeWriter = new BinaryWriter(CodeFile))
using (BinaryWriter Writer = new BinaryWriter(CodeFile))
{
for (long i = 0; i < 0x50; i += 4)
long Offset;
for (Offset = 0; Offset + 0x50 < Binary.LongLength; Offset++)
{
FullWriter.Write(Memory.ReadInt32(Position + i));
}
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;
Writer.Write(Binary[Offset + 0x50]);
}
//Align to meet nvdisasm requeriments
while (Offset % 0x20 != 0)
{
FullWriter.Write(0);
CodeWriter.Write(0);
Writer.Write(0);
Offset += 4;
}

View file

@ -29,6 +29,8 @@ namespace Ryujinx.HLE.Gpu.Engines
private int CurrentInstance = 0;
private Dictionary<long, long> ShadersSize;
public NvGpuEngine3d(NvGpu Gpu)
{
this.Gpu = Gpu;
@ -68,6 +70,8 @@ namespace Ryujinx.HLE.Gpu.Engines
{
UploadedKeys[i] = new List<long>();
}
ShadersSize = new Dictionary<long, long>();
}
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
@ -270,7 +274,14 @@ namespace Ryujinx.HLE.Gpu.Engines
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(VpBPos, BinaryA, BinaryB, GalShaderType.Vertex);
}
Gpu.Renderer.Shader.Bind(VpBPos);
Index = 2;
@ -297,7 +308,13 @@ namespace Ryujinx.HLE.Gpu.Engines
Keys[(int)Type] = Key;
Gpu.Renderer.Shader.Create(Vmm, Key, Type);
if (IsShaderModified(Vmm, Key))
{
byte[] Binary = ReadShaderBinary(Vmm, Key);
Gpu.Renderer.Shader.Create(Key, null, Binary, Type);
}
Gpu.Renderer.Shader.Bind(Key);
}
@ -870,5 +887,53 @@ namespace Ryujinx.HLE.Gpu.Engines
return Vmm.IsRegionModified(Key, Size, Type);
}
private bool IsShaderModified(NvGpuVmm Vmm, long Key)
{
long Address = Vmm.GetPhysicalAddress(Key);
if (ShadersSize.TryGetValue(Address, out long Size))
{
if (!QueryKeyUpload(Vmm, Address, Size, NvGpuBufferType.Shader))
{
return false;
}
}
return true;
}
private byte[] ReadShaderBinary(NvGpuVmm Vmm, long Key)
{
long Size = GetShaderSize(Vmm, Key);
long Address = Vmm.GetPhysicalAddress(Key);
ShadersSize[Address] = Size;
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.HLE.Gpu.Memory
{
enum NvGpuBufferType
{
Shader,
Index,
Vertex,
Texture,

View file

@ -24,14 +24,11 @@ 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);
byte[] Binary = File.ReadAllBytes(args[1]);
GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType);
GlslProgram Program = Decompiler.Decompile(Binary, ShaderType);
Console.WriteLine(Program.Code);
}
Console.WriteLine(Program.Code);
}
else
{