aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs9
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs13
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs17
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs6
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs34
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs260
6 files changed, 279 insertions, 60 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index b4764d57..9f263e9d 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -368,12 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (hostCode != null)
{
- bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
- int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
-
- ShaderInfo shaderInfo = specState.PipelineState.HasValue
- ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
- : new ShaderInfo(fragmentOutputMap, fromCache: true);
+ ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState);
IProgram hostProgram;
@@ -385,6 +380,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
else
{
+ bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
+
hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
}
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
index 58e5c7b1..d80518b1 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
@@ -491,23 +491,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
- int fragmentOutputMap = -1;
+ ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context);
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
{
ShaderProgram shader = compilation.TranslatedStages[index];
shaderSources[index] = CreateShaderSource(shader);
-
- if (shader.Info.Stage == ShaderStage.Fragment)
- {
- fragmentOutputMap = shader.Info.FragmentOutputMap;
- }
+ shaderInfoBuilder.AddStageInfo(shader.Info);
}
- ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue
- ? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true)
- : new ShaderInfo(fragmentOutputMap, fromCache: true);
-
+ ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true);
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
index 77e52667..2dc5c971 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
@@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
int binaryCodeLength = reader.ReadInt32();
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
- output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
+ output.Add(new ShaderSource(binaryCode, stage, TargetLanguage.Spirv));
}
return output.ToArray();
}
-
- private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
- {
- for (int i = 0; i < stages.Length; i++)
- {
- CachedShaderStage currentStage = stages[i];
-
- if (currentStage?.Info != null && currentStage.Info.Stage == stage)
- {
- return ShaderCache.GetBindings(currentStage.Info);
- }
- }
-
- return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
- }
}
} \ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
index 7db627ba..e4e1e0d6 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs
@@ -110,16 +110,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
}
- return GetStageIndex() * (int)maxPerStage + index;
+ return GetStageIndex(_stageIndex) * (int)maxPerStage + index;
}
- private int GetStageIndex()
+ public static int GetStageIndex(int stageIndex)
{
// This is just a simple remapping to ensure that most frequently used shader stages
// have the lowest binding numbers.
// This is useful because if we need to run on a system with a low limit on the bindings,
// then we can still get most games working as the most common shaders will have low binding numbers.
- return _stageIndex switch
+ return stageIndex switch
{
4 => 1, // Fragment
3 => 2, // Geometry
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index d543f42a..b7ba159a 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
-
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
-
- IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
+ ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
@@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext previousStage = null;
+ ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
+
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
{
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
@@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (program != null)
{
shaderSources.Add(CreateShaderSource(program));
+ infoBuilder.AddStageInfo(program.Info);
}
previousStage = currentStage;
@@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
- int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
- IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
+ ShaderInfo info = infoBuilder.Build(pipeline);
+
+ IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
@@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Shader source</returns>
public static ShaderSource CreateShaderSource(ShaderProgram program)
{
- return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
+ return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
}
/// <summary>
@@ -718,25 +721,6 @@ 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>
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
new file mode 100644
index 00000000..39b31cf6
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
@@ -0,0 +1,260 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Shader;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gpu.Shader
+{
+ /// <summary>
+ /// Shader info structure builder.
+ /// </summary>
+ class ShaderInfoBuilder
+ {
+ private const int TotalSets = 4;
+
+ private const int UniformSetIndex = 0;
+ private const int StorageSetIndex = 1;
+ private const int TextureSetIndex = 2;
+ private const int ImageSetIndex = 3;
+
+ private const ResourceStages SupportBufferStags =
+ ResourceStages.Compute |
+ ResourceStages.Vertex |
+ ResourceStages.Fragment;
+
+ private readonly GpuContext _context;
+
+ private int _fragmentOutputMap;
+
+ private readonly List<ResourceDescriptor>[] _resourceDescriptors;
+ private readonly List<ResourceUsage>[] _resourceUsages;
+
+ /// <summary>
+ /// Creates a new shader info builder.
+ /// </summary>
+ /// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
+ public ShaderInfoBuilder(GpuContext context)
+ {
+ _context = context;
+
+ _fragmentOutputMap = -1;
+
+ _resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
+ _resourceUsages = new List<ResourceUsage>[TotalSets];
+
+ for (int index = 0; index < TotalSets; index++)
+ {
+ _resourceDescriptors[index] = new();
+ _resourceUsages[index] = new();
+ }
+
+ AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
+ }
+
+ /// <summary>
+ /// Adds information from a given shader stage.
+ /// </summary>
+ /// <param name="info">Shader stage information</param>
+ public void AddStageInfo(ShaderProgramInfo info)
+ {
+ if (info.Stage == ShaderStage.Fragment)
+ {
+ _fragmentOutputMap = info.FragmentOutputMap;
+ }
+
+ int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
+ {
+ ShaderStage.TessellationControl => 1,
+ ShaderStage.TessellationEvaluation => 2,
+ ShaderStage.Geometry => 3,
+ ShaderStage.Fragment => 4,
+ _ => 0
+ });
+
+ ResourceStages stages = info.Stage switch
+ {
+ ShaderStage.Compute => ResourceStages.Compute,
+ ShaderStage.Vertex => ResourceStages.Vertex,
+ ShaderStage.TessellationControl => ResourceStages.TessellationControl,
+ ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
+ ShaderStage.Geometry => ResourceStages.Geometry,
+ ShaderStage.Fragment => ResourceStages.Fragment,
+ _ => ResourceStages.None
+ };
+
+ int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
+ int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
+ int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
+ int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
+
+ int uniformBinding = 1 + stageIndex * uniformsPerStage;
+ int storageBinding = stageIndex * storagesPerStage;
+ int textureBinding = stageIndex * texturesPerStage * 2;
+ int imageBinding = stageIndex * imagesPerStage * 2;
+
+ AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
+ AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
+ AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
+ AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
+
+ AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
+ AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
+ AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
+ AddUsage(info.Images, stages, ImageSetIndex, isImage: true);
+ }
+
+ /// <summary>
+ /// Adds a resource descriptor to the list of descriptors.
+ /// </summary>
+ /// <param name="stages">Shader stages where the resource is used</param>
+ /// <param name="type">Type of the resource</param>
+ /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
+ /// <param name="binding">Binding number where the resource will be bound</param>
+ /// <param name="count">Number of resources bound at the binding location</param>
+ private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
+ {
+ for (int index = 0; index < count; index++)
+ {
+ _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
+ }
+ }
+
+ /// <summary>
+ /// Adds two interleaved groups of resources to the list of descriptors.
+ /// </summary>
+ /// <param name="stages">Shader stages where the resource is used</param>
+ /// <param name="type">Type of the first interleaved resource</param>
+ /// <param name="type2">Type of the second interleaved resource</param>
+ /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
+ /// <param name="binding">Binding number where the resource will be bound</param>
+ /// <param name="count">Number of resources bound at the binding location</param>
+ private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
+ {
+ AddDescriptor(stages, type, setIndex, binding, count);
+ AddDescriptor(stages, type2, setIndex, binding + count, count);
+ }
+
+ /// <summary>
+ /// Adds an array resource to the list of descriptors.
+ /// </summary>
+ /// <param name="stages">Shader stages where the resource is used</param>
+ /// <param name="type">Type of the resource</param>
+ /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
+ /// <param name="binding">Binding number where the resource will be bound</param>
+ /// <param name="count">Number of resources bound at the binding location</param>
+ private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
+ {
+ _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
+ }
+
+ /// <summary>
+ /// Adds buffer usage information to the list of usages.
+ /// </summary>
+ /// <param name="buffers">Buffers to be added</param>
+ /// <param name="stages">Stages where the buffers are used</param>
+ /// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
+ /// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
+ private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage)
+ {
+ foreach (BufferDescriptor buffer in buffers)
+ {
+ _resourceUsages[setIndex].Add(new ResourceUsage(
+ buffer.Binding,
+ isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
+ stages,
+ buffer.Flags.HasFlag(BufferUsageFlags.Write) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
+ }
+ }
+
+ /// <summary>
+ /// Adds texture usage information to the list of usages.
+ /// </summary>
+ /// <param name="textures">Textures to be added</param>
+ /// <param name="stages">Stages where the textures are used</param>
+ /// <param name="setIndex">Descriptor set index where the textures will be bound</param>
+ /// <param name="isImage">True for images, false for textures</param>
+ private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
+ {
+ foreach (TextureDescriptor texture in textures)
+ {
+ bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
+
+ ResourceType type = isBuffer
+ ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
+ : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
+
+ _resourceUsages[setIndex].Add(new ResourceUsage(
+ texture.Binding,
+ type,
+ stages,
+ texture.Flags.HasFlag(TextureUsageFlags.ImageStore) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
+ }
+ }
+
+ /// <summary>
+ /// Creates a new shader information structure from the added information.
+ /// </summary>
+ /// <param name="pipeline">Optional pipeline state for background shader compilation</param>
+ /// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
+ /// <returns>Shader information</returns>
+ public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
+ {
+ var descriptors = new ResourceDescriptorCollection[TotalSets];
+ var usages = new ResourceUsageCollection[TotalSets];
+
+ for (int index = 0; index < TotalSets; index++)
+ {
+ descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
+ usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
+ }
+
+ ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
+
+ if (pipeline.HasValue)
+ {
+ return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
+ }
+ else
+ {
+ return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
+ }
+ }
+
+ /// <summary>
+ /// Builds shader information for shaders from the disk cache.
+ /// </summary>
+ /// <param name="context">GPU context that owns the shaders</param>
+ /// <param name="programs">Shaders from the disk cache</param>
+ /// <param name="pipeline">Optional pipeline for background compilation</param>
+ /// <returns>Shader information</returns>
+ public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline)
+ {
+ ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
+
+ foreach (CachedShaderStage program in programs)
+ {
+ if (program?.Info != null)
+ {
+ builder.AddStageInfo(program.Info);
+ }
+ }
+
+ return builder.Build(pipeline, fromCache: true);
+ }
+
+ /// <summary>
+ /// Builds shader information for a compute shader.
+ /// </summary>
+ /// <param name="context">GPU context that owns the shader</param>
+ /// <param name="info">Compute shader information</param>
+ /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
+ /// <returns>Shader information</returns>
+ public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
+ {
+ ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
+
+ builder.AddStageInfo(info);
+
+ return builder.Build(null, fromCache);
+ }
+ }
+} \ No newline at end of file