Implement persistent shader cache

This commit is contained in:
gdkchan 2020-02-08 21:02:46 -03:00
parent d0febd50e8
commit 00739fdac4
30 changed files with 749 additions and 336 deletions

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.GAL
void SetIndexBuffer(BufferRange buffer, IndexType type);
void SetImage(int index, ShaderStage stage, ITexture texture);
void SetImage(int unit, ITexture texture);
void SetPointSize(float size);
@ -54,15 +54,15 @@ namespace Ryujinx.Graphics.GAL
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
void SetSampler(int index, ShaderStage stage, ISampler sampler);
void SetSampler(int unit, ISampler sampler);
void SetStencilTest(StencilTestDescriptor stencilTest);
void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer);
void SetStorageBuffer(int bindingPoint, BufferRange buffer);
void SetTexture(int index, ShaderStage stage, ITexture texture);
void SetTexture(int unit, ITexture texture);
void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
void SetUniformBuffer(int bindingPoint, BufferRange buffer);
void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs);
void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers);

View file

@ -2,5 +2,13 @@ using System;
namespace Ryujinx.Graphics.GAL
{
public interface IProgram : IDisposable { }
public interface IProgram : IDisposable
{
void SetUniformBufferBindingPoint(string name, int bindingPoint);
void SetStorageBufferBindingPoint(string name, int bindingPoint);
void SetTextureUnit(string name, int unit);
void SetImageUnit(string name, int unit);
byte[] GetGpuBinary();
}
}

View file

@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.GAL
IBuffer CreateBuffer(int size);
IProgram CreateProgram(IShader[] shaders);
IProgram CreateProgramFromGpuBinary(ReadOnlySpan<byte> data);
ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info);

View file

@ -10,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
partial class Methods
{
/// <summary>
/// Meta data of the current compute shader.
/// </summary>
public ShaderMeta CurrentCpMeta { get; private set; }
/// <summary>
/// Dispatches compute work.
/// </summary>
@ -29,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);
ComputeShader cs = ShaderCache.GetComputeShader(
Shader.Shader cs = ShaderCache.GetComputeShader(
shaderGpuVa,
qmd.CtaThreadDimension0,
qmd.CtaThreadDimension1,
@ -37,6 +42,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
localMemorySize,
sharedMemorySize);
CurrentCpMeta = cs.Meta;
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
@ -49,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
TextureManager.SetComputeTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
ShaderProgramInfo info = cs.Shader.Info;
ShaderProgramInfo info = cs.Meta.Info[0];
uint sbEnableMask = 0;
uint ubEnableMask = 0;

View file

@ -18,7 +18,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
partial class Methods
{
private readonly GpuContext _context;
private readonly ShaderProgramInfo[] _currentProgramInfo;
/// <summary>
/// Meta data of the current graphics shader.
/// </summary>
public ShaderMeta CurrentGpMeta { get; private set; }
/// <summary>
/// In-memory shader cache.
@ -48,8 +52,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
ShaderCache = new ShaderCache(_context);
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
BufferManager = new BufferManager(context);
TextureManager = new TextureManager(context);
}
@ -223,9 +225,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// </summary>
private void UpdateStorageBuffers()
{
for (int stage = 0; stage < _currentProgramInfo.Length; stage++)
for (int stage = 0; stage < CurrentGpMeta.Info.Length; stage++)
{
ShaderProgramInfo info = _currentProgramInfo[stage];
ShaderProgramInfo info = CurrentGpMeta.Info[stage];
if (info == null)
{
@ -753,15 +755,15 @@ namespace Ryujinx.Graphics.Gpu.Engine
addressesArray[index] = baseAddress + shader.Offset;
}
GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses);
Shader.Shader gs = ShaderCache.GetGraphicsShader(state, addresses);
_vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
CurrentGpMeta = gs.Meta;
_vsUsesInstanceId = gs.Meta.Info[0]?.UsesInstanceId ?? false;
for (int stage = 0; stage < Constants.ShaderStages; stage++)
{
ShaderProgramInfo info = gs.Shaders[stage]?.Info;
_currentProgramInfo[stage] = info;
ShaderProgramInfo info = gs.Meta.Info[stage];
if (info == null)
{

View file

@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.IO;
namespace Ryujinx.Graphics.Gpu
{
@ -95,6 +96,16 @@ namespace Ryujinx.Graphics.Gpu
SequenceNumber++;
}
public void LoadShaderCache() => Methods.ShaderCache.LoadShaderCache();
public void SetShaderCachePath(string path)
{
// Ensure that the directory exists.
Directory.CreateDirectory(path);
Methods.ShaderCache.Configuration.ShaderPath = path;
}
/// <summary>
/// Sets the process memory manager, after the application process is initialized.
/// This is required for any GPU memory access.

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using System;
@ -224,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_textureState[stageIndex][index].Texture = hostTexture;
_context.Renderer.Pipeline.SetTexture(index, stage, hostTexture);
_context.Renderer.Pipeline.SetTexture(CurrentShaderMeta().GetTextureUnit(stage, index), hostTexture);
}
Sampler sampler = _samplerPool.Get(samplerId);
@ -235,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_textureState[stageIndex][index].Sampler = hostSampler;
_context.Renderer.Pipeline.SetSampler(index, stage, hostSampler);
_context.Renderer.Pipeline.SetSampler(CurrentShaderMeta().GetTextureUnit(stage, index), hostSampler);
}
}
}
@ -270,7 +271,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_imageState[stageIndex][index].Texture = hostTexture;
_context.Renderer.Pipeline.SetImage(index, stage, hostTexture);
_context.Renderer.Pipeline.SetImage(CurrentShaderMeta().GetImageUnit(stage, index), hostTexture);
}
}
}
@ -363,5 +364,14 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_rebind = true;
}
/// <summary>
/// Gets the shader meta data for the currently bound shader on the graphics or compute pipeline.
/// </summary>
/// <returns>Currently bound shader</returns>
private ShaderMeta CurrentShaderMeta()
{
return _isCompute ? _context.Methods.CurrentCpMeta : _context.Methods.CurrentGpMeta;
}
}
}

View file

@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using System;
@ -409,7 +410,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
_context.Renderer.Pipeline.SetStorageBuffer(index, ShaderStage.Compute, buffer);
int bindingPoint = CurrentShaderMeta(ShaderStage.Compute).GetStorageBufferBindingPoint(ShaderStage.Compute, index);
_context.Renderer.Pipeline.SetStorageBuffer(bindingPoint, buffer);
}
enableMask = _cpUniformBuffers.EnableMask;
@ -430,7 +433,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
_context.Renderer.Pipeline.SetUniformBuffer(index, ShaderStage.Compute, buffer);
int bindingPoint = CurrentShaderMeta(ShaderStage.Compute).GetUniformBufferBindingPoint(ShaderStage.Compute, index);
_context.Renderer.Pipeline.SetUniformBuffer(bindingPoint, buffer);
}
// Force rebind after doing compute work.
@ -598,11 +603,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (isStorage)
{
_context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer);
int bindingPoint = CurrentShaderMeta(stage).GetStorageBufferBindingPoint(stage, index);
_context.Renderer.Pipeline.SetStorageBuffer(bindingPoint, buffer);
}
else
{
_context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer);
int bindingPoint = CurrentShaderMeta(stage).GetUniformBufferBindingPoint(stage, index);
_context.Renderer.Pipeline.SetUniformBuffer(bindingPoint, buffer);
}
}
@ -686,6 +695,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Gets the shader meta data for the currently bound shader on the graphics or compute pipeline.
/// </summary>
/// <param name="stage">Stage of the stage where the bindings will be modified</param>
/// <returns>Currently bound shader</returns>
private ShaderMeta CurrentShaderMeta(ShaderStage stage)
{
return stage == ShaderStage.Compute ? _context.Methods.CurrentCpMeta : _context.Methods.CurrentGpMeta;
}
/// <summary>
/// Disposes all buffers in the cache.
/// It's an error to use the buffer manager after disposal.

View file

@ -233,28 +233,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return PteUnmapped;
}
/// <summary>
/// Gets the number of mapped or reserved pages on a given region.
/// </summary>
/// <param name="gpuVa">Start GPU virtual address of the region</param>
/// <param name="maxSize">Maximum size of the data</param>
/// <returns>Mapped size in bytes of the specified region</returns>
internal ulong GetSubSize(ulong gpuVa, ulong maxSize)
{
ulong size = 0;
while (GetPte(gpuVa + size) != PteUnmapped)
{
size += PageSize;
if (size >= maxSize)
{
return maxSize;
}
}
return size;
}
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address.
/// </summary>

View file

@ -3,18 +3,33 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// FNV1a hash calculation.
/// This is not a strict implementation of the FNV1a algorithm,
/// it was optimized for speed.
/// </summary>
struct Fnv1a
{
private const int Prime = 0x1000193;
private const int OffsetBasis = unchecked((int)0x811c9dc5);
/// <summary>
/// Current hash value.
/// </summary>
public int Hash { get; private set; }
/// <summary>
/// Initializes the hash value.
/// </summary>
public void Initialize()
{
Hash = OffsetBasis;
}
/// <summary>
/// Hashes data and updates the current hash value.
/// </summary>
/// <param name="data">Data to be hashed</param>
public void Add(ReadOnlySpan<byte> data)
{
ReadOnlySpan<int> dataInt = MemoryMarshal.Cast<byte, int>(data);

View file

@ -1,29 +0,0 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Cached graphics shader code for all stages.
/// </summary>
class GraphicsShader
{
/// <summary>
/// Host shader program object.
/// </summary>
public IProgram HostProgram { get; set; }
/// <summary>
/// Compiled shader for each shader stage.
/// </summary>
public ShaderProgram[] Shaders { get; }
/// <summary>
/// Creates a new instance of cached graphics shader.
/// </summary>
public GraphicsShader()
{
Shaders = new ShaderProgram[Constants.ShaderStages];
}
}
}

View file

@ -1,12 +1,11 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Cached compute shader code.
/// </summary>
class ComputeShader
class Shader
{
/// <summary>
/// Host shader program object.
@ -16,17 +15,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Cached shader.
/// </summary>
public ShaderProgram Shader { get; }
public ShaderMeta Meta { 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, ShaderProgram shader)
/// <param name="meta">Shader meta data</param>
public Shader(IProgram hostProgram, ShaderMeta meta)
{
HostProgram = hostProgram;
Shader = shader;
Meta = meta;
}
}
}

View file

@ -5,7 +5,7 @@ using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gpu.Shader
{
@ -22,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly GpuContext _context;
private readonly ShaderMap<ComputeShader> _cpPrograms;
private readonly ShaderMap<GraphicsShader> _gpPrograms;
private readonly ShaderMap<Shader> _cache;
private readonly ShaderDumper _dumper;
public ShaderCacheConfiguration Configuration { get; }
/// <summary>
/// Creates a new instance of the shader cache.
/// </summary>
@ -35,10 +36,40 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
_context = context;
_cpPrograms = new ShaderMap<ComputeShader>();
_gpPrograms = new ShaderMap<GraphicsShader>();
_cache = new ShaderMap<Shader>();
_dumper = new ShaderDumper();
_dumper = new ShaderDumper();
Configuration = new ShaderCacheConfiguration();
}
/// <summary>
/// Loads all pre-compiled shaders cached on disk.
/// </summary>
public void LoadShaderCache()
{
if (!Configuration.Enabled)
{
return;
}
ShaderCacheFileFormat[] cached = ShaderCacheFile.Load(Configuration.ShaderPath);
foreach (ShaderCacheFileFormat scff in cached)
{
IProgram hostProgram = _context.Renderer.CreateProgramFromGpuBinary(scff.Code);
Shader shader = new Shader(hostProgram, new ShaderMeta(hostProgram, scff.Info));
ShaderPack pack = new ShaderPack();
for (int index = 0; index < scff.GuestCode.Length; index++)
{
pack.Add(scff.GuestCode[index]);
}
_cache.Add(scff.Hash, shader, pack);
}
}
/// <summary>
@ -54,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <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>
public ComputeShader GetComputeShader(
public Shader GetComputeShader(
ulong gpuVa,
int localSizeX,
int localSizeY,
@ -73,13 +104,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
pack.Add(code);
ComputeShader cs = _cpPrograms.Get(pack, out int hash);
Shader cs = _cache.Get(pack, out int hash);
if (cs != null)
{
return cs;
}
_dumper.Dump(code, compute: true, out string fullPath, out string codePath);
int QueryInfo(QueryInfoName info, int index)
{
return info switch
@ -95,11 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorCallbacks callbacks = new TranslatorCallbacks(QueryInfo, PrintLog);
ShaderProgram program;
_dumper.Dump(code, compute: true, out string fullPath, out string codePath);
program = Translator.Translate(code, callbacks, DefaultFlags | TranslationFlags.Compute);
ShaderProgram program = Translator.Translate(code, callbacks, DefaultFlags | TranslationFlags.Compute);
if (fullPath != null && codePath != null)
{
@ -109,9 +138,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
IShader shader = _context.Renderer.CompileShader(program);
cs = new ComputeShader(_context.Renderer.CreateProgram(new IShader[] { shader }), program);
IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader });
_cpPrograms.Add(hash, cs, pack);
cs = new Shader(hostProgram, new ShaderMeta(hostProgram, program.Info));
int insertIndex = _cache.Add(hash, cs, pack);
if (Configuration.Enabled)
{
ShaderProgramInfo[] info = new ShaderProgramInfo[] { program.Info };
ShaderCacheFile.Save(Configuration.ShaderPath, info, pack, hostProgram.GetGpuBinary(), hash, insertIndex);
}
return cs;
}
@ -126,7 +164,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <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)
public Shader GetGraphicsShader(GpuState state, ShaderAddresses addresses)
{
ShaderPack pack = new ShaderPack();
@ -155,48 +193,49 @@ namespace Ryujinx.Graphics.Gpu.Shader
pack.Add(GetShaderCode(addresses.Fragment));
}
GraphicsShader gs = _gpPrograms.Get(pack, out int hash);
Shader gs = _cache.Get(pack, out int hash);
if (gs != null)
{
return gs;
}
gs = new GraphicsShader();
ShaderProgram[] programs = new ShaderProgram[5];
if (addresses.VertexA != 0)
programs[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex);
programs[1] = TranslateGraphicsShader(state, ShaderStage.TessellationControl, addresses.TessControl);
programs[2] = TranslateGraphicsShader(state, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
programs[3] = TranslateGraphicsShader(state, ShaderStage.Geometry, addresses.Geometry);
programs[4] = TranslateGraphicsShader(state, ShaderStage.Fragment, addresses.Fragment);
BackpropQualifiers(programs);
IShader[] shaders = new IShader[programs.Count(x => x != null)];
int index = 0;
for (int stage = 0; stage < programs.Length; stage++)
{
gs.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA);
}
else
{
gs.Shaders[0] = TranslateGraphicsShader(state, ShaderStage.Vertex, addresses.Vertex);
}
ShaderProgram program = programs[stage];
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)
if (program != null)
{
continue;
shaders[index++] = _context.Renderer.CompileShader(program);
}
shaders.Add(_context.Renderer.CompileShader(program));
}
gs.HostProgram = _context.Renderer.CreateProgram(shaders.ToArray());
IProgram hostProgram = _context.Renderer.CreateProgram(shaders);
_gpPrograms.Add(hash, gs, pack);
ShaderProgramInfo[] info = programs.Select(x => x?.Info).ToArray();
gs = new Shader(hostProgram, new ShaderMeta(hostProgram, info));
int insertIndex = _cache.Add(hash, gs, pack);
if (Configuration.Enabled)
{
ShaderCacheFile.Save(Configuration.ShaderPath, info, pack, hostProgram.GetGpuBinary(), hash, insertIndex);
}
return gs;
}
@ -210,9 +249,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="state">Current GPU state</param>
/// <param name="stage">Shader stage</param>
/// <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 ShaderProgram TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa, ulong gpuVaA = 0)
private ShaderProgram TranslateGraphicsShader(GpuState state, ShaderStage stage, ulong gpuVa)
{
if (gpuVa == 0)
{
@ -232,41 +270,49 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorCallbacks callbacks = new TranslatorCallbacks(QueryInfo, PrintLog);
ShaderProgram program;
ReadOnlySpan<byte> code = GetShaderCode(gpuVa);
if (gpuVaA != 0)
_dumper.Dump(code, compute: false, out string fullPath, out string codePath);
ShaderProgram program = Translator.Translate(code, callbacks, DefaultFlags);
if (fullPath != null && codePath != null)
{
// TODO.
program = null;
}
else
{
ReadOnlySpan<byte> code = GetShaderCode(gpuVa);
program = Translator.Translate(code, callbacks, DefaultFlags);
_dumper.Dump(code, compute: false, out string fullPath, out string codePath);
if (fullPath != null && codePath != null)
{
program.Prepend("// " + codePath);
program.Prepend("// " + fullPath);
}
program.Prepend("// " + codePath);
program.Prepend("// " + fullPath);
}
return program;
}
/// <summary>
/// Gets a span of shader code at a given memory address.
/// This takes into account the header of graphics shaders.
/// </summary>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <returns>A span of the shader code</returns>
private ReadOnlySpan<byte> GetShaderCode(ulong gpuVa)
{
return GetShaderCodeImpl(gpuVa, ShaderHeaderSize);
}
/// <summary>
/// Gets a span of shader code at a given memory address.
/// This assumes that the shader is a compute shader and has no header.
/// </summary>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <returns>A span of the shader code</returns>
private ReadOnlySpan<byte> GetShaderCodeHeaderless(ulong gpuVa)
{
return GetShaderCodeImpl(gpuVa, 0);
}
/// <summary>
/// Gets a span of shader code at a given memory address.
/// </summary>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <param name="size">Initial size of the shader code</param>
/// <returns>A span of the shader code</returns>
private ReadOnlySpan<byte> GetShaderCodeImpl(ulong gpuVa, ulong size)
{
while (true)
@ -290,15 +336,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This is required by older versions of OpenGL (pre-4.3).
/// </summary>
/// <param name="gs">Graphics shader cached code</param>
private void BackpropQualifiers(GraphicsShader gs)
private void BackpropQualifiers(ShaderProgram[] programs)
{
ShaderProgram fragmentShader = gs.Shaders[4];
ShaderProgram fragmentShader = programs[4];
bool isFirst = true;
for (int stage = 3; stage >= 0; stage--)
{
if (gs.Shaders[stage] == null)
if (programs[stage] == null)
{
continue;
}
@ -311,11 +357,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (isFirst && !string.IsNullOrEmpty(iq))
{
gs.Shaders[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
programs[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
}
else
{
gs.Shaders[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
programs[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
}
}
@ -429,12 +475,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
public void Dispose()
{
foreach (ComputeShader shader in _cpPrograms)
{
shader.HostProgram.Dispose();
}
foreach (GraphicsShader shader in _gpPrograms)
foreach (Shader shader in _cache)
{
shader.HostProgram.Dispose();
}

View file

@ -0,0 +1,19 @@
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Persistent shader cache configuration manager.
/// </summary>
class ShaderCacheConfiguration
{
/// <summary>
/// True when the persistent shader cache is enabled, false otherwise.
/// </summary>
internal bool Enabled => ShaderPath != null;
/// <summary>
/// Path where the shader cache files should be saved.
/// When set to null, the persistent shader cache is disabled.
/// </summary>
public string ShaderPath { get; set; }
}
}

View file

@ -0,0 +1,77 @@
using Ryujinx.Graphics.Shader;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Persistent shader cache file management.
/// </summary>
static class ShaderCacheFile
{
private const string Extension = "shbin";
/// <summary>
/// Loads all cached shaders from disk.
/// </summary>
/// <param name="basePath">Base path of the shader cache</param>
/// <returns>Shader cache file data array</returns>
public static ShaderCacheFileFormat[] Load(string basePath)
{
string[] files = Directory.GetFiles(basePath, $"*.{Extension}", SearchOption.TopDirectoryOnly);
List<ShaderCacheFileFormat> cached = new List<ShaderCacheFileFormat>();
foreach (string fileName in files)
{
using FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryFormatter formatter = new BinaryFormatter();
ShaderCacheFileFormat scff = (ShaderCacheFileFormat)formatter.Deserialize(fs);
cached.Add(scff);
}
return cached.ToArray();
}
/// <summary>
/// Saves shader code to disk.
/// </summary>
/// <param name="basePath">Base path of the shader cache</param>
/// <param name="info">Array of shader program information for all stages</param>
/// <param name="pack">Pack with spans of guest shader code</param>
/// <param name="code">Host binary shader code</param>
/// <param name="hash">Hash calculated from the guest shader code</param>
/// <param name="index">Index to disambiguate the shader in case of hash collisions</param>
public static void Save(string basePath, ShaderProgramInfo[] info, ShaderPack pack, byte[] code, int hash, int index)
{
ShaderCacheFileFormat scff = new ShaderCacheFileFormat(hash, pack, code, info);
BinaryFormatter formatter = new BinaryFormatter();
string fileName = GetShaderPath(basePath, hash, index);
using FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
formatter.Serialize(fs, scff);
}
/// <summary>
/// Gets the file path for a given shader.
/// </summary>
/// <param name="basePath">Base path of the shader cache</param>
/// <param name="hash">Hash calculated from the guest shader code</param>
/// <param name="index">Index to disambiguate the shader in case of hash collisions</param>
/// <returns>File path</returns>
private static string GetShaderPath(string basePath, int hash, int index)
{
return index != 0
? Path.Combine(basePath, hash.ToString("X8", CultureInfo.InvariantCulture) + "_" + index.ToString(CultureInfo.InvariantCulture) + '.' + Extension)
: Path.Combine(basePath, hash.ToString("X8", CultureInfo.InvariantCulture) + '.' + Extension);
}
}
}

View file

@ -0,0 +1,47 @@
using Ryujinx.Graphics.Shader;
using System;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Shader cache file structure.
/// </summary>
[Serializable]
class ShaderCacheFileFormat
{
/// <summary>
/// Hash of the guest shader code.
/// </summary>
public int Hash { get; }
/// <summary>
/// Guest shader code for all active stages.
/// </summary>
public byte[][] GuestCode { get; }
/// <summary>
/// Host binary shader code.
/// </summary>
public byte[] Code { get; }
/// <summary>
/// Shader program information.
/// </summary>
public ShaderProgramInfo[] Info { get; }
/// <summary>
/// Creates the shader cache file format structure.
/// </summary>
/// <param name="hash">Hash of the guest shader code</param>
/// <param name="pack">Spans of guest shader code</param>
/// <param name="code">Host binary shader code</param>
/// <param name="info">Shader program information</param>
public ShaderCacheFileFormat(int hash, ShaderPack pack, byte[] code, ShaderProgramInfo[] info)
{
Hash = hash;
GuestCode = pack.ToArray();
Code = code;
Info = info;
}
}
}

View file

@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
private string _dumpPath;
private int _dumpIndex;
/// <summary>
/// Creates a new instance of the shader dumper.
/// </summary>
public ShaderDumper()
{
_dumpIndex = 1;

View file

@ -4,20 +4,37 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Map of compiled host shader code, keyed by hash and guest code.
/// </summary>
/// <typeparam name="T">Type of the host shader code</typeparam>
class ShaderMap<T> : IEnumerable<T>
{
private struct ShaderEntry
{
/// <summary>
/// Host shader.
/// </summary>
public T Shader { get; }
private readonly byte[][] _code;
/// <summary>
/// Creates a new shader entry on the shader map.
/// </summary>
/// <param name="shader">Host shader</param>
/// <param name="code">Guest shader code for all active shaders</param>
public ShaderEntry(T shader, byte[][] code)
{
Shader = shader;
_code = code;
}
/// <summary>
/// Checks if the shader code on this entry is equal to the guest shader code in memory.
/// </summary>
/// <param name="pack">Pack of guest shader code</param>
/// <returns>True if the code is equal, false otherwise</returns>
public bool CodeEquals(ShaderPack pack)
{
if (pack.Count != _code.Length)
@ -39,11 +56,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly Dictionary<int, List<ShaderEntry>> _shaders;
/// <summary>
/// Creates a new instance of the shader map.
/// </summary>
public ShaderMap()
{
_shaders = new Dictionary<int, List<ShaderEntry>>();
}
/// <summary>
/// Gets the host shader for the guest shader code,
/// or the default value for the type if not found.
/// </summary>
/// <param name="pack">Pack with spans of guest shader code</param>
/// <param name="hash">Calculated hash of all the shader code on the pack</param>
/// <returns>Host shader, or the default value if not found</returns>
public T Get(ShaderPack pack, out int hash)
{
Fnv1a hasher = new Fnv1a();
@ -73,7 +100,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
return default;
}
public void Add(int hash, T shader, ShaderPack pack)
/// <summary>
/// Adds a new host shader to the shader map.
/// </summary>
/// <param name="hash">Hash of the shader code, returned from the <see cref="Get"/> method</param>
/// <param name="shader">Host shader</param>
/// <param name="pack">Pack with spans of guest shader code</param>
/// <returns>Index to disambiguate the shader in case of hash collisions</returns>
public int Add(int hash, T shader, ShaderPack pack)
{
if (!_shaders.TryGetValue(hash, out List<ShaderEntry> list))
{
@ -89,7 +123,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
code[index] = pack[index].ToArray();
}
int insertIndex = list.Count;
list.Add(new ShaderEntry(shader, code));
return insertIndex;
}
public IEnumerator<T> GetEnumerator()

View file

@ -0,0 +1,177 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Shader meta data.
/// </summary>
class ShaderMeta
{
private const int ShaderStages = 6;
private const int UbStageShift = 5;
private const int SbStageShift = 4;
private const int TexStageShift = 5;
private const int ImgStageShift = 3;
private const int UbsPerStage = 1 << UbStageShift;
private const int SbsPerStage = 1 << SbStageShift;
private const int TexsPerStage = 1 << TexStageShift;
private const int ImgsPerStage = 1 << ImgStageShift;
private readonly int[] _ubBindingPoints;
private readonly int[] _sbBindingPoints;
private readonly int[] _textureUnits;
private readonly int[] _imageUnits;
/// <summary>
/// Shader program information.
/// </summary>
public ShaderProgramInfo[] Info { get; }
/// <summary>
/// Creates a new instace of the shader meta data.
/// </summary>
/// <param name="hostProgram">Host shader program</param>
/// <param name="infos">Shader program information, per stage</param>
public ShaderMeta(IProgram hostProgram, params ShaderProgramInfo[] infos)
{
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
_textureUnits = new int[TexsPerStage * ShaderStages];
_imageUnits = new int[ImgsPerStage * ShaderStages];
for (int index = 0; index < _ubBindingPoints.Length; index++)
{
_ubBindingPoints[index] = -1;
}
for (int index = 0; index < _sbBindingPoints.Length; index++)
{
_sbBindingPoints[index] = -1;
}
for (int index = 0; index < _textureUnits.Length; index++)
{
_textureUnits[index] = -1;
}
for (int index = 0; index < _imageUnits.Length; index++)
{
_imageUnits[index] = -1;
}
int ubBindingPoint = 0;
int sbBindingPoint = 0;
int textureUnit = 0;
int imageUnit = 0;
Info = new ShaderProgramInfo[infos.Length];
for (int index = 0; index < infos.Length; index++)
{
ShaderProgramInfo info = infos[index];
if (info == null)
{
continue;
}
foreach (BufferDescriptor descriptor in info.CBuffers)
{
hostProgram.SetUniformBufferBindingPoint(descriptor.Name, ubBindingPoint);
int bpIndex = (int)info.Stage << UbStageShift | descriptor.Slot;
_ubBindingPoints[bpIndex] = ubBindingPoint;
ubBindingPoint++;
}
foreach (BufferDescriptor descriptor in info.SBuffers)
{
hostProgram.SetStorageBufferBindingPoint(descriptor.Name, sbBindingPoint);
int bpIndex = (int)info.Stage << SbStageShift | descriptor.Slot;
_sbBindingPoints[bpIndex] = sbBindingPoint;
sbBindingPoint++;
}
int samplerIndex = 0;
foreach (TextureDescriptor descriptor in info.Textures)
{
hostProgram.SetImageUnit(descriptor.Name, textureUnit);
int uIndex = (int)info.Stage << TexStageShift | samplerIndex++;
_textureUnits[uIndex] = textureUnit;
textureUnit++;
}
int imageIndex = 0;
foreach (TextureDescriptor descriptor in info.Images)
{
hostProgram.SetImageUnit(descriptor.Name, imageUnit);
int uIndex = (int)info.Stage << ImgStageShift | imageIndex++;
_imageUnits[uIndex] = imageUnit;
imageUnit++;
}
Info[index] = info;
}
}
/// <summary>
/// Gets the uniform buffer binding point for a given shader stage and resource index.
/// </summary>
/// <param name="stage">Shader stage</param>
/// <param name="index">Resource index</param>
/// <returns>Host binding point</returns>
public int GetUniformBufferBindingPoint(ShaderStage stage, int index)
{
return _ubBindingPoints[(int)stage << UbStageShift | index];
}
/// <summary>
/// Gets the storage buffer binding point for a given shader stage and resource index.
/// </summary>
/// <param name="stage">Shader stage</param>
/// <param name="index">Resource index</param>
/// <returns>Host binding point</returns>
public int GetStorageBufferBindingPoint(ShaderStage stage, int index)
{
return _sbBindingPoints[(int)stage << SbStageShift | index];
}
/// <summary>
/// Gets the texture unit for a given shader stage and texture index.
/// </summary>
/// <param name="stage">Shader stage</param>
/// <param name="index">Texture index</param>
/// <returns>Host unit</returns>
public int GetTextureUnit(ShaderStage stage, int index)
{
return _textureUnits[(int)stage << TexStageShift | index];
}
/// <summary>
/// Gets the image unit for a given shader stage and image index.
/// </summary>
/// <param name="stage">Shader stage</param>
/// <param name="index">Image index</param>
/// <returns>Host unit</returns>
public int GetImageUnit(ShaderStage stage, int index)
{
return _imageUnits[(int)stage << ImgStageShift | index];
}
}
}

View file

@ -2,6 +2,9 @@
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Spans of shader code for each active shader stage.
/// </summary>
ref struct ShaderPack
{
private ReadOnlySpan<byte> _code0;
@ -10,8 +13,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ReadOnlySpan<byte> _code3;
private ReadOnlySpan<byte> _code4;
/// <summary>
/// Number of active shader stages.
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Gets code for a given shader stage.
/// </summary>
/// <param name="index">Index of the shader code</param>
/// <returns>Guest shader code</returns>
public ReadOnlySpan<byte> this[int index]
{
get
@ -44,6 +55,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
}
/// <summary>
/// Adds shader code to the pack.
/// This can be used to add code for a shader stage to the pack.
/// </summary>
/// <param name="code">Code to be added</param>
public void Add(ReadOnlySpan<byte> code)
{
if (Count >= 5)
@ -74,5 +90,43 @@ namespace Ryujinx.Graphics.Gpu.Shader
_code4 = code;
}
}
/// <summary>
/// Copies the internal code spans to a new array.
/// </summary>
/// <returns>Arrays of code for all the stages</returns>
public byte[][] ToArray()
{
byte[][] output = new byte[Count][];
int index = 0;
if (Count >= 1)
{
output[index++] = _code0.ToArray();
}
if (Count >= 2)
{
output[index++] = _code1.ToArray();
}
if (Count >= 3)
{
output[index++] = _code2.ToArray();
}
if (Count >= 4)
{
output[index++] = _code3.ToArray();
}
if (Count >= 5)
{
output[index++] = _code4.ToArray();
}
return output;
}
}
}

View file

@ -574,11 +574,9 @@ namespace Ryujinx.Graphics.OpenGL
GL.FrontFace(frontFace.Convert());
}
public void SetImage(int index, ShaderStage stage, ITexture texture)
public void SetImage(int unit, ITexture texture)
{
int unit = _program.GetImageUnit(stage, index);
if (unit != -1 && texture != null)
if (texture != null)
{
TextureView view = (TextureView)texture;
@ -664,11 +662,9 @@ namespace Ryujinx.Graphics.OpenGL
UpdateDepthTest();
}
public void SetSampler(int index, ShaderStage stage, ISampler sampler)
public void SetSampler(int unit, ISampler sampler)
{
int unit = _program.GetTextureUnit(stage, index);
if (unit != -1 && sampler != null)
if (sampler != null)
{
((Sampler)sampler).Bind(unit);
}
@ -716,16 +712,14 @@ namespace Ryujinx.Graphics.OpenGL
_stencilFrontMask = stencilTest.FrontMask;
}
public void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer)
public void SetStorageBuffer(int bindingPoint, BufferRange buffer)
{
SetBuffer(index, stage, buffer, isStorage: true);
SetBuffer(bindingPoint, buffer, isStorage: true);
}
public void SetTexture(int index, ShaderStage stage, ITexture texture)
public void SetTexture(int unit, ITexture texture)
{
int unit = _program.GetTextureUnit(stage, index);
if (unit != -1 && texture != null)
if (texture != null)
{
if (unit == 0)
{
@ -738,9 +732,9 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer)
public void SetUniformBuffer(int bindingPoint, BufferRange buffer)
{
SetBuffer(index, stage, buffer, isStorage: false);
SetBuffer(bindingPoint, buffer, isStorage: false);
}
public void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs)
@ -811,17 +805,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
}
private void SetBuffer(int index, ShaderStage stage, BufferRange buffer, bool isStorage)
private void SetBuffer(int bindingPoint, BufferRange buffer, bool isStorage)
{
int bindingPoint = isStorage
? _program.GetStorageBufferBindingPoint(stage, index)
: _program.GetUniformBufferBindingPoint(stage, index);
if (bindingPoint == -1)
{
return;
}
BufferRangeTarget target = isStorage
? BufferRangeTarget.ShaderStorageBuffer
: BufferRangeTarget.UniformBuffer;

View file

@ -1,62 +1,23 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using System;
using System.Buffers.Binary;
namespace Ryujinx.Graphics.OpenGL
{
class Program : IProgram
{
private const int ShaderStages = 6;
private const int UbStageShift = 5;
private const int SbStageShift = 4;
private const int TexStageShift = 5;
private const int ImgStageShift = 3;
private const int UbsPerStage = 1 << UbStageShift;
private const int SbsPerStage = 1 << SbStageShift;
private const int TexsPerStage = 1 << TexStageShift;
private const int ImgsPerStage = 1 << ImgStageShift;
public int Handle { get; private set; }
public bool IsLinked { get; private set; }
private int[] _ubBindingPoints;
private int[] _sbBindingPoints;
private int[] _textureUnits;
private int[] _imageUnits;
public Program(IShader[] shaders)
{
_ubBindingPoints = new int[UbsPerStage * ShaderStages];
_sbBindingPoints = new int[SbsPerStage * ShaderStages];
_textureUnits = new int[TexsPerStage * ShaderStages];
_imageUnits = new int[ImgsPerStage * ShaderStages];
for (int index = 0; index < _ubBindingPoints.Length; index++)
{
_ubBindingPoints[index] = -1;
}
for (int index = 0; index < _sbBindingPoints.Length; index++)
{
_sbBindingPoints[index] = -1;
}
for (int index = 0; index < _textureUnits.Length; index++)
{
_textureUnits[index] = -1;
}
for (int index = 0; index < _imageUnits.Length; index++)
{
_imageUnits[index] = -1;
}
Handle = GL.CreateProgram();
GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1);
for (int index = 0; index < shaders.Length; index++)
{
int shaderHandle = ((Shader)shaders[index]).Handle;
@ -76,92 +37,64 @@ namespace Ryujinx.Graphics.OpenGL
CheckProgramLink();
Bind();
}
int ubBindingPoint = 0;
int sbBindingPoint = 0;
int textureUnit = 0;
int imageUnit = 0;
public Program(ReadOnlySpan<byte> code)
{
BinaryFormat binFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4));
for (int index = 0; index < shaders.Length; index++)
Handle = GL.CreateProgram();
unsafe
{
Shader shader = (Shader)shaders[index];
foreach (BufferDescriptor descriptor in shader.Info.CBuffers)
fixed (byte* ptr = code)
{
int location = GL.GetUniformBlockIndex(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.UniformBlockBinding(Handle, location, ubBindingPoint);
int bpIndex = (int)shader.Stage << UbStageShift | descriptor.Slot;
_ubBindingPoints[bpIndex] = ubBindingPoint;
ubBindingPoint++;
}
foreach (BufferDescriptor descriptor in shader.Info.SBuffers)
{
int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name);
if (location < 0)
{
continue;
}
GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint);
int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot;
_sbBindingPoints[bpIndex] = sbBindingPoint;
sbBindingPoint++;
}
int samplerIndex = 0;
foreach (TextureDescriptor descriptor in shader.Info.Textures)
{
int location = GL.GetUniformLocation(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.Uniform1(location, textureUnit);
int uIndex = (int)shader.Stage << TexStageShift | samplerIndex++;
_textureUnits[uIndex] = textureUnit;
textureUnit++;
}
int imageIndex = 0;
foreach (TextureDescriptor descriptor in shader.Info.Images)
{
int location = GL.GetUniformLocation(Handle, descriptor.Name);
if (location < 0)
{
continue;
}
GL.Uniform1(location, imageUnit);
int uIndex = (int)shader.Stage << ImgStageShift | imageIndex++;
_imageUnits[uIndex] = imageUnit;
imageUnit++;
GL.ProgramBinary(Handle, binFormat, (IntPtr)ptr, code.Length - 4);
}
}
CheckProgramLink();
Bind();
}
public void SetUniformBufferBindingPoint(string name, int bindingPoint)
{
int location = GL.GetUniformBlockIndex(Handle, name);
if (location < 0)
{
return;
}
GL.UniformBlockBinding(Handle, location, bindingPoint);
}
public void SetStorageBufferBindingPoint(string name, int bindingPoint)
{
int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, name);
if (location < 0)
{
return;
}
GL.ShaderStorageBlockBinding(Handle, location, bindingPoint);
}
public void SetTextureUnit(string name, int unit) => SetTextureOrImageUnit(name, unit);
public void SetImageUnit(string name, int unit) => SetTextureOrImageUnit(name, unit);
public void SetTextureOrImageUnit(string name, int unit)
{
int location = GL.GetUniformLocation(Handle, name);
if (location < 0)
{
return;
}
GL.Uniform1(location, unit);
}
public void Bind()
@ -169,26 +102,6 @@ namespace Ryujinx.Graphics.OpenGL
GL.UseProgram(Handle);
}
public int GetUniformBufferBindingPoint(ShaderStage stage, int index)
{
return _ubBindingPoints[(int)stage << UbStageShift | index];
}
public int GetStorageBufferBindingPoint(ShaderStage stage, int index)
{
return _sbBindingPoints[(int)stage << SbStageShift | index];
}
public int GetTextureUnit(ShaderStage stage, int index)
{
return _textureUnits[(int)stage << TexStageShift | index];
}
public int GetImageUnit(ShaderStage stage, int index)
{
return _imageUnits[(int)stage << ImgStageShift | index];
}
private void CheckProgramLink()
{
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status);
@ -204,6 +117,21 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public byte[] GetGpuBinary()
{
GL.GetProgram(Handle, (GetProgramParameterName)All.ProgramBinaryLength, out int size);
byte[] data = new byte[size + 4];
Span<byte> dataSpan = data;
GL.GetProgramBinary(Handle, size, out _, out BinaryFormat binFormat, data);
BinaryPrimitives.WriteInt32LittleEndian(dataSpan.Slice(size, 4), (int)binFormat);
return data;
}
public void Dispose()
{
if (Handle != 0)

View file

@ -2,6 +2,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using System;
namespace Ryujinx.Graphics.OpenGL
{
@ -45,6 +46,11 @@ namespace Ryujinx.Graphics.OpenGL
return new Program(shaders);
}
public IProgram CreateProgramFromGpuBinary(ReadOnlySpan<byte> data)
{
return new Program(data);
}
public ISampler CreateSampler(SamplerCreateInfo info)
{
return new Sampler(info);

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.OpenGL
public ShaderProgramInfo Info => _program.Info;
public ShaderStage Stage => _program.Stage;
public ShaderStage Stage => Info.Stage;
public Shader(ShaderProgram program)
{
@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.OpenGL
ShaderType type = ShaderType.VertexShader;
switch (program.Stage)
switch (program.Info.Stage)
{
case ShaderStage.Compute: type = ShaderType.ComputeShader; break;
case ShaderStage.Vertex: type = ShaderType.VertexShader; break;

View file

@ -1,5 +1,8 @@
using System;
namespace Ryujinx.Graphics.Shader
{
[Serializable]
public struct BufferDescriptor
{
public string Name { get; }

View file

@ -6,15 +6,12 @@ namespace Ryujinx.Graphics.Shader
{
public ShaderProgramInfo Info { get; }
public ShaderStage Stage { get; }
public string Code { get; private set; }
internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code)
internal ShaderProgram(ShaderProgramInfo info, string code)
{
Info = info;
Stage = stage;
Code = code;
Info = info;
Code = code;
}
public void Prepend(string line)

View file

@ -3,6 +3,7 @@ using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Shader
{
[Serializable]
public class ShaderProgramInfo
{
public ReadOnlyCollection<BufferDescriptor> CBuffers { get; }
@ -12,6 +13,8 @@ namespace Ryujinx.Graphics.Shader
public ReadOnlyCollection<InterpolationQualifier> InterpolationQualifiers { get; }
public ShaderStage Stage { get; }
public bool UsesInstanceId { get; }
internal ShaderProgramInfo(
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.Shader
TextureDescriptor[] textures,
TextureDescriptor[] images,
InterpolationQualifier[] interpolationQualifiers,
ShaderStage stage,
bool usesInstanceId)
{
CBuffers = Array.AsReadOnly(cBuffers);
@ -29,6 +33,8 @@ namespace Ryujinx.Graphics.Shader
InterpolationQualifiers = Array.AsReadOnly(interpolationQualifiers);
Stage = stage;
UsesInstanceId = usesInstanceId;
}
}

View file

@ -1,5 +1,8 @@
using System;
namespace Ryujinx.Graphics.Shader
{
[Serializable]
public struct TextureDescriptor
{
public string Name { get; }

View file

@ -56,9 +56,10 @@ namespace Ryujinx.Graphics.Shader.Translation
program.TextureDescriptors,
program.ImageDescriptors,
sInfo.InterpolationQualifiers,
config.Stage,
sInfo.UsesInstanceId);
return new ShaderProgram(spInfo, config.Stage, program.Code);
return new ShaderProgram(spInfo, program.Code);
}
private static Operation[] DecodeShader(

View file

@ -292,6 +292,13 @@ namespace Ryujinx.Ui
_renderer.Initialize();
// Shader cache setup.
string basePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Ryujinx");
string workPath = System.IO.Path.Combine(basePath, "games", _device.System.TitleIdText, "cache", "gpu");
_device.Gpu.SetShaderCachePath(workPath);
_device.Gpu.LoadShaderCache();
// Make sure the first frame is not transparent.
GL.ClearColor(OpenTK.Color.Black);
GL.Clear(ClearBufferMask.ColorBufferBit);