aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2023-05-21 14:04:21 -0300
committerGitHub <noreply@github.com>2023-05-21 14:04:21 -0300
commit5626f2ca1c49342b20772224f956147df6957b5a (patch)
tree9e60d080754e3686d75cc8606db5967126d2c1b9
parent402f05b8ef013807997589ecc0a8ff50267dcd23 (diff)
Replace ShaderBindings with new ResourceLayout structure for Vulkan (#5025)1.1.812
* Introduce ResourceLayout * Part 1: Use new ResourceSegments array on UpdateAndBind * Part 2: Use ResourceLayout to build PipelineLayout * Delete old code * XML docs * Fix shader cache load NRE * Fix typo
-rw-r--r--src/Ryujinx.Graphics.GAL/ResourceLayout.cs179
-rw-r--r--src/Ryujinx.Graphics.GAL/ShaderBindings.cs24
-rw-r--r--src/Ryujinx.Graphics.GAL/ShaderInfo.cs7
-rw-r--r--src/Ryujinx.Graphics.GAL/ShaderSource.cs8
-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
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs215
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs32
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs13
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs46
-rw-r--r--src/Ryujinx.Graphics.Vulkan/EnumConversion.cs52
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HelperShader.cs198
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs97
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs26
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs271
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs22
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs67
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Shader.cs3
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs106
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs13
24 files changed, 1044 insertions, 674 deletions
diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
new file mode 100644
index 00000000..3cde281f
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.ObjectModel;
+
+namespace Ryujinx.Graphics.GAL
+{
+ public enum ResourceType : byte
+ {
+ UniformBuffer,
+ StorageBuffer,
+ Texture,
+ Sampler,
+ TextureAndSampler,
+ Image,
+ BufferTexture,
+ BufferImage
+ }
+
+ public enum ResourceAccess : byte
+ {
+ None = 0,
+ Read = 1,
+ Write = 2,
+ ReadWrite = Read | Write
+ }
+
+ [Flags]
+ public enum ResourceStages : byte
+ {
+ None = 0,
+ Compute = 1 << 0,
+ Vertex = 1 << 1,
+ TessellationControl = 1 << 2,
+ TessellationEvaluation = 1 << 3,
+ Geometry = 1 << 4,
+ Fragment = 1 << 5
+ }
+
+ public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor>
+ {
+ public int Binding { get; }
+ public int Count { get; }
+ public ResourceType Type { get; }
+ public ResourceStages Stages { get; }
+
+ public ResourceDescriptor(int binding, int count, ResourceType type, ResourceStages stages)
+ {
+ Binding = binding;
+ Count = count;
+ Type = type;
+ Stages = stages;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Binding, Count, Type, Stages);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ResourceDescriptor other && Equals(other);
+ }
+
+ public bool Equals(ResourceDescriptor other)
+ {
+ return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages;
+ }
+ }
+
+ public readonly struct ResourceUsage : IEquatable<ResourceUsage>
+ {
+ public int Binding { get; }
+ public ResourceType Type { get; }
+ public ResourceStages Stages { get; }
+ public ResourceAccess Access { get; }
+
+ public ResourceUsage(int binding, ResourceType type, ResourceStages stages, ResourceAccess access)
+ {
+ Binding = binding;
+ Type = type;
+ Stages = stages;
+ Access = access;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Binding, Type, Stages, Access);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ResourceUsage other && Equals(other);
+ }
+
+ public bool Equals(ResourceUsage other)
+ {
+ return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access;
+ }
+ }
+
+ public readonly struct ResourceDescriptorCollection
+ {
+ public ReadOnlyCollection<ResourceDescriptor> Descriptors { get; }
+
+ public ResourceDescriptorCollection(ReadOnlyCollection<ResourceDescriptor> descriptors)
+ {
+ Descriptors = descriptors;
+ }
+
+ public override int GetHashCode()
+ {
+ HashCode hasher = new HashCode();
+
+ if (Descriptors != null)
+ {
+ foreach (var descriptor in Descriptors)
+ {
+ hasher.Add(descriptor);
+ }
+ }
+
+ return hasher.ToHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is ResourceDescriptorCollection other && Equals(other);
+ }
+
+ public bool Equals(ResourceDescriptorCollection other)
+ {
+ if ((Descriptors == null) != (other.Descriptors == null))
+ {
+ return false;
+ }
+
+ if (Descriptors != null)
+ {
+ if (Descriptors.Count != other.Descriptors.Count)
+ {
+ return false;
+ }
+
+ for (int index = 0; index < Descriptors.Count; index++)
+ {
+ if (!Descriptors[index].Equals(other.Descriptors[index]))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ public readonly struct ResourceUsageCollection
+ {
+ public ReadOnlyCollection<ResourceUsage> Usages { get; }
+
+ public ResourceUsageCollection(ReadOnlyCollection<ResourceUsage> usages)
+ {
+ Usages = usages;
+ }
+ }
+
+ public readonly struct ResourceLayout
+ {
+ public ReadOnlyCollection<ResourceDescriptorCollection> Sets { get; }
+ public ReadOnlyCollection<ResourceUsageCollection> SetUsages { get; }
+
+ public ResourceLayout(
+ ReadOnlyCollection<ResourceDescriptorCollection> sets,
+ ReadOnlyCollection<ResourceUsageCollection> setUsages)
+ {
+ Sets = sets;
+ SetUsages = setUsages;
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/ShaderBindings.cs b/src/Ryujinx.Graphics.GAL/ShaderBindings.cs
deleted file mode 100644
index 6ab29382..00000000
--- a/src/Ryujinx.Graphics.GAL/ShaderBindings.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.GAL
-{
- public readonly struct ShaderBindings
- {
- public IReadOnlyCollection<int> UniformBufferBindings { get; }
- public IReadOnlyCollection<int> StorageBufferBindings { get; }
- public IReadOnlyCollection<int> TextureBindings { get; }
- public IReadOnlyCollection<int> ImageBindings { get; }
-
- public ShaderBindings(
- IReadOnlyCollection<int> uniformBufferBindings,
- IReadOnlyCollection<int> storageBufferBindings,
- IReadOnlyCollection<int> textureBindings,
- IReadOnlyCollection<int> imageBindings)
- {
- UniformBufferBindings = uniformBufferBindings;
- StorageBufferBindings = storageBufferBindings;
- TextureBindings = textureBindings;
- ImageBindings = imageBindings;
- }
- }
-}
diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs
index b4c87117..643f1bc5 100644
--- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs
+++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs
@@ -3,19 +3,22 @@ namespace Ryujinx.Graphics.GAL
public struct ShaderInfo
{
public int FragmentOutputMap { get; }
+ public ResourceLayout ResourceLayout { get; }
public ProgramPipelineState? State { get; }
public bool FromCache { get; set; }
- public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state, bool fromCache = false)
+ public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
+ ResourceLayout = resourceLayout;
State = state;
FromCache = fromCache;
}
- public ShaderInfo(int fragmentOutputMap, bool fromCache = false)
+ public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
+ ResourceLayout = resourceLayout;
State = null;
FromCache = fromCache;
}
diff --git a/src/Ryujinx.Graphics.GAL/ShaderSource.cs b/src/Ryujinx.Graphics.GAL/ShaderSource.cs
index 91d3a632..773c0a8a 100644
--- a/src/Ryujinx.Graphics.GAL/ShaderSource.cs
+++ b/src/Ryujinx.Graphics.GAL/ShaderSource.cs
@@ -7,24 +7,22 @@ namespace Ryujinx.Graphics.GAL
{
public string Code { get; }
public byte[] BinaryCode { get; }
- public ShaderBindings Bindings { get; }
public ShaderStage Stage { get; }
public TargetLanguage Language { get; }
- public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language)
+ public ShaderSource(string code, byte[] binaryCode, ShaderStage stage, TargetLanguage language)
{
Code = code;
BinaryCode = binaryCode;
- Bindings = bindings;
Stage = stage;
Language = language;
}
- public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language)
+ public ShaderSource(string code, ShaderStage stage, TargetLanguage language) : this(code, null, stage, language)
{
}
- public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language)
+ public ShaderSource(byte[] binaryCode, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, stage, language)
{
}
}
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
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index a47ea5ff..f3ac36e1 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -372,8 +372,9 @@ namespace Ryujinx.Graphics.Vulkan
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
{
var program = _program;
- int stagesCount = program.Bindings[setIndex].Length;
- if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex)
+ var bindingSegments = program.BindingSegments[setIndex];
+
+ if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex)
{
return;
}
@@ -410,125 +411,113 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
+ foreach (ResourceBindingSegment segment in bindingSegments)
{
- var stageBindings = program.Bindings[setIndex][stageIndex];
- int bindingsCount = stageBindings.Length;
- int count;
+ int binding = segment.Binding;
+ int count = segment.Count;
- for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
+ if (setIndex == PipelineBase.UniformSetIndex)
{
- int binding = stageBindings[bindingIndex];
- count = 1;
-
- while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
+ for (int i = 0; i < count; i++)
{
- count++;
- }
+ int index = binding + i;
- if (setIndex == PipelineBase.UniformSetIndex)
- {
- for (int i = 0; i < count; i++)
+ if (!_uniformSet[index])
{
- int index = binding + i;
-
- if (!_uniformSet[index])
- {
- UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
+ UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
- _uniformSet[index] = true;
- }
+ _uniformSet[index] = true;
}
-
- ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
- dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
}
- else if (setIndex == PipelineBase.StorageSetIndex)
+
+ ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
+ dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
+ }
+ else if (setIndex == PipelineBase.StorageSetIndex)
+ {
+ for (int i = 0; i < count; i++)
{
- for (int i = 0; i < count; i++)
- {
- int index = binding + i;
+ int index = binding + i;
- if (!_storageSet[index])
- {
- UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
+ if (!_storageSet[index])
+ {
+ UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
- _storageSet[index] = true;
- }
+ _storageSet[index] = true;
}
+ }
- ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
- if (program.HasMinimalLayout)
- {
- dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
- }
- else
- {
- dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
- }
+ ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
+ if (program.HasMinimalLayout)
+ {
+ dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
}
- else if (setIndex == PipelineBase.TextureSetIndex)
+ else
{
- if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)
- {
- Span<DescriptorImageInfo> textures = _textures;
-
- for (int i = 0; i < count; i++)
- {
- ref var texture = ref textures[i];
+ dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
+ }
+ }
+ else if (setIndex == PipelineBase.TextureSetIndex)
+ {
+ if (segment.Type != ResourceType.BufferTexture)
+ {
+ Span<DescriptorImageInfo> textures = _textures;
- texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
- texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
+ for (int i = 0; i < count; i++)
+ {
+ ref var texture = ref textures[i];
- if (texture.ImageView.Handle == 0)
- {
- texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
- }
+ texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
+ texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
- if (texture.Sampler.Handle == 0)
- {
- texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
- }
+ if (texture.ImageView.Handle == 0)
+ {
+ texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
}
- dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
- }
- else
- {
- Span<BufferView> bufferTextures = _bufferTextures;
-
- for (int i = 0; i < count; i++)
+ if (texture.Sampler.Handle == 0)
{
- bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
+ texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
}
-
- dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
}
+
+ dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
}
- else if (setIndex == PipelineBase.ImageSetIndex)
+ else
{
- if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout)
+ Span<BufferView> bufferTextures = _bufferTextures;
+
+ for (int i = 0; i < count; i++)
{
- Span<DescriptorImageInfo> images = _images;
+ bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
+ }
- for (int i = 0; i < count; i++)
- {
- images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
- }
+ dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
+ }
+ }
+ else if (setIndex == PipelineBase.ImageSetIndex)
+ {
+ if (segment.Type != ResourceType.BufferImage)
+ {
+ Span<DescriptorImageInfo> images = _images;
- dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
- }
- else
+ for (int i = 0; i < count; i++)
{
- Span<BufferView> bufferImages = _bufferImages;
+ images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
+ }
- for (int i = 0; i < count; i++)
- {
- bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
- }
+ dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
+ }
+ else
+ {
+ Span<BufferView> bufferImages = _bufferImages;
- dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
+ for (int i = 0; i < count; i++)
+ {
+ bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
}
+
+ dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
}
}
}
@@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
{
- var dummyBuffer = _dummyBuffer?.GetBuffer();
- int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length;
-
if (!_uniformSet[0])
{
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
@@ -587,41 +573,32 @@ namespace Ryujinx.Graphics.Vulkan
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
}
- for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
+ var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
+ var dummyBuffer = _dummyBuffer?.GetBuffer();
+
+ foreach (ResourceBindingSegment segment in bindingSegments)
{
- var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex];
- int bindingsCount = stageBindings.Length;
- int count;
+ int binding = segment.Binding;
+ int count = segment.Count;
+
+ bool doUpdate = false;
- for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
+ for (int i = 0; i < count; i++)
{
- int binding = stageBindings[bindingIndex];
- count = 1;
+ int index = binding + i;
- while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
+ if (!_uniformSet[index])
{
- count++;
- }
-
- bool doUpdate = false;
-
- for (int i = 0; i < count; i++)
- {
- int index = binding + i;
-
- if (!_uniformSet[index])
- {
- UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
- _uniformSet[index] = true;
- doUpdate = true;
- }
+ UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
+ _uniformSet[index] = true;
+ doUpdate = true;
}
+ }
- if (doUpdate)
- {
- ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
- UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
- }
+ if (doUpdate)
+ {
+ ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
+ UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
}
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
index 5f15f15f..e9952126 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
@@ -54,29 +54,29 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
- var computeBindings = new ShaderBindings(
- new[] { 2 },
- Array.Empty<int>(),
- new[] { 1 },
- new[] { 0 });
-
- var sharpeningBindings = new ShaderBindings(
- new[] { 2, 3, 4 },
- Array.Empty<int>(),
- new[] { 1 },
- new[] { 0 });
+ var scalingResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+
+ var sharpeningResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- });
+ new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, scalingResourceLayout);
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- });
+ new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, sharpeningResourceLayout);
}
public void Run(
@@ -160,10 +160,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.ComputeBarrier();
// Sharpening pass
- _pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(_sharpeningProgram);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
- _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
_pipeline.SetImage(0, destinationTexture);
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
index b7316d85..9da003dd 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
@@ -38,18 +38,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
- var computeBindings = new ShaderBindings(
- new[] { 2 },
- Array.Empty<int>(),
- new[] { 1 },
- new[] { 0 });
+ var resourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- });
+ new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, resourceLayout);
}
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index 38f86bae..0d392a65 100644
--- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
@@ -77,23 +77,23 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
- var edgeBindings = new ShaderBindings(
- new[] { 2 },
- Array.Empty<int>(),
- new[] { 1 },
- new[] { 0 });
-
- var blendBindings = new ShaderBindings(
- new[] { 2 },
- Array.Empty<int>(),
- new[] { 1, 3, 4 },
- new[] { 0 });
-
- var neighbourBindings = new ShaderBindings(
- new[] { 2 },
- Array.Empty<int>(),
- new[] { 1, 3 },
- new[] { 0 });
+ var edgeResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+
+ var blendResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
+
+ var neighbourResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
@@ -117,18 +117,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- }, new[] { specInfo });
+ new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, edgeResourceLayout, new[] { specInfo });
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- }, new[] { specInfo });
+ new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, blendResourceLayout, new[] { specInfo });
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv)
- }, new[] { specInfo });
+ new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv)
+ }, neighbourResourceLayout, new[] { specInfo });
}
public void DeletePipelines()
diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
index 1f03b68c..55868ee3 100644
--- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
+++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
@@ -36,6 +36,56 @@ namespace Ryujinx.Graphics.Vulkan
};
}
+ public static ShaderStageFlags Convert(this ResourceStages stages)
+ {
+ ShaderStageFlags stageFlags = stages.HasFlag(ResourceStages.Compute)
+ ? ShaderStageFlags.ComputeBit
+ : ShaderStageFlags.None;
+
+ if (stages.HasFlag(ResourceStages.Vertex))
+ {
+ stageFlags |= ShaderStageFlags.VertexBit;
+ }
+
+ if (stages.HasFlag(ResourceStages.TessellationControl))
+ {
+ stageFlags |= ShaderStageFlags.TessellationControlBit;
+ }
+
+ if (stages.HasFlag(ResourceStages.TessellationEvaluation))
+ {
+ stageFlags |= ShaderStageFlags.TessellationEvaluationBit;
+ }
+
+ if (stages.HasFlag(ResourceStages.Geometry))
+ {
+ stageFlags |= ShaderStageFlags.GeometryBit;
+ }
+
+ if (stages.HasFlag(ResourceStages.Fragment))
+ {
+ stageFlags |= ShaderStageFlags.FragmentBit;
+ }
+
+ return stageFlags;
+ }
+
+ public static DescriptorType Convert(this ResourceType type)
+ {
+ return type switch
+ {
+ ResourceType.UniformBuffer => DescriptorType.UniformBuffer,
+ ResourceType.StorageBuffer => DescriptorType.StorageBuffer,
+ ResourceType.Texture => DescriptorType.SampledImage,
+ ResourceType.Sampler => DescriptorType.Sampler,
+ ResourceType.TextureAndSampler => DescriptorType.CombinedImageSampler,
+ ResourceType.Image => DescriptorType.StorageImage,
+ ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer,
+ ResourceType.BufferImage => DescriptorType.StorageTexelBuffer,
+ _ => throw new ArgumentException($"Invalid resource type \"{type}\".")
+ };
+ }
+
public static SamplerAddressMode Convert(this AddressMode mode)
{
return mode switch
@@ -48,7 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
- _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
+ _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
};
}
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
index c064696d..43214be4 100644
--- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
- var blitVertexBindings = new ShaderBindings(
- new[] { 1 },
- Array.Empty<int>(),
- Array.Empty<int>(),
- Array.Empty<int>());
-
- var blitFragmentBindings = new ShaderBindings(
- Array.Empty<int>(),
- Array.Empty<int>(),
- new[] { 0 },
- Array.Empty<int>());
+ var blitResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
+ .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
- var colorClearFragmentBindings = new ShaderBindings(
- Array.Empty<int>(),
- Array.Empty<int>(),
- Array.Empty<int>(),
- Array.Empty<int>());
+ var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorClearResourceLayout);
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorClearResourceLayout);
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorClearResourceLayout);
- var strideChangeBindings = new ShaderBindings(
- new[] { 0 },
- new[] { 1, 2 },
- Array.Empty<int>(),
- Array.Empty<int>());
+ var strideChangeResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, strideChangeResourceLayout);
- var colorCopyBindings = new ShaderBindings(
- new[] { 0 },
- Array.Empty<int>(),
- new[] { 0 },
- new[] { 0 });
+ var colorCopyResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
+ .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, colorCopyResourceLayout);
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, colorCopyResourceLayout);
_programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, colorCopyResourceLayout);
- var colorDrawToMsVertexBindings = new ShaderBindings(
- Array.Empty<int>(),
- Array.Empty<int>(),
- Array.Empty<int>(),
- Array.Empty<int>());
-
- var colorDrawToMsFragmentBindings = new ShaderBindings(
- new[] { 0 },
- Array.Empty<int>(),
- new[] { 0 },
- Array.Empty<int>());
+ var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorDrawToMsResourceLayout);
- var convertD32S8ToD24S8Bindings = new ShaderBindings(
- new[] { 0 },
- new[] { 1, 2 },
- Array.Empty<int>(),
- Array.Empty<int>());
+ var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, convertD32S8ToD24S8ResourceLayout);
- var convertIndexBufferBindings = new ShaderBindings(
- new[] { 0 },
- new[] { 1, 2 },
- Array.Empty<int>(),
- Array.Empty<int>());
+ var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, convertIndexBufferResourceLayout);
- var convertIndirectDataBindings = new ShaderBindings(
- new[] { 0 },
- new[] { 1, 2, 3 },
- Array.Empty<int>(),
- Array.Empty<int>());
+ var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
+ .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
+ .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, convertIndirectDataResourceLayout);
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorDrawToMsResourceLayout);
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorDrawToMsResourceLayout);
if (gd.Capabilities.SupportsShaderStencilExport)
{
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, blitResourceLayout);
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorDrawToMsResourceLayout);
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
{
- new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
- new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
- });
+ new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
+ new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
+ }, colorDrawToMsResourceLayout);
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs
index c834fa62..e7c43567 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs
@@ -1,52 +1,101 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
-using System.Collections.Generic;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Vulkan
{
class PipelineLayoutCache
{
- private readonly PipelineLayoutCacheEntry[] _plce;
- private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
-
- public PipelineLayoutCache()
+ private readonly struct PlceKey : IEquatable<PlceKey>
{
- _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
- _plceMinimal = new List<PipelineLayoutCacheEntry>();
+ public readonly ReadOnlyCollection<ResourceDescriptorCollection> SetDescriptors;
+ public readonly bool UsePushDescriptors;
+
+ public PlceKey(ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, bool usePushDescriptors)
+ {
+ SetDescriptors = setDescriptors;
+ UsePushDescriptors = usePushDescriptors;
+ }
+
+ public override int GetHashCode()
+ {
+ HashCode hasher = new HashCode();
+
+ if (SetDescriptors != null)
+ {
+ foreach (var setDescriptor in SetDescriptors)
+ {
+ hasher.Add(setDescriptor);
+ }
+ }
+
+ hasher.Add(UsePushDescriptors);
+
+ return hasher.ToHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is PlceKey other && Equals(other);
+ }
+
+ public bool Equals(PlceKey other)
+ {
+ if ((SetDescriptors == null) != (other.SetDescriptors == null))
+ {
+ return false;
+ }
+
+ if (SetDescriptors != null)
+ {
+ if (SetDescriptors.Count != other.SetDescriptors.Count)
+ {
+ return false;
+ }
+
+ for (int index = 0; index < SetDescriptors.Count; index++)
+ {
+ if (!SetDescriptors[index].Equals(other.SetDescriptors[index]))
+ {
+ return false;
+ }
+ }
+ }
+
+ return UsePushDescriptors == other.UsePushDescriptors;
+ }
}
- public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
+ private readonly ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry> _plces;
+
+ public PipelineLayoutCache()
{
- var plce = new PipelineLayoutCacheEntry(gd, device, shaders);
- _plceMinimal.Add(plce);
- return plce;
+ _plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>();
}
- public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
+ public PipelineLayoutCacheEntry GetOrCreate(
+ VulkanRenderer gd,
+ Device device,
+ ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
+ bool usePushDescriptors)
{
- if (_plce[stages] == null)
- {
- _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
- }
+ var key = new PlceKey(setDescriptors, usePushDescriptors);
- return _plce[stages];
+ return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors));
}
protected virtual unsafe void Dispose(bool disposing)
{
if (disposing)
{
- for (int i = 0; i < _plce.Length; i++)
- {
- _plce[i]?.Dispose();
- }
-
- foreach (var plce in _plceMinimal)
+ foreach (var plce in _plces.Values)
{
plce.Dispose();
}
- _plceMinimal.Clear();
+ _plces.Clear();
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
index 2c966115..eeb25dc0 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
@@ -1,6 +1,7 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Vulkan
{
@@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly int[] _dsCacheCursor;
private int _dsLastCbIndex;
- private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device)
+ private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
{
_gd = gd;
_device = device;
@@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
{
- _dsCache[i] = new List<Auto<DescriptorSetCollection>>[PipelineBase.DescriptorSetLayouts];
+ _dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount];
- for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++)
+ for (int j = 0; j < _dsCache[i].Length; j++)
{
_dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
}
}
- _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts];
+ _dsCacheCursor = new int[setsCount];
}
- public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device)
- {
- DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout);
- PipelineLayout = pipelineLayout;
- }
-
- public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, ShaderSource[] shaders) : this(gd, device)
+ public PipelineLayoutCacheEntry(
+ VulkanRenderer gd,
+ Device device,
+ ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
+ bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
{
- DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout);
- PipelineLayout = pipelineLayout;
+ (DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
}
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
@@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_dsLastCbIndex = commandBufferIndex;
- for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++)
+ for (int i = 0; i < _dsCacheCursor.Length; i++)
{
_dsCacheCursor[i] = 0;
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
index 2d881419..bcb2c1a5 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
@@ -1,257 +1,74 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
-using System.Collections.Generic;
-using System.Numerics;
+using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Vulkan
{
static class PipelineLayoutFactory
{
- private const ShaderStageFlags SupportBufferStages =
- ShaderStageFlags.VertexBit |
- ShaderStageFlags.FragmentBit |
- ShaderStageFlags.ComputeBit;
-
- private static ShaderStageFlags ActiveStages(uint stages)
- {
- ShaderStageFlags stageFlags = 0;
-
- while (stages != 0)
- {
- int stage = BitOperations.TrailingZeroCount(stages);
- stages &= ~(1u << stage);
-
- stageFlags |= stage switch
- {
- 1 => ShaderStageFlags.FragmentBit,
- 2 => ShaderStageFlags.GeometryBit,
- 3 => ShaderStageFlags.TessellationControlBit,
- 4 => ShaderStageFlags.TessellationEvaluationBit,
- _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
- };
- }
-
- return stageFlags;
- }
-
- public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
+ public static unsafe (DescriptorSetLayout[], PipelineLayout) Create(
+ VulkanRenderer gd,
+ Device device,
+ ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
+ bool usePushDescriptors)
{
- int stagesCount = BitOperations.PopCount(stages);
-
- int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
- int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
- int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
+ DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
- DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
- DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
- DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
- DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
+ bool isMoltenVk = gd.IsMoltenVk;
- uLayoutBindings[0] = new DescriptorSetLayoutBinding
+ for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
{
- Binding = 0,
- DescriptorType = DescriptorType.UniformBuffer,
- DescriptorCount = 1,
- StageFlags = SupportBufferStages
- };
+ ResourceDescriptorCollection rdc = setDescriptors[setIndex];
- int iter = 0;
- var activeStages = ActiveStages(stages);
-
- while (stages != 0)
- {
- int stage = BitOperations.TrailingZeroCount(stages);
- stages &= ~(1u << stage);
+ ResourceStages activeStages = ResourceStages.None;
- var stageFlags = stage switch
+ if (isMoltenVk)
{
- 1 => ShaderStageFlags.FragmentBit,
- 2 => ShaderStageFlags.GeometryBit,
- 3 => ShaderStageFlags.TessellationControlBit,
- 4 => ShaderStageFlags.TessellationEvaluationBit,
- _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
- };
-
- void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
- {
- int totalPerStage = maxPerStage * skip;
-
- for (int i = 0; i < maxPerStage; i++)
+ for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
{
- bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
- {
- Binding = (uint)(start + stage * totalPerStage + i),
- DescriptorType = type,
- DescriptorCount = 1,
- StageFlags = stageFlags
- };
+ activeStages |= rdc.Descriptors[descIndex].Stages;
}
}
- void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
- {
- // There's a bug on MoltenVK where using the same buffer across different stages
- // causes invalid resource errors, allow the binding on all active stages as workaround.
- var flags = gd.IsMoltenVk ? activeStages : stageFlags;
-
- bindings[start + iter] = new DescriptorSetLayoutBinding
- {
- Binding = (uint)(start + stage * maxPerStage),
- DescriptorType = DescriptorType.StorageBuffer,
- DescriptorCount = (uint)maxPerStage,
- StageFlags = flags
- };
- }
-
- Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
- SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
- Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
- Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
- Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
- Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
-
- iter++;
- }
-
- DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
-
- var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = uLayoutBindings,
- BindingCount = (uint)uCount,
- Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
- };
-
- var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = sLayoutBindings,
- BindingCount = (uint)stagesCount
- };
-
- var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = tLayoutBindings,
- BindingCount = (uint)tCount
- };
-
- var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = iLayoutBindings,
- BindingCount = (uint)iCount
- };
-
- gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
+ DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
- fixed (DescriptorSetLayout* pLayouts = layouts)
- {
- var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
+ for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
{
- SType = StructureType.PipelineLayoutCreateInfo,
- PSetLayouts = pLayouts,
- SetLayoutCount = PipelineBase.DescriptorSetLayouts
- };
-
- gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
- }
+ ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
- return layouts;
- }
-
- public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
- {
- int stagesCount = shaders.Length;
+ ResourceStages stages = descriptor.Stages;
- int uCount = 0;
- int sCount = 0;
- int tCount = 0;
- int iCount = 0;
-
- foreach (var shader in shaders)
- {
- uCount += shader.Bindings.UniformBufferBindings.Count;
- sCount += shader.Bindings.StorageBufferBindings.Count;
- tCount += shader.Bindings.TextureBindings.Count;
- iCount += shader.Bindings.ImageBindings.Count;
- }
-
- DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
- DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
- DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
- DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
-
- int uIndex = 0;
- int sIndex = 0;
- int tIndex = 0;
- int iIndex = 0;
+ if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
+ {
+ // There's a bug on MoltenVK where using the same buffer across different stages
+ // causes invalid resource errors, allow the binding on all active stages as workaround.
+ stages = activeStages;
+ }
- foreach (var shader in shaders)
- {
- var stageFlags = shader.Stage.Convert();
+ layoutBindings[descIndex] = new DescriptorSetLayoutBinding()
+ {
+ Binding = (uint)descriptor.Binding,
+ DescriptorType = descriptor.Type.Convert(),
+ DescriptorCount = (uint)descriptor.Count,
+ StageFlags = stages.Convert()
+ };
+ }
- void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
+ fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
{
- foreach (var b in bds)
+ var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
{
- bindings[start++] = new DescriptorSetLayoutBinding
- {
- Binding = (uint)b,
- DescriptorType = type,
- DescriptorCount = 1,
- StageFlags = stageFlags
- };
- }
- }
+ SType = StructureType.DescriptorSetLayoutCreateInfo,
+ PBindings = pLayoutBindings,
+ BindingCount = (uint)layoutBindings.Length,
+ Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None
+ };
- // TODO: Support buffer textures and images here.
- // This is only used for the helper shaders on the backend, and we don't use buffer textures on them
- // so far, so it's not really necessary right now.
- Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
- Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
- Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
- Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
+ gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
+ }
}
- DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
-
- var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = uLayoutBindings,
- BindingCount = (uint)uCount
- };
-
- var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = sLayoutBindings,
- BindingCount = (uint)sCount
- };
-
- var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = tLayoutBindings,
- BindingCount = (uint)tCount
- };
-
- var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
- {
- SType = StructureType.DescriptorSetLayoutCreateInfo,
- PBindings = iLayoutBindings,
- BindingCount = (uint)iCount
- };
-
- gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
- gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
+ PipelineLayout layout;
fixed (DescriptorSetLayout* pLayouts = layouts)
{
@@ -259,13 +76,13 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.PipelineLayoutCreateInfo,
PSetLayouts = pLayouts,
- SetLayoutCount = PipelineBase.DescriptorSetLayouts
+ SetLayoutCount = (uint)layouts.Length
};
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
}
- return layouts;
+ return (layouts, layout);
}
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
new file mode 100644
index 00000000..feeba474
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ readonly struct ResourceBindingSegment
+ {
+ public readonly int Binding;
+ public readonly int Count;
+ public readonly ResourceType Type;
+ public readonly ResourceStages Stages;
+ public readonly ResourceAccess Access;
+
+ public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, ResourceAccess access)
+ {
+ Binding = binding;
+ Count = count;
+ Type = type;
+ Stages = stages;
+ Access = access;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
new file mode 100644
index 00000000..eac0d6c2
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
@@ -0,0 +1,67 @@
+using Ryujinx.Graphics.GAL;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ class ResourceLayoutBuilder
+ {
+ private const int TotalSets = PipelineBase.DescriptorSetLayouts;
+
+ private readonly List<ResourceDescriptor>[] _resourceDescriptors;
+ private readonly List<ResourceUsage>[] _resourceUsages;
+
+ public ResourceLayoutBuilder()
+ {
+ _resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
+ _resourceUsages = new List<ResourceUsage>[TotalSets];
+
+ for (int index = 0; index < TotalSets; index++)
+ {
+ _resourceDescriptors[index] = new();
+ _resourceUsages[index] = new();
+ }
+ }
+
+ public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
+ {
+ int setIndex = type switch
+ {
+ ResourceType.UniformBuffer => PipelineBase.UniformSetIndex,
+ ResourceType.StorageBuffer => PipelineBase.StorageSetIndex,
+ ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex,
+ ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex,
+ _ => throw new ArgumentException($"Invalid resource type \"{type}\".")
+ };
+
+ ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite;
+
+ _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
+ _resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages, access));
+
+ return this;
+ }
+
+ private static bool IsReadOnlyType(ResourceType type)
+ {
+ return type == ResourceType.UniformBuffer ||
+ type == ResourceType.Sampler ||
+ type == ResourceType.TextureAndSampler ||
+ type == ResourceType.BufferTexture;
+ }
+
+ public ResourceLayout Build()
+ {
+ 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());
+ }
+
+ return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs
index ca99ebf0..d853bb04 100644
--- a/src/Ryujinx.Graphics.Vulkan/Shader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs
@@ -26,8 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
public ShaderStageFlags StageFlags => _stage;
- public ShaderBindings Bindings { get; }
-
public ProgramLinkStatus CompileStatus { private set; get; }
public readonly Task CompileTask;
@@ -36,7 +34,6 @@ namespace Ryujinx.Graphics.Vulkan
{
_api = api;
_device = device;
- Bindings = shaderSource.Bindings;
CompileStatus = ProgramLinkStatus.Incomplete;
diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
index 1694049c..334dfc20 100644
--- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
@@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
public uint Stages { get; }
- public int[][][] Bindings { get; }
+ public ResourceBindingSegment[][] BindingSegments { get; }
public ProgramLinkStatus LinkStatus { get; private set; }
@@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
private Task _compileTask;
private bool _firstBackgroundUse;
- public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
+ public ShaderCollection(
+ VulkanRenderer gd,
+ Device device,
+ ShaderSource[] shaders,
+ ResourceLayout resourceLayout,
+ SpecDescription[] specDescription = null,
+ bool isMinimal = false)
{
_gd = gd;
_device = device;
@@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
_shaders = internalShaders;
- bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
+ bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
- _plce = isMinimal
- ? gd.PipelineLayoutCache.Create(gd, device, shaders)
- : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
+ _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
HasMinimalLayout = isMinimal;
- UsePushDescriptors = usePd;
+ UsePushDescriptors = usePushDescriptors;
Stages = stages;
- int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
- {
- bool hasAny = false;
- int[][] bindings = new int[internalShaders.Length][];
-
- for (int i = 0; i < internalShaders.Length; i++)
- {
- var collection = selector(internalShaders[i].Bindings);
- hasAny |= collection.Count != 0;
- bindings[i] = collection.ToArray();
- }
-
- return hasAny ? bindings : Array.Empty<int[]>();
- }
-
- Bindings = new[]
- {
- GrabAll(x => x.UniformBufferBindings),
- GrabAll(x => x.StorageBufferBindings),
- GrabAll(x => x.TextureBindings),
- GrabAll(x => x.ImageBindings)
- };
+ BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
_compileTask = Task.CompletedTask;
_firstBackgroundUse = false;
@@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
Device device,
ShaderSource[] sources,
+ ResourceLayout resourceLayout,
ProgramPipelineState state,
- bool fromCache) : this(gd, device, sources)
+ bool fromCache) : this(gd, device, sources, resourceLayout)
{
_state = state;
@@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
_firstBackgroundUse = !fromCache;
}
+ private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
+ {
+ ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
+
+ for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
+ {
+ List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>();
+
+ ResourceUsage currentUsage = default;
+ int currentCount = 0;
+
+ for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
+ {
+ ResourceUsage usage = setUsages[setIndex].Usages[index];
+
+ // If the resource is not accessed, we don't need to update it.
+ if (usage.Access == ResourceAccess.None)
+ {
+ continue;
+ }
+
+ if (currentUsage.Binding + currentCount != usage.Binding ||
+ currentUsage.Type != usage.Type ||
+ currentUsage.Stages != usage.Stages ||
+ currentUsage.Access != usage.Access)
+ {
+ if (currentCount != 0)
+ {
+ currentSegments.Add(new ResourceBindingSegment(
+ currentUsage.Binding,
+ currentCount,
+ currentUsage.Type,
+ currentUsage.Stages,
+ currentUsage.Access));
+ }
+
+ currentUsage = usage;
+ currentCount = 1;
+ }
+ else
+ {
+ currentCount++;
+ }
+ }
+
+ if (currentCount != 0)
+ {
+ currentSegments.Add(new ResourceBindingSegment(
+ currentUsage.Binding,
+ currentCount,
+ currentUsage.Type,
+ currentUsage.Stages,
+ currentUsage.Access));
+ }
+
+ segments[setIndex] = currentSegments.ToArray();
+ }
+
+ return segments;
+ }
+
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index e4ce4904..ffa1a103 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -10,7 +10,6 @@ using Silk.NET.Vulkan.Extensions.EXT;
using Silk.NET.Vulkan.Extensions.KHR;
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
@@ -398,17 +397,17 @@ namespace Ryujinx.Graphics.Vulkan
if (info.State.HasValue || isCompute)
{
- return new ShaderCollection(this, _device, sources, info.State ?? default, info.FromCache);
+ return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache);
}
else
{
- return new ShaderCollection(this, _device, sources);
+ return new ShaderCollection(this, _device, sources, info.ResourceLayout);
}
}
- internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null)
+ internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null)
{
- return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true);
+ return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true);
}
public ISampler CreateSampler(GAL.SamplerCreateInfo info)
@@ -658,7 +657,7 @@ namespace Ryujinx.Graphics.Vulkan
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
}
- public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
+ internal GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
{
return topology switch
{
@@ -669,7 +668,7 @@ namespace Ryujinx.Graphics.Vulkan
};
}
- public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
+ internal bool TopologyUnsupported(GAL.PrimitiveTopology topology)
{
return topology switch
{