Implement persistent shader cache
This commit is contained in:
parent
d0febd50e8
commit
00739fdac4
30 changed files with 749 additions and 336 deletions
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
19
Ryujinx.Graphics.Gpu/Shader/ShaderCacheConfiguration.cs
Normal file
19
Ryujinx.Graphics.Gpu/Shader/ShaderCacheConfiguration.cs
Normal 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; }
|
||||
}
|
||||
}
|
77
Ryujinx.Graphics.Gpu/Shader/ShaderCacheFile.cs
Normal file
77
Ryujinx.Graphics.Gpu/Shader/ShaderCacheFile.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
47
Ryujinx.Graphics.Gpu/Shader/ShaderCacheFileFormat.cs
Normal file
47
Ryujinx.Graphics.Gpu/Shader/ShaderCacheFileFormat.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
177
Ryujinx.Graphics.Gpu/Shader/ShaderMeta.cs
Normal file
177
Ryujinx.Graphics.Gpu/Shader/ShaderMeta.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
[Serializable]
|
||||
public struct BufferDescriptor
|
||||
{
|
||||
public string Name { get; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
[Serializable]
|
||||
public struct TextureDescriptor
|
||||
{
|
||||
public string Name { get; }
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue