Change shader cache to identify code by hash instead of address
This commit is contained in:
parent
fb0939f9b6
commit
d0febd50e8
13 changed files with 406 additions and 365 deletions
|
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
|
||||
TextureManager.SetComputeTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
|
||||
|
||||
ShaderProgramInfo info = cs.Shader.Program.Info;
|
||||
ShaderProgramInfo info = cs.Shader.Info;
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = 0;
|
||||
|
|
|
@ -755,11 +755,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||
|
||||
GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses);
|
||||
|
||||
_vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false;
|
||||
_vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
|
||||
|
||||
for (int stage = 0; stage < Constants.ShaderStages; stage++)
|
||||
{
|
||||
ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info;
|
||||
ShaderProgramInfo info = gs.Shaders[stage]?.Info;
|
||||
|
||||
_currentProgramInfo[stage] = info;
|
||||
|
||||
|
|
|
@ -35,14 +35,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
/// This reads as much data as possible, up to the specified maximum size.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">GPU virtual address where the data is located</param>
|
||||
/// <param name="maxSize">Maximum size of the data</param>
|
||||
/// <param name="size">Size of the data</param>
|
||||
/// <returns>The span of the data at the specified memory location</returns>
|
||||
public ReadOnlySpan<byte> GetSpan(ulong gpuVa, ulong maxSize)
|
||||
public ReadOnlySpan<byte> GetSpan(ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
ulong size = _context.MemoryManager.GetSubSize(gpuVa, maxSize);
|
||||
|
||||
return _context.PhysicalMemory.GetSpan(processVa, size);
|
||||
}
|
||||
|
||||
|
@ -86,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 8-bits unsigned integer from GPU mapped memory.
|
||||
/// Writes a 8-bits unsigned integer from GPU mapped memory.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">GPU virtual address where the value is located</param>
|
||||
/// <param name="value">The value to be written</param>
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached shader code for a single shader stage.
|
||||
/// </summary>
|
||||
class CachedShader
|
||||
{
|
||||
/// <summary>
|
||||
/// Shader program containing translated code.
|
||||
/// </summary>
|
||||
public ShaderProgram Program { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Host shader object.
|
||||
/// </summary>
|
||||
public IShader HostShader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maxwell binary shader code.
|
||||
/// </summary>
|
||||
public int[] Code { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instace of the cached shader.
|
||||
/// </summary>
|
||||
/// <param name="program">Shader program</param>
|
||||
/// <param name="code">Maxwell binary shader code</param>
|
||||
public CachedShader(ShaderProgram program, int[] code)
|
||||
{
|
||||
Program = program;
|
||||
Code = code;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -15,14 +16,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Cached shader.
|
||||
/// </summary>
|
||||
public CachedShader Shader { get; }
|
||||
public ShaderProgram Shader { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the compute shader.
|
||||
/// </summary>
|
||||
/// <param name="hostProgram">Host shader program</param>
|
||||
/// <param name="shader">Cached shader</param>
|
||||
public ComputeShader(IProgram hostProgram, CachedShader shader)
|
||||
public ComputeShader(IProgram hostProgram, ShaderProgram shader)
|
||||
{
|
||||
HostProgram = hostProgram;
|
||||
Shader = shader;
|
||||
|
|
35
Ryujinx.Graphics.Gpu/Shader/Fnv1a.cs
Normal file
35
Ryujinx.Graphics.Gpu/Shader/Fnv1a.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
struct Fnv1a
|
||||
{
|
||||
private const int Prime = 0x1000193;
|
||||
private const int OffsetBasis = unchecked((int)0x811c9dc5);
|
||||
|
||||
public int Hash { get; private set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Hash = OffsetBasis;
|
||||
}
|
||||
|
||||
public void Add(ReadOnlySpan<byte> data)
|
||||
{
|
||||
ReadOnlySpan<int> dataInt = MemoryMarshal.Cast<byte, int>(data);
|
||||
|
||||
int offset;
|
||||
|
||||
for (offset = 0; offset < dataInt.Length; offset++)
|
||||
{
|
||||
Hash = (Hash ^ data[offset]) * Prime;
|
||||
}
|
||||
|
||||
for (offset *= 4; offset < data.Length; offset++)
|
||||
{
|
||||
Hash = (Hash ^ data[offset]) * Prime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -15,14 +16,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Compiled shader for each shader stage.
|
||||
/// </summary>
|
||||
public CachedShader[] Shaders { get; }
|
||||
public ShaderProgram[] Shaders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of cached graphics shader.
|
||||
/// </summary>
|
||||
public GraphicsShader()
|
||||
{
|
||||
Shaders = new CachedShader[Constants.ShaderStages];
|
||||
Shaders = new ShaderProgram[Constants.ShaderStages];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ using Ryujinx.Graphics.Shader;
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -17,17 +16,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// </summary>
|
||||
class ShaderCache : IDisposable
|
||||
{
|
||||
private const int MaxProgramSize = 0x100000;
|
||||
private const int ShaderHeaderSize = 0x50;
|
||||
|
||||
private const TranslationFlags DefaultFlags = TranslationFlags.DebugMode;
|
||||
|
||||
private GpuContext _context;
|
||||
private readonly GpuContext _context;
|
||||
|
||||
private ShaderDumper _dumper;
|
||||
private readonly ShaderMap<ComputeShader> _cpPrograms;
|
||||
private readonly ShaderMap<GraphicsShader> _gpPrograms;
|
||||
|
||||
private Dictionary<ulong, List<ComputeShader>> _cpPrograms;
|
||||
|
||||
private Dictionary<ShaderAddresses, List<GraphicsShader>> _gpPrograms;
|
||||
private readonly ShaderDumper _dumper;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the shader cache.
|
||||
|
@ -37,11 +35,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
_context = context;
|
||||
|
||||
_dumper = new ShaderDumper();
|
||||
_cpPrograms = new ShaderMap<ComputeShader>();
|
||||
_gpPrograms = new ShaderMap<GraphicsShader>();
|
||||
|
||||
_cpPrograms = new Dictionary<ulong, List<ComputeShader>>();
|
||||
|
||||
_gpPrograms = new Dictionary<ShaderAddresses, List<GraphicsShader>>();
|
||||
_dumper = new ShaderDumper();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -64,205 +61,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
int localSizeZ,
|
||||
int localMemorySize,
|
||||
int sharedMemorySize)
|
||||
{
|
||||
bool isCached = _cpPrograms.TryGetValue(gpuVa, out List<ComputeShader> list);
|
||||
|
||||
if (isCached)
|
||||
{
|
||||
foreach (ComputeShader cachedCpShader in list)
|
||||
{
|
||||
if (!IsShaderDifferent(cachedCpShader, gpuVa))
|
||||
{
|
||||
return cachedCpShader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CachedShader shader = TranslateComputeShader(
|
||||
gpuVa,
|
||||
localSizeX,
|
||||
localSizeY,
|
||||
localSizeZ,
|
||||
localMemorySize,
|
||||
sharedMemorySize);
|
||||
|
||||
shader.HostShader = _context.Renderer.CompileShader(shader.Program);
|
||||
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader });
|
||||
|
||||
ComputeShader cpShader = new ComputeShader(hostProgram, shader);
|
||||
|
||||
if (!isCached)
|
||||
{
|
||||
list = new List<ComputeShader>();
|
||||
|
||||
_cpPrograms.Add(gpuVa, list);
|
||||
}
|
||||
|
||||
list.Add(cpShader);
|
||||
|
||||
return cpShader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a graphics shader program from the shader cache.
|
||||
/// This includes all the specified shader stages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This automatically translates, compiles and adds the code to the cache if not present.
|
||||
/// </remarks>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="addresses">Addresses of the shaders for each stage</param>
|
||||
/// <returns>Compiled graphics shader code</returns>
|
||||
public GraphicsShader GetGraphicsShader(GpuState state, ShaderAddresses addresses)
|
||||
{
|
||||
bool isCached = _gpPrograms.TryGetValue(addresses, out List<GraphicsShader> list);
|
||||
|
||||
if (isCached)
|
||||
{
|
||||
foreach (GraphicsShader cachedGpShaders in list)
|
||||
{
|
||||
if (!IsShaderDifferent(cachedGpShaders, addresses))
|
||||
{
|
||||
return cachedGpShaders;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsShader gpShaders = new GraphicsShader();
|
||||
|
||||
if (addresses.VertexA != 0)
|
||||
{
|
||||
gpShaders.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpShaders.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex);
|
||||
}
|
||||
|
||||
gpShaders.Shaders[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl);
|
||||
gpShaders.Shaders[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
|
||||
gpShaders.Shaders[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry);
|
||||
gpShaders.Shaders[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment);
|
||||
|
||||
BackpropQualifiers(gpShaders);
|
||||
|
||||
List<IShader> hostShaders = new List<IShader>();
|
||||
|
||||
for (int stage = 0; stage < gpShaders.Shaders.Length; stage++)
|
||||
{
|
||||
ShaderProgram program = gpShaders.Shaders[stage]?.Program;
|
||||
|
||||
if (program == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IShader hostShader = _context.Renderer.CompileShader(program);
|
||||
|
||||
gpShaders.Shaders[stage].HostShader = hostShader;
|
||||
|
||||
hostShaders.Add(hostShader);
|
||||
}
|
||||
|
||||
gpShaders.HostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray());
|
||||
|
||||
if (!isCached)
|
||||
{
|
||||
list = new List<GraphicsShader>();
|
||||
|
||||
_gpPrograms.Add(addresses, list);
|
||||
}
|
||||
|
||||
list.Add(gpShaders);
|
||||
|
||||
return gpShaders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if compute shader code in memory is different from the cached shader.
|
||||
/// </summary>
|
||||
/// <param name="cpShader">Cached compute shader</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
|
||||
/// <returns>True if the code is different, false otherwise</returns>
|
||||
private bool IsShaderDifferent(ComputeShader cpShader, ulong gpuVa)
|
||||
{
|
||||
return IsShaderDifferent(cpShader.Shader, gpuVa);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if graphics shader code from all stages in memory is different from the cached shaders.
|
||||
/// </summary>
|
||||
/// <param name="gpShaders">Cached graphics shaders</param>
|
||||
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
|
||||
/// <returns>True if the code is different, false otherwise</returns>
|
||||
private bool IsShaderDifferent(GraphicsShader gpShaders, ShaderAddresses addresses)
|
||||
{
|
||||
for (int stage = 0; stage < gpShaders.Shaders.Length; stage++)
|
||||
{
|
||||
CachedShader shader = gpShaders.Shaders[stage];
|
||||
|
||||
ulong gpuVa = 0;
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case 0: gpuVa = addresses.Vertex; break;
|
||||
case 1: gpuVa = addresses.TessControl; break;
|
||||
case 2: gpuVa = addresses.TessEvaluation; break;
|
||||
case 3: gpuVa = addresses.Geometry; break;
|
||||
case 4: gpuVa = addresses.Fragment; break;
|
||||
}
|
||||
|
||||
if (IsShaderDifferent(shader, gpuVa))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the code of the specified cached shader is different from the code in memory.
|
||||
/// </summary>
|
||||
/// <param name="shader">Cached shader to compare with</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||
/// <returns>True if the code is different, false otherwise</returns>
|
||||
private bool IsShaderDifferent(CachedShader shader, ulong gpuVa)
|
||||
{
|
||||
if (shader == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> memoryCode = _context.MemoryAccessor.GetSpan(gpuVa, (ulong)shader.Code.Length * 4);
|
||||
|
||||
return !MemoryMarshal.Cast<byte, int>(memoryCode).SequenceEqual(shader.Code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the binary Maxwell shader code to something that the host API accepts.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||
/// <param name="localSizeX">Local group size X of the computer shader</param>
|
||||
/// <param name="localSizeY">Local group size Y of the computer shader</param>
|
||||
/// <param name="localSizeZ">Local group size Z of the computer shader</param>
|
||||
/// <param name="localMemorySize">Local memory size of the compute shader</param>
|
||||
/// <param name="sharedMemorySize">Shared memory size of the compute shader</param>
|
||||
/// <returns>Compiled compute shader code</returns>
|
||||
private CachedShader TranslateComputeShader(
|
||||
ulong gpuVa,
|
||||
int localSizeX,
|
||||
int localSizeY,
|
||||
int localSizeZ,
|
||||
int localMemorySize,
|
||||
int sharedMemorySize)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ShaderPack pack = new ShaderPack();
|
||||
|
||||
ReadOnlySpan<byte> code = GetShaderCodeHeaderless(gpuVa);
|
||||
|
||||
pack.Add(code);
|
||||
|
||||
ComputeShader cs = _cpPrograms.Get(pack, out int hash);
|
||||
|
||||
if (cs != null)
|
||||
{
|
||||
return cs;
|
||||
}
|
||||
|
||||
int QueryInfo(QueryInfoName info, int index)
|
||||
{
|
||||
return info switch
|
||||
|
@ -280,21 +97,108 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
ShaderProgram program;
|
||||
|
||||
ReadOnlySpan<byte> code = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize);
|
||||
_dumper.Dump(code, compute: true, out string fullPath, out string codePath);
|
||||
|
||||
program = Translator.Translate(code, callbacks, DefaultFlags | TranslationFlags.Compute);
|
||||
|
||||
int[] codeCached = MemoryMarshal.Cast<byte, int>(code.Slice(0, program.Size)).ToArray();
|
||||
|
||||
_dumper.Dump(code, compute: true, out string fullPath, out string codePath);
|
||||
|
||||
if (fullPath != null && codePath != null)
|
||||
{
|
||||
program.Prepend("// " + codePath);
|
||||
program.Prepend("// " + fullPath);
|
||||
}
|
||||
|
||||
return new CachedShader(program, codeCached);
|
||||
IShader shader = _context.Renderer.CompileShader(program);
|
||||
|
||||
cs = new ComputeShader(_context.Renderer.CreateProgram(new IShader[] { shader }), program);
|
||||
|
||||
_cpPrograms.Add(hash, cs, pack);
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a graphics shader program from the shader cache.
|
||||
/// This includes all the specified shader stages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This automatically translates, compiles and adds the code to the cache if not present.
|
||||
/// </remarks>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="addresses">Addresses of the shaders for each stage</param>
|
||||
/// <returns>Compiled graphics shader code</returns>
|
||||
public GraphicsShader GetGraphicsShader(GpuState state, ShaderAddresses addresses)
|
||||
{
|
||||
ShaderPack pack = new ShaderPack();
|
||||
|
||||
if (addresses.Vertex != 0)
|
||||
{
|
||||
pack.Add(GetShaderCode(addresses.Vertex));
|
||||
}
|
||||
|
||||
if (addresses.TessControl != 0)
|
||||
{
|
||||
pack.Add(GetShaderCode(addresses.TessControl));
|
||||
}
|
||||
|
||||
if (addresses.TessEvaluation != 0)
|
||||
{
|
||||
pack.Add(GetShaderCode(addresses.TessEvaluation));
|
||||
}
|
||||
|
||||
if (addresses.Geometry != 0)
|
||||
{
|
||||
pack.Add(GetShaderCode(addresses.Geometry));
|
||||
}
|
||||
|
||||
if (addresses.Fragment != 0)
|
||||
{
|
||||
pack.Add(GetShaderCode(addresses.Fragment));
|
||||
}
|
||||
|
||||
GraphicsShader gs = _gpPrograms.Get(pack, out int hash);
|
||||
|
||||
if (gs != null)
|
||||
{
|
||||
return gs;
|
||||
}
|
||||
|
||||
gs = new GraphicsShader();
|
||||
|
||||
if (addresses.VertexA != 0)
|
||||
{
|
||||
gs.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA);
|
||||
}
|
||||
else
|
||||
{
|
||||
gs.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex);
|
||||
}
|
||||
|
||||
gs.Shaders[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl);
|
||||
gs.Shaders[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
|
||||
gs.Shaders[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry);
|
||||
gs.Shaders[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment);
|
||||
|
||||
BackpropQualifiers(gs);
|
||||
|
||||
List<IShader> shaders = new List<IShader>();
|
||||
|
||||
for (int stage = 0; stage < gs.Shaders.Length; stage++)
|
||||
{
|
||||
ShaderProgram program = gs.Shaders[stage];
|
||||
|
||||
if (program == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
shaders.Add(_context.Renderer.CompileShader(program));
|
||||
}
|
||||
|
||||
gs.HostProgram = _context.Renderer.CreateProgram(shaders.ToArray());
|
||||
|
||||
_gpPrograms.Add(hash, gs, pack);
|
||||
|
||||
return gs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -308,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="gpuVa">GPU virtual address of the shader code</param>
|
||||
/// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" shader code</param>
|
||||
/// <returns>Compiled graphics shader code</returns>
|
||||
private CachedShader TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0)
|
||||
private ShaderProgram TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
|
@ -330,37 +234,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
ShaderProgram program;
|
||||
|
||||
int[] codeCached = null;
|
||||
|
||||
if (gpuVaA != 0)
|
||||
{
|
||||
ReadOnlySpan<byte> codeA = _context.MemoryAccessor.GetSpan(gpuVaA, MaxProgramSize);
|
||||
ReadOnlySpan<byte> codeB = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize);
|
||||
|
||||
program = Translator.Translate(codeA, codeB, callbacks, DefaultFlags);
|
||||
|
||||
// TODO: We should also take "codeA" into account.
|
||||
codeCached = MemoryMarshal.Cast<byte, int>(codeB.Slice(0, program.Size)).ToArray();
|
||||
|
||||
_dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA);
|
||||
_dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB);
|
||||
|
||||
if (fullPathA != null && fullPathB != null && codePathA != null && codePathB != null)
|
||||
{
|
||||
program.Prepend("// " + codePathB);
|
||||
program.Prepend("// " + fullPathB);
|
||||
program.Prepend("// " + codePathA);
|
||||
program.Prepend("// " + fullPathA);
|
||||
}
|
||||
// TODO.
|
||||
program = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadOnlySpan<byte> code = _context.MemoryAccessor.GetSpan(gpuVa, MaxProgramSize);
|
||||
ReadOnlySpan<byte> code = GetShaderCode(gpuVa);
|
||||
|
||||
program = Translator.Translate(code, callbacks, DefaultFlags);
|
||||
|
||||
codeCached = MemoryMarshal.Cast<byte, int>(code.Slice(0, program.Size)).ToArray();
|
||||
|
||||
_dumper.Dump(code, compute: false, out string fullPath, out string codePath);
|
||||
|
||||
if (fullPath != null && codePath != null)
|
||||
|
@ -370,9 +254,34 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
}
|
||||
}
|
||||
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
return program;
|
||||
}
|
||||
|
||||
return new CachedShader(program, codeCached);
|
||||
private ReadOnlySpan<byte> GetShaderCode(ulong gpuVa)
|
||||
{
|
||||
return GetShaderCodeImpl(gpuVa, ShaderHeaderSize);
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> GetShaderCodeHeaderless(ulong gpuVa)
|
||||
{
|
||||
return GetShaderCodeImpl(gpuVa, 0);
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> GetShaderCodeImpl(ulong gpuVa, ulong size)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ulong op = _context.MemoryAccessor.Read<ulong>(gpuVa + size);
|
||||
|
||||
size += sizeof(ulong);
|
||||
|
||||
if (op == 0x50b0000000070f00 || op == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _context.MemoryAccessor.GetSpan(gpuVa, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -380,16 +289,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// to ealier shader stages output.
|
||||
/// This is required by older versions of OpenGL (pre-4.3).
|
||||
/// </summary>
|
||||
/// <param name="program">Graphics shader cached code</param>
|
||||
private void BackpropQualifiers(GraphicsShader program)
|
||||
/// <param name="gs">Graphics shader cached code</param>
|
||||
private void BackpropQualifiers(GraphicsShader gs)
|
||||
{
|
||||
ShaderProgram fragmentShader = program.Shaders[4]?.Program;
|
||||
ShaderProgram fragmentShader = gs.Shaders[4];
|
||||
|
||||
bool isFirst = true;
|
||||
|
||||
for (int stage = 3; stage >= 0; stage--)
|
||||
{
|
||||
if (program.Shaders[stage] == null)
|
||||
if (gs.Shaders[stage] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -402,11 +311,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
if (isFirst && !string.IsNullOrEmpty(iq))
|
||||
{
|
||||
program.Shaders[stage].Program.Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
|
||||
gs.Shaders[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.Shaders[stage].Program.Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
|
||||
gs.Shaders[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,8 +382,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
TextureTarget target = descriptor.UnpackTextureTarget();
|
||||
|
||||
bool is2DTexture = target == TextureTarget.Texture2D ||
|
||||
target == TextureTarget.Texture2DRect;
|
||||
bool is2DTexture = target == TextureTarget.Texture2D || target == TextureTarget.Texture2DRect;
|
||||
|
||||
return !descriptor.UnpackTextureCoordNormalized() && is2DTexture;
|
||||
}
|
||||
|
@ -521,26 +429,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (List<ComputeShader> list in _cpPrograms.Values)
|
||||
foreach (ComputeShader shader in _cpPrograms)
|
||||
{
|
||||
foreach (ComputeShader shader in list)
|
||||
{
|
||||
shader.HostProgram.Dispose();
|
||||
shader.Shader?.HostShader.Dispose();
|
||||
}
|
||||
shader.HostProgram.Dispose();
|
||||
}
|
||||
|
||||
foreach (List<GraphicsShader> list in _gpPrograms.Values)
|
||||
|
||||
foreach (GraphicsShader shader in _gpPrograms)
|
||||
{
|
||||
foreach (GraphicsShader shader in list)
|
||||
{
|
||||
shader.HostProgram.Dispose();
|
||||
|
||||
foreach (CachedShader cachedShader in shader.Shaders)
|
||||
{
|
||||
cachedShader?.HostShader.Dispose();
|
||||
}
|
||||
}
|
||||
shader.HostProgram.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
|
@ -9,12 +8,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// </summary>
|
||||
class ShaderDumper
|
||||
{
|
||||
private const int ShaderHeaderSize = 0x50;
|
||||
|
||||
private string _runtimeDir;
|
||||
private string _dumpPath;
|
||||
private int _dumpIndex;
|
||||
|
||||
public int CurrentDumpIndex => _dumpIndex;
|
||||
|
||||
public ShaderDumper()
|
||||
{
|
||||
_dumpIndex = 1;
|
||||
|
@ -29,6 +28,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="codePath">Output path for the shader code without header</param>
|
||||
public void Dump(ReadOnlySpan<byte> code, bool compute, out string fullPath, out string codePath)
|
||||
{
|
||||
int headerSize = compute ? 0 : ShaderHeaderSize;
|
||||
|
||||
_dumpPath = GraphicsConfig.ShadersDumpPath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_dumpPath))
|
||||
|
@ -46,8 +47,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
_dumpIndex++;
|
||||
|
||||
code = Translator.ExtractCode(code, compute, out int headerSize);
|
||||
|
||||
using (MemoryStream stream = new MemoryStream(code.ToArray()))
|
||||
{
|
||||
BinaryReader codeReader = new BinaryReader(stream);
|
||||
|
|
111
Ryujinx.Graphics.Gpu/Shader/ShaderMap.cs
Normal file
111
Ryujinx.Graphics.Gpu/Shader/ShaderMap.cs
Normal file
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
class ShaderMap<T> : IEnumerable<T>
|
||||
{
|
||||
private struct ShaderEntry
|
||||
{
|
||||
public T Shader { get; }
|
||||
|
||||
private readonly byte[][] _code;
|
||||
|
||||
public ShaderEntry(T shader, byte[][] code)
|
||||
{
|
||||
Shader = shader;
|
||||
_code = code;
|
||||
}
|
||||
|
||||
public bool CodeEquals(ShaderPack pack)
|
||||
{
|
||||
if (pack.Count != _code.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < pack.Count; index++)
|
||||
{
|
||||
if (!pack[index].SequenceEqual(_code[index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<int, List<ShaderEntry>> _shaders;
|
||||
|
||||
public ShaderMap()
|
||||
{
|
||||
_shaders = new Dictionary<int, List<ShaderEntry>>();
|
||||
}
|
||||
|
||||
public T Get(ShaderPack pack, out int hash)
|
||||
{
|
||||
Fnv1a hasher = new Fnv1a();
|
||||
|
||||
hasher.Initialize();
|
||||
|
||||
for (int index = 0; index < pack.Count; index++)
|
||||
{
|
||||
hasher.Add(pack[index]);
|
||||
}
|
||||
|
||||
hash = hasher.Hash;
|
||||
|
||||
if (_shaders.TryGetValue(hash, out List<ShaderEntry> list))
|
||||
{
|
||||
for (int index = 0; index < list.Count; index++)
|
||||
{
|
||||
ShaderEntry entry = list[index];
|
||||
|
||||
if (entry.CodeEquals(pack))
|
||||
{
|
||||
return entry.Shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Add(int hash, T shader, ShaderPack pack)
|
||||
{
|
||||
if (!_shaders.TryGetValue(hash, out List<ShaderEntry> list))
|
||||
{
|
||||
list = new List<ShaderEntry>();
|
||||
|
||||
_shaders.Add(hash, list);
|
||||
}
|
||||
|
||||
byte[][] code = new byte[pack.Count][];
|
||||
|
||||
for (int index = 0; index < pack.Count; index++)
|
||||
{
|
||||
code[index] = pack[index].ToArray();
|
||||
}
|
||||
|
||||
list.Add(new ShaderEntry(shader, code));
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
foreach (var list in _shaders.Values)
|
||||
{
|
||||
foreach (ShaderEntry entry in list)
|
||||
{
|
||||
yield return entry.Shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
78
Ryujinx.Graphics.Gpu/Shader/ShaderPack.cs
Normal file
78
Ryujinx.Graphics.Gpu/Shader/ShaderPack.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
ref struct ShaderPack
|
||||
{
|
||||
private ReadOnlySpan<byte> _code0;
|
||||
private ReadOnlySpan<byte> _code1;
|
||||
private ReadOnlySpan<byte> _code2;
|
||||
private ReadOnlySpan<byte> _code3;
|
||||
private ReadOnlySpan<byte> _code4;
|
||||
|
||||
public int Count { get; private set; }
|
||||
|
||||
public ReadOnlySpan<byte> this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((uint)index > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return _code0;
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
return _code1;
|
||||
}
|
||||
else if (index == 2)
|
||||
{
|
||||
return _code2;
|
||||
}
|
||||
else if (index == 3)
|
||||
{
|
||||
return _code3;
|
||||
}
|
||||
else /* if (index == 4) */
|
||||
{
|
||||
return _code4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ReadOnlySpan<byte> code)
|
||||
{
|
||||
if (Count >= 5)
|
||||
{
|
||||
throw new InvalidOperationException("Already full.");
|
||||
}
|
||||
|
||||
int index = Count++;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
_code0 = code;
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
_code1 = code;
|
||||
}
|
||||
else if (index == 2)
|
||||
{
|
||||
_code2 = code;
|
||||
}
|
||||
else if (index == 3)
|
||||
{
|
||||
_code3 = code;
|
||||
}
|
||||
else /* if (index == 4) */
|
||||
{
|
||||
_code4 = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,14 +10,11 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
public string Code { get; private set; }
|
||||
|
||||
public int Size { get; }
|
||||
|
||||
internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code, int size)
|
||||
internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code)
|
||||
{
|
||||
Info = info;
|
||||
Stage = stage;
|
||||
Code = code;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public void Prepend(string line)
|
||||
|
|
|
@ -14,46 +14,22 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
private const int HeaderSize = 0x50;
|
||||
|
||||
public static ReadOnlySpan<byte> ExtractCode(ReadOnlySpan<byte> code, bool compute, out int headerSize)
|
||||
{
|
||||
headerSize = compute ? 0 : HeaderSize;
|
||||
|
||||
Block[] cfg = Decoder.Decode(code, (ulong)headerSize);
|
||||
|
||||
if (cfg == null)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
ulong endAddress = 0;
|
||||
|
||||
foreach (Block block in cfg)
|
||||
{
|
||||
if (endAddress < block.EndAddress)
|
||||
{
|
||||
endAddress = block.EndAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return code.Slice(0, headerSize + (int)endAddress);
|
||||
}
|
||||
|
||||
public static ShaderProgram Translate(ReadOnlySpan<byte> code, TranslatorCallbacks callbacks, TranslationFlags flags)
|
||||
{
|
||||
Operation[] ops = DecodeShader(code, callbacks, flags, out ShaderConfig config, out int size);
|
||||
Operation[] ops = DecodeShader(code, callbacks, flags, out ShaderConfig config);
|
||||
|
||||
return Translate(ops, config, size);
|
||||
return Translate(ops, config);
|
||||
}
|
||||
|
||||
public static ShaderProgram Translate(ReadOnlySpan<byte> vpACode, ReadOnlySpan<byte> vpBCode, TranslatorCallbacks callbacks, TranslationFlags flags)
|
||||
{
|
||||
Operation[] vpAOps = DecodeShader(vpACode, callbacks, flags, out _, out _);
|
||||
Operation[] vpBOps = DecodeShader(vpBCode, callbacks, flags, out ShaderConfig config, out int sizeB);
|
||||
Operation[] vpAOps = DecodeShader(vpACode, callbacks, flags, out _);
|
||||
Operation[] vpBOps = DecodeShader(vpBCode, callbacks, flags, out ShaderConfig config);
|
||||
|
||||
return Translate(Combine(vpAOps, vpBOps), config, sizeB);
|
||||
return Translate(Combine(vpAOps, vpBOps), config);
|
||||
}
|
||||
|
||||
private static ShaderProgram Translate(Operation[] ops, ShaderConfig config, int size)
|
||||
private static ShaderProgram Translate(Operation[] ops, ShaderConfig config)
|
||||
{
|
||||
BasicBlock[] blocks = ControlFlowGraph.MakeCfg(ops);
|
||||
|
||||
|
@ -82,17 +58,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
sInfo.InterpolationQualifiers,
|
||||
sInfo.UsesInstanceId);
|
||||
|
||||
string glslCode = program.Code;
|
||||
|
||||
return new ShaderProgram(spInfo, config.Stage, glslCode, size);
|
||||
return new ShaderProgram(spInfo, config.Stage, program.Code);
|
||||
}
|
||||
|
||||
private static Operation[] DecodeShader(
|
||||
ReadOnlySpan<byte> code,
|
||||
TranslatorCallbacks callbacks,
|
||||
TranslationFlags flags,
|
||||
out ShaderConfig config,
|
||||
out int size)
|
||||
out ShaderConfig config)
|
||||
{
|
||||
Block[] cfg;
|
||||
|
||||
|
@ -113,24 +86,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
config.PrintLog("Invalid branch detected, failed to build CFG.");
|
||||
|
||||
size = 0;
|
||||
|
||||
return Array.Empty<Operation>();
|
||||
}
|
||||
|
||||
EmitterContext context = new EmitterContext(config);
|
||||
|
||||
ulong maxEndAddress = 0;
|
||||
|
||||
for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++)
|
||||
{
|
||||
Block block = cfg[blkIndex];
|
||||
|
||||
if (maxEndAddress < block.EndAddress)
|
||||
{
|
||||
maxEndAddress = block.EndAddress;
|
||||
}
|
||||
|
||||
context.CurrBlock = block;
|
||||
|
||||
context.MarkLabel(context.GetLabel(block.Address));
|
||||
|
@ -215,8 +179,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
size = (int)maxEndAddress + (((flags & TranslationFlags.Compute) != 0) ? 0 : HeaderSize);
|
||||
|
||||
return context.GetOperations();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue