aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs167
1 files changed, 135 insertions, 32 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 0779bf2c..c998fe09 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -1,13 +1,17 @@
+using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.Shader.Cache;
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Shader
@@ -59,11 +63,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
public readonly CachedShaderProgram CachedProgram;
public readonly IProgram HostProgram;
+ public readonly byte[] BinaryCode;
- public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram)
+ public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram, byte[] binaryCode)
{
CachedProgram = cachedProgram;
HostProgram = hostProgram;
+ BinaryCode = binaryCode;
}
}
@@ -94,9 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_programsToSaveQueue = new Queue<ProgramToSave>();
- string diskCacheTitleId = GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
- ? CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId)
- : null;
+ string diskCacheTitleId = GetDiskCachePath();
_computeShaderCache = new ComputeShaderCacheHashTable();
_graphicsShaderCache = new ShaderCacheHashTable();
@@ -109,6 +113,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Gets the path where the disk cache for the current application is stored.
+ /// </summary>
+ private static string GetDiskCachePath()
+ {
+ return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
+ ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
+ : null;
+ }
+
+ /// <summary>
/// Processes the queue of shaders that must save their binaries to the disk cache.
/// </summary>
public void ProcessShaderCacheQueue()
@@ -123,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (result == ProgramLinkStatus.Success)
{
- _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.HostProgram.GetBinary());
+ _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.BinaryCode ?? programToSave.HostProgram.GetBinary());
}
_programsToSaveQueue.Dequeue();
@@ -143,16 +157,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (_diskCacheHostStorage.CacheEnabled)
{
- if (!_diskCacheHostStorage.CacheExists())
- {
- // If we don't have a shader cache on the new format, try to perform migration from the old shader cache.
- Logger.Info?.Print(LogClass.Gpu, "No shader cache found, trying to migrate from legacy shader cache...");
-
- int migrationCount = Migration.MigrateFromLegacyCache(_context, _diskCacheHostStorage);
-
- Logger.Info?.Print(LogClass.Gpu, $"Migrated {migrationCount} shaders.");
- }
-
ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader(
_context,
_graphicsShaderCache,
@@ -210,26 +214,75 @@ namespace Ryujinx.Graphics.Gpu.Shader
return cpShader;
}
- ShaderSpecializationState specState = new ShaderSpecializationState(computeState);
+ ShaderSpecializationState specState = new ShaderSpecializationState(ref computeState);
GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState);
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
- TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, gpuVa);
+ TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
- IProgram hostProgram = _context.Renderer.CreateProgram(new ShaderSource[] { CreateShaderSource(translatedShader.Program) }, new ShaderInfo(-1));
+ ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
+
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
_computeShaderCache.Add(cpShader);
- EnqueueProgramToSave(new ProgramToSave(cpShader, hostProgram));
+ EnqueueProgramToSave(cpShader, hostProgram, shaderSourcesArray);
_cpPrograms[gpuVa] = cpShader;
return cpShader;
}
/// <summary>
+ /// Updates the shader pipeline state based on the current GPU state.
+ /// </summary>
+ /// <param name="state">Current GPU 3D engine state</param>
+ /// <param name="pipeline">Shader pipeline state to be updated</param>
+ /// <param name="graphicsState">Current graphics state</param>
+ /// <param name="channel">Current GPU channel</param>
+ private void UpdatePipelineInfo(
+ ref ThreedClassState state,
+ ref ProgramPipelineState pipeline,
+ GpuChannelGraphicsState graphicsState,
+ GpuChannel channel)
+ {
+ channel.TextureManager.UpdateRenderTargets();
+
+ var rtControl = state.RtControl;
+ var msaaMode = state.RtMsaaMode;
+
+ pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
+
+ int count = rtControl.UnpackCount();
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ int rtIndex = rtControl.UnpackPermutationIndex(index);
+
+ var colorState = state.RtColorState[rtIndex];
+
+ if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
+ {
+ pipeline.AttachmentEnable[index] = false;
+ pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
+ }
+ else
+ {
+ pipeline.AttachmentEnable[index] = true;
+ pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
+ }
+ }
+
+ pipeline.DepthStencilEnable = state.RtDepthStencilEnable;
+ pipeline.DepthStencilFormat = pipeline.DepthStencilEnable ? state.RtDepthStencilState.Format.Convert().Format : Format.D24UnormS8Uint;
+
+ pipeline.VertexBufferCount = Constants.TotalVertexBuffers;
+ pipeline.Topology = graphicsState.Topology;
+ }
+
+ /// <summary>
/// Gets a graphics shader program from the shader cache.
/// This includes all the specified shader stages.
/// </summary>
@@ -237,6 +290,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This automatically translates, compiles and adds the code to the cache if not present.
/// </remarks>
/// <param name="state">GPU state</param>
+ /// <param name="pipeline">Pipeline state</param>
/// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">3D engine state</param>
@@ -244,6 +298,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Compiled graphics shader code</returns>
public CachedShaderProgram GetGraphicsShader(
ref ThreedClassState state,
+ ref ProgramPipelineState pipeline,
GpuChannel channel,
GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
@@ -262,7 +317,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state);
- ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, transformFeedbackDescriptors);
+ UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel);
+
+ ShaderSpecializationState specState = new ShaderSpecializationState(ref graphicsState, ref pipeline, transformFeedbackDescriptors);
GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors);
ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
@@ -270,6 +327,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
TranslatorContext nextStage = null;
+ TargetApi api = _context.Capabilities.Api;
+
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{
ulong gpuVa = addressesSpan[stageIndex + 1];
@@ -277,7 +336,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (gpuVa != 0)
{
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex);
- TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, gpuVa);
+ TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
if (nextStage != null)
{
@@ -286,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (stageIndex == 0 && addresses.VertexA != 0)
{
- translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
+ translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
}
translatorContexts[stageIndex + 1] = currentStage;
@@ -336,13 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
}
+ ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
+
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
- IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap));
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
_graphicsShaderCache.Add(gpShaders);
- EnqueueProgramToSave(new ProgramToSave(gpShaders, hostProgram));
+ EnqueueProgramToSave(gpShaders, hostProgram, shaderSourcesArray);
_gpPrograms[addresses] = gpShaders;
return gpShaders;
@@ -355,7 +416,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Shader source</returns>
public static ShaderSource CreateShaderSource(ShaderProgram program)
{
- return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
+ return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
}
/// <summary>
@@ -364,11 +425,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <remarks>
/// This will not do anything if disk shader cache is disabled.
/// </remarks>
- /// <param name="programToSave">Program to be saved on disk</param>
- private void EnqueueProgramToSave(ProgramToSave programToSave)
+ /// <param name="program">Cached shader program</param>
+ /// <param name="hostProgram">Host program</param>
+ /// <param name="sources">Source for each shader stage</param>
+ private void EnqueueProgramToSave(CachedShaderProgram program, IProgram hostProgram, ShaderSource[] sources)
{
if (_diskCacheHostStorage.CacheEnabled)
{
+ byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(sources) : null;
+ ProgramToSave programToSave = new ProgramToSave(program, hostProgram, binaryCode);
+
_programsToSaveQueue.Enqueue(programToSave);
}
}
@@ -480,11 +546,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Decode the binary Maxwell shader code to a translator context.
/// </summary>
/// <param name="gpuAccessor">GPU state accessor</param>
+ /// <param name="api">Graphics API that will be used with the shader</param>
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
/// <returns>The generated translator context</returns>
- public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, ulong gpuVa)
+ public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
{
- var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute);
+ var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -495,12 +562,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
/// </remarks>
/// <param name="gpuAccessor">GPU state accessor</param>
+ /// <param name="api">Graphics API that will be used with the shader</param>
/// <param name="flags">Flags that controls shader translation</param>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <returns>The generated translator context</returns>
- public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TranslationFlags flags, ulong gpuVa)
+ public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
{
- var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
+ var options = CreateTranslationOptions(api, flags);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -596,6 +664,41 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <summary>
+ /// Gets information about the bindings used by a shader program.
+ /// </summary>
+ /// <param name="info">Shader program information to get the information from</param>
+ /// <returns>Shader bindings</returns>
+ public static ShaderBindings GetBindings(ShaderProgramInfo info)
+ {
+ var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
+ var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
+ var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
+ var imageBindings = info.Images.Select(x => x.Binding).ToArray();
+
+ return new ShaderBindings(
+ uniformBufferBindings,
+ storageBufferBindings,
+ textureBindings,
+ imageBindings);
+ }
+
+ /// <summary>
+ /// Creates shader translation options with the requested graphics API and flags.
+ /// The shader language is choosen based on the current configuration and graphics API.
+ /// </summary>
+ /// <param name="api">Target graphics API</param>
+ /// <param name="flags">Translation flags</param>
+ /// <returns>Translation options</returns>
+ private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
+ {
+ TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
+ ? TargetLanguage.Spirv
+ : TargetLanguage.Glsl;
+
+ return new TranslationOptions(lang, api, flags);
+ }
+
+ /// <summary>
/// Disposes the shader cache, deleting all the cached shaders.
/// It's an error to use the shader cache after disposal.
/// </summary>