diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 8a5c998b5..a0e5760cf 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -32,7 +32,7 @@ void main() private const string ColorBlitFragmentShaderSource = @"#version 450 core -layout (binding = 128, set = 2) uniform sampler2D tex; +layout (binding = 0, set = 2) uniform sampler2D tex; layout (location = 0) in vec2 tex_coord; layout (location = 0) out vec4 colour; @@ -44,7 +44,7 @@ void main() private const string ColorBlitClearAlphaFragmentShaderSource = @"#version 450 core -layout (binding = 128, set = 2) uniform sampler2D tex; +layout (binding = 0, set = 2) uniform sampler2D tex; layout (location = 0) in vec2 tex_coord; layout (location = 0) out vec4 colour; @@ -125,20 +125,20 @@ void main() var fragmentBindings = new ShaderBindings( Array.Empty(), Array.Empty(), - new[] { Constants.MaxTexturesPerStage * 2 }, + new[] { 0 }, Array.Empty()); - _programColorBlit = gd.CreateProgram(new[] + _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), - }, new ShaderInfo(-1)); + }); - _programColorBlitClearAlpha = gd.CreateProgram(new[] + _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), - }, new ShaderInfo(-1)); + }); var fragmentBindings2 = new ShaderBindings( Array.Empty(), @@ -146,11 +146,11 @@ void main() Array.Empty(), Array.Empty()); - _programColorClear = gd.CreateProgram(new[] + _programColorClear = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl), - }, new ShaderInfo(-1)); + }); } public void Blit( @@ -191,7 +191,7 @@ void main() var sampler = linearFilter ? _samplerLinear : _samplerNearest; - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, Constants.MaxTexturesPerStage * 2, src, sampler); + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); Span region = stackalloc float[RegionBufferSize / sizeof(float)]; @@ -328,7 +328,7 @@ void main() { const int RegionBufferSize = 16; - pipeline.SetTextureAndSampler(ShaderStage.Fragment, Constants.MaxTexturesPerStage * 2, src, srcSampler); + pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); Span region = stackalloc float[RegionBufferSize / sizeof(float)]; diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs index a1de6781e..15a24fa51 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs @@ -1,14 +1,25 @@ +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; +using System.Collections.Generic; namespace Ryujinx.Graphics.Vulkan { class PipelineLayoutCache { - private PipelineLayoutCacheEntry[] _plce; + private readonly PipelineLayoutCacheEntry[] _plce; + private readonly List _plceMinimal; public PipelineLayoutCache() { _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages]; + _plceMinimal = new List(); + } + + public PipelineLayoutCacheEntry Create(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders) + { + var plce = new PipelineLayoutCacheEntry(gd, device, shaders); + _plceMinimal.Add(plce); + return plce; } public PipelineLayoutCacheEntry GetOrCreate(VulkanGraphicsDevice gd, Device device, uint stages) @@ -29,6 +40,13 @@ namespace Ryujinx.Graphics.Vulkan { _plce[i]?.Dispose(); } + + foreach (var plce in _plceMinimal) + { + plce.Dispose(); + } + + _plceMinimal.Clear(); } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs index 4505362d5..d86d04faa 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System.Collections.Generic; @@ -15,16 +16,10 @@ namespace Ryujinx.Graphics.Vulkan private readonly int[] _dsCacheCursor; private int _dsLastCbIndex; - private readonly uint _stages; - - public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, uint stages) + private PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device) { _gd = gd; _device = device; - _stages = stages; - - DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, out var pipelineLayout); - PipelineLayout = pipelineLayout; _dsCache = new List>[CommandBufferPool.MaxCommandBuffers][]; @@ -41,6 +36,18 @@ namespace Ryujinx.Graphics.Vulkan _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts]; } + public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, uint stages) : this(gd, device) + { + DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, out var pipelineLayout); + PipelineLayout = pipelineLayout; + } + + public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders) : this(gd, device) + { + DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout); + PipelineLayout = pipelineLayout; + } + public Auto GetNewDescriptorSetCollection( VulkanGraphicsDevice gd, int commandBufferIndex, @@ -75,28 +82,25 @@ namespace Ryujinx.Graphics.Vulkan { if (disposing) { - unsafe + for (int i = 0; i < _dsCache.Length; i++) { - for (int i = 0; i < _dsCache.Length; i++) + for (int j = 0; j < _dsCache[i].Length; j++) { - for (int y = 0; y < _dsCache[i].Length; y++) + for (int k = 0; k < _dsCache[i][j].Count; k++) { - for (int z = 0; z < _dsCache[i][y].Count; z++) - { - _dsCache[i][y][z].Dispose(); - } - - _dsCache[i][y].Clear(); + _dsCache[i][j][k].Dispose(); } - } - _gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); - - for (int i = 0; i < DescriptorSetLayouts.Length; i++) - { - _gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null); + _dsCache[i][j].Clear(); } } + + _gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); + + for (int i = 0; i < DescriptorSetLayouts.Length; i++) + { + _gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null); + } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index 0f289c68d..c3ca0fc92 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -1,10 +1,17 @@ -using Silk.NET.Vulkan; +using Ryujinx.Graphics.GAL; +using Silk.NET.Vulkan; +using System.Collections.Generic; using System.Numerics; namespace Ryujinx.Graphics.Vulkan { static class PipelineLayoutFactory { + private const ShaderStageFlags SupportBufferStages = + ShaderStageFlags.ShaderStageVertexBit | + ShaderStageFlags.ShaderStageFragmentBit | + ShaderStageFlags.ShaderStageComputeBit; + public static unsafe DescriptorSetLayout[] Create(VulkanGraphicsDevice gd, Device device, uint stages, out PipelineLayout layout) { int stagesCount = BitOperations.PopCount(stages); @@ -23,7 +30,7 @@ namespace Ryujinx.Graphics.Vulkan Binding = 0, DescriptorType = DescriptorType.UniformBuffer, DescriptorCount = 1, - StageFlags = ShaderStageFlags.ShaderStageVertexBit | ShaderStageFlags.ShaderStageFragmentBit | ShaderStageFlags.ShaderStageComputeBit + StageFlags = SupportBufferStages }; int iter = 0; @@ -128,5 +135,126 @@ namespace Ryujinx.Graphics.Vulkan return layouts; } + + public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders, out PipelineLayout layout) + { + int stagesCount = shaders.Length; + + int uCount = 0; + int tCount = 0; + int iCount = 0; + + foreach (var shader in shaders) + { + uCount += shader.Bindings.UniformBufferBindings.Count; + tCount += shader.Bindings.TextureBindings.Count; + iCount += shader.Bindings.ImageBindings.Count; + } + + 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 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) + { + bindings[start++] = new DescriptorSetLayoutBinding + { + Binding = (uint)b, + DescriptorType = type, + DescriptorCount = 1, + StageFlags = stageFlags + }; + } + } + + void SetStorage(DescriptorSetLayoutBinding* bindings, ref int start, int count) + { + bindings[start++] = new DescriptorSetLayoutBinding + { + Binding = (uint)start, + DescriptorType = DescriptorType.StorageBuffer, + DescriptorCount = (uint)count, + StageFlags = stageFlags + }; + } + + // TODO: Support buffer textures and images here. + // This is only used for the helper shaders on the backkend, 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); + SetStorage(sLayoutBindings, ref sIndex, shader.Bindings.StorageBufferBindings.Count); + Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings); + Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings); + } + + DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineFull.DescriptorSetLayouts]; + + var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() + { + SType = StructureType.DescriptorSetLayoutCreateInfo, + PBindings = uLayoutBindings, + BindingCount = (uint)uCount + }; + + 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[PipelineFull.UniformSetIndex]).ThrowOnError(); + gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineFull.StorageSetIndex]).ThrowOnError(); + gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineFull.TextureSetIndex]).ThrowOnError(); + gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineFull.ImageSetIndex]).ThrowOnError(); + + fixed (DescriptorSetLayout* pLayouts = layouts) + { + var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo() + { + SType = StructureType.PipelineLayoutCreateInfo, + PSetLayouts = pLayouts, + SetLayoutCount = PipelineFull.DescriptorSetLayouts + }; + + gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError(); + } + + return layouts; + } } } diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/Ryujinx.Graphics.Vulkan/Shader.cs index d15cfb2b0..2ced4bea7 100644 --- a/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/Ryujinx.Graphics.Vulkan/Shader.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader; using shaderc; using Silk.NET.Vulkan; using System; -using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index e8343517a..0516fe83e 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan private Task _compileTask; private bool _firstBackgroundUse; - public ShaderCollection(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders) + public ShaderCollection(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders, bool isMinimal = false) { _gd = gd; _device = device; @@ -87,7 +87,9 @@ namespace Ryujinx.Graphics.Vulkan _shaders = internalShaders; - _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, stages); + _plce = isMinimal + ? gd.PipelineLayoutCache.Create(gd, device, shaders) + : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages); Stages = stages; diff --git a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs index 7499ab703..adf36d54c 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs @@ -317,6 +317,11 @@ namespace Ryujinx.Graphics.Vulkan } } + internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources) + { + return new ShaderCollection(this, _device, sources, isMinimal: true); + } + public ISampler CreateSampler(GAL.SamplerCreateInfo info) { return new SamplerHolder(this, _device, info);