diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs new file mode 100644 index 0000000000..3cde281fc2 --- /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 + { + 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 + { + 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 Descriptors { get; } + + public ResourceDescriptorCollection(ReadOnlyCollection 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 Usages { get; } + + public ResourceUsageCollection(ReadOnlyCollection usages) + { + Usages = usages; + } + } + + public readonly struct ResourceLayout + { + public ReadOnlyCollection Sets { get; } + public ReadOnlyCollection SetUsages { get; } + + public ResourceLayout( + ReadOnlyCollection sets, + ReadOnlyCollection 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 6ab2938298..0000000000 --- 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 UniformBufferBindings { get; } - public IReadOnlyCollection StorageBufferBindings { get; } - public IReadOnlyCollection TextureBindings { get; } - public IReadOnlyCollection ImageBindings { get; } - - public ShaderBindings( - IReadOnlyCollection uniformBufferBindings, - IReadOnlyCollection storageBufferBindings, - IReadOnlyCollection textureBindings, - IReadOnlyCollection 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 b4c8711789..643f1bc5fc 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 91d3a6325d..773c0a8a14 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 b4764d578e..9f263e9d76 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 58e5c7b10e..d80518b109 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 77e526672c..2dc5c97197 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(), Array.Empty(), Array.Empty(), Array.Empty()); - } } } \ 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 7db627ba9e..e4e1e0d611 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 d543f42ad8..b7ba159a51 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 /// Shader source 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); } /// @@ -717,25 +720,6 @@ namespace Ryujinx.Graphics.Gpu.Shader }; } - /// - /// Gets information about the bindings used by a shader program. - /// - /// Shader program information to get the information from - /// Shader bindings - 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); - } - /// /// Creates shader translation options with the requested graphics API and flags. /// The shader language is choosen based on the current configuration and graphics API. diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs new file mode 100644 index 0000000000..39b31cf6ab --- /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 +{ + /// + /// Shader info structure builder. + /// + 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[] _resourceDescriptors; + private readonly List[] _resourceUsages; + + /// + /// Creates a new shader info builder. + /// + /// GPU context that owns the shaders that will be added to the builder + public ShaderInfoBuilder(GpuContext context) + { + _context = context; + + _fragmentOutputMap = -1; + + _resourceDescriptors = new List[TotalSets]; + _resourceUsages = new List[TotalSets]; + + for (int index = 0; index < TotalSets; index++) + { + _resourceDescriptors[index] = new(); + _resourceUsages[index] = new(); + } + + AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1); + } + + /// + /// Adds information from a given shader stage. + /// + /// Shader stage information + 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); + } + + /// + /// Adds a resource descriptor to the list of descriptors. + /// + /// Shader stages where the resource is used + /// Type of the resource + /// Descriptor set number where the resource will be bound + /// Binding number where the resource will be bound + /// Number of resources bound at the binding location + 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)); + } + } + + /// + /// Adds two interleaved groups of resources to the list of descriptors. + /// + /// Shader stages where the resource is used + /// Type of the first interleaved resource + /// Type of the second interleaved resource + /// Descriptor set number where the resource will be bound + /// Binding number where the resource will be bound + /// Number of resources bound at the binding location + 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); + } + + /// + /// Adds an array resource to the list of descriptors. + /// + /// Shader stages where the resource is used + /// Type of the resource + /// Descriptor set number where the resource will be bound + /// Binding number where the resource will be bound + /// Number of resources bound at the binding location + private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) + { + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages)); + } + + /// + /// Adds buffer usage information to the list of usages. + /// + /// Buffers to be added + /// Stages where the buffers are used + /// Descriptor set index where the buffers will be bound + /// True for storage buffers, false for uniform buffers + private void AddUsage(IEnumerable 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)); + } + } + + /// + /// Adds texture usage information to the list of usages. + /// + /// Textures to be added + /// Stages where the textures are used + /// Descriptor set index where the textures will be bound + /// True for images, false for textures + private void AddUsage(IEnumerable 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)); + } + } + + /// + /// Creates a new shader information structure from the added information. + /// + /// Optional pipeline state for background shader compilation + /// Indicates if the shader comes from a disk cache + /// Shader information + 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); + } + } + + /// + /// Builds shader information for shaders from the disk cache. + /// + /// GPU context that owns the shaders + /// Shaders from the disk cache + /// Optional pipeline for background compilation + /// Shader information + public static ShaderInfo BuildForCache(GpuContext context, IEnumerable 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); + } + + /// + /// Builds shader information for a compute shader. + /// + /// GPU context that owns the shader + /// Compute shader information + /// True if the compute shader comes from a disk cache, false otherwise + /// Shader information + 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 a47ea5ff89..f3ac36e138 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 (!_uniformSet[index]) + { + UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + + _uniformSet[index] = true; + } } - if (setIndex == PipelineBase.UniformSetIndex) + ReadOnlySpan uniformBuffers = _uniformBuffers; + dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); + } + else if (setIndex == PipelineBase.StorageSetIndex) + { + for (int i = 0; i < count; i++) { + int index = binding + i; + + if (!_storageSet[index]) + { + UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer); + + _storageSet[index] = true; + } + } + + ReadOnlySpan storageBuffers = _storageBuffers; + if (program.HasMinimalLayout) + { + dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); + } + else + { + dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count)); + } + } + else if (setIndex == PipelineBase.TextureSetIndex) + { + if (segment.Type != ResourceType.BufferTexture) + { + Span textures = _textures; + for (int i = 0; i < count; i++) { - int index = binding + i; + ref var texture = ref textures[i]; - if (!_uniformSet[index]) + texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; + texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; + + if (texture.ImageView.Handle == 0) { - UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; + } - _uniformSet[index] = true; + if (texture.Sampler.Handle == 0) + { + texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; } } - ReadOnlySpan uniformBuffers = _uniformBuffers; - dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); + dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler); } - else if (setIndex == PipelineBase.StorageSetIndex) + else { + Span bufferTextures = _bufferTextures; + for (int i = 0; i < count; i++) { - int index = binding + i; - - if (!_storageSet[index]) - { - UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer); - - _storageSet[index] = true; - } + bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; } - ReadOnlySpan storageBuffers = _storageBuffers; - if (program.HasMinimalLayout) - { - dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer); - } - else - { - dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count)); - } + dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer); } - else if (setIndex == PipelineBase.TextureSetIndex) + } + else if (setIndex == PipelineBase.ImageSetIndex) + { + if (segment.Type != ResourceType.BufferImage) { - if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout) + Span images = _images; + + for (int i = 0; i < count; i++) { - Span textures = _textures; - - for (int i = 0; i < count; i++) - { - ref var texture = ref textures[i]; - - texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; - texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; - - if (texture.ImageView.Handle == 0) - { - texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; - } - - if (texture.Sampler.Handle == 0) - { - texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; - } - } - - dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler); + images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; } - else - { - Span bufferTextures = _bufferTextures; - for (int i = 0; i < count; i++) - { - bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default; - } - - dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer); - } + dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage); } - else if (setIndex == PipelineBase.ImageSetIndex) + else { - if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout) + Span bufferImages = _bufferImages; + + for (int i = 0; i < count; i++) { - Span images = _images; - - for (int i = 0; i < count; i++) - { - images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default; - } - - dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage); + bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default; } - else - { - Span bufferImages = _bufferImages; - 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); - } + 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 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; - for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count) + bool doUpdate = false; + + 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++; + UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer); + _uniformSet[index] = true; + doUpdate = true; } + } - 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; - } - } - - if (doUpdate) - { - ReadOnlySpan uniformBuffers = _uniformBuffers; - UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); - } + if (doUpdate) + { + ReadOnlySpan 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 5f15f15f6e..e9952126f4 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(), - 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 sharpeningBindings = new ShaderBindings( - new[] { 2, 3, 4 }, - Array.Empty(), - new[] { 1 }, - new[] { 0 }); + 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 b7316d8573..9da003ddaf 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(), - 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 38f86baeb4..0d392a65f7 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(), - new[] { 1 }, - 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 blendBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty(), - new[] { 1, 3, 4 }, - new[] { 0 }); + 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 neighbourBindings = new ShaderBindings( - new[] { 2 }, - Array.Empty(), - new[] { 1, 3 }, - new[] { 0 }); + 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 1f03b68c2f..55868ee35b 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 c064696d85..43214be4d4 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(), - Array.Empty(), - Array.Empty()); - - var blitFragmentBindings = new ShaderBindings( - Array.Empty(), - Array.Empty(), - new[] { 0 }, - Array.Empty()); + 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(), - Array.Empty(), - Array.Empty(), - Array.Empty()); + 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(), - Array.Empty()); + 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(), - 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(), - Array.Empty(), - Array.Empty(), - Array.Empty()); - - var colorDrawToMsFragmentBindings = new ShaderBindings( - new[] { 0 }, - Array.Empty(), - new[] { 0 }, - Array.Empty()); + 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(), - Array.Empty()); + 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(), - Array.Empty()); + 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(), - Array.Empty()); + 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 c834fa621c..e7c4356706 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 _plceMinimal; + private readonly struct PlceKey : IEquatable + { + public readonly ReadOnlyCollection SetDescriptors; + public readonly bool UsePushDescriptors; + + public PlceKey(ReadOnlyCollection 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; + } + } + + private readonly ConcurrentDictionary _plces; public PipelineLayoutCache() { - _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages]; - _plceMinimal = new List(); + _plces = new ConcurrentDictionary(); } - public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders) + public PipelineLayoutCacheEntry GetOrCreate( + VulkanRenderer gd, + Device device, + ReadOnlyCollection setDescriptors, + bool usePushDescriptors) { - var plce = new PipelineLayoutCacheEntry(gd, device, shaders); - _plceMinimal.Add(plce); - return plce; - } + var key = new PlceKey(setDescriptors, usePushDescriptors); - public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd) - { - if (_plce[stages] == null) - { - _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd); - } - - 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 2c96611599..eeb25dc0f6 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>[PipelineBase.DescriptorSetLayouts]; + _dsCache[i] = new List>[setsCount]; - for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++) + for (int j = 0; j < _dsCache[i].Length; j++) { _dsCache[i][j] = new List>(); } } - _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts]; + _dsCacheCursor = new int[setsCount]; } - public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device) + public PipelineLayoutCacheEntry( + VulkanRenderer gd, + Device device, + ReadOnlyCollection setDescriptors, + bool usePushDescriptors) : this(gd, device, setDescriptors.Count) { - DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout); - PipelineLayout = pipelineLayout; - } - - public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, ShaderSource[] shaders) : this(gd, device) - { - DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout); - PipelineLayout = pipelineLayout; + (DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors); } public Auto 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 2d8814191c..bcb2c1a503 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) + public static unsafe (DescriptorSetLayout[], PipelineLayout) Create( + VulkanRenderer gd, + Device device, + ReadOnlyCollection setDescriptors, + bool usePushDescriptors) { - ShaderStageFlags stageFlags = 0; + DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count]; - while (stages != 0) + bool isMoltenVk = gd.IsMoltenVk; + + for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++) { - int stage = BitOperations.TrailingZeroCount(stages); - stages &= ~(1u << stage); + ResourceDescriptorCollection rdc = setDescriptors[setIndex]; - stageFlags |= stage switch + ResourceStages activeStages = ResourceStages.None; + + if (isMoltenVk) { - 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) - { - int stagesCount = BitOperations.PopCount(stages); - - int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1; - int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount; - int iCount = Constants.MaxImagesPerStage * 2 * stagesCount; - - DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount]; - DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount]; - DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount]; - DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount]; - - uLayoutBindings[0] = new DescriptorSetLayoutBinding - { - Binding = 0, - DescriptorType = DescriptorType.UniformBuffer, - DescriptorCount = 1, - StageFlags = SupportBufferStages - }; - - int iter = 0; - var activeStages = ActiveStages(stages); - - while (stages != 0) - { - int stage = BitOperations.TrailingZeroCount(stages); - stages &= ~(1u << stage); - - var stageFlags = stage switch - { - 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; + DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count]; - bindings[start + iter] = new DescriptorSetLayoutBinding + for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++) + { + ResourceDescriptor descriptor = rdc.Descriptors[descIndex]; + + ResourceStages stages = descriptor.Stages; + + if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk) { - Binding = (uint)(start + stage * maxPerStage), - DescriptorType = DescriptorType.StorageBuffer, - DescriptorCount = (uint)maxPerStage, - StageFlags = flags + // 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; + } + + layoutBindings[descIndex] = new DescriptorSetLayoutBinding() + { + Binding = (uint)descriptor.Binding, + DescriptorType = descriptor.Type.Convert(), + DescriptorCount = (uint)descriptor.Count, + StageFlags = stages.Convert() }; } - 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(); - - fixed (DescriptorSetLayout* pLayouts = layouts) - { - var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo() + fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings) { - SType = StructureType.PipelineLayoutCreateInfo, - PSetLayouts = pLayouts, - SetLayoutCount = PipelineBase.DescriptorSetLayouts - }; - - gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); - } - - return layouts; - } - - public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout) - { - int stagesCount = shaders.Length; - - 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; - - foreach (var shader in shaders) - { - var stageFlags = shader.Stage.Convert(); - - void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable bds) - { - 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 0000000000..feeba4744c --- /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 0000000000..eac0d6c200 --- /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[] _resourceDescriptors; + private readonly List[] _resourceUsages; + + public ResourceLayoutBuilder() + { + _resourceDescriptors = new List[TotalSets]; + _resourceUsages = new List[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 ca99ebf073..d853bb04c8 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 1694049c97..334dfc2004 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> 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(); - } - - 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 setUsages) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; + + for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) + { + List currentSegments = new List(); + + 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 e4ce4904b0..ffa1a1034e 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 {