Add minimal pipeline layouts that only contains used bindings

They are used by helper shaders, the intention is avoiding needing to recompile the shaders (from GLSL to SPIR-V) if the bindings changes on the translated guest shaders
This commit is contained in:
gdk 2022-06-10 22:14:20 -03:00 committed by riperiperi
parent 38ecf0f117
commit 400ed2d96d
7 changed files with 195 additions and 39 deletions

View file

@ -32,7 +32,7 @@ void main()
private const string ColorBlitFragmentShaderSource = @"#version 450 core 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) in vec2 tex_coord;
layout (location = 0) out vec4 colour; layout (location = 0) out vec4 colour;
@ -44,7 +44,7 @@ void main()
private const string ColorBlitClearAlphaFragmentShaderSource = @"#version 450 core 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) in vec2 tex_coord;
layout (location = 0) out vec4 colour; layout (location = 0) out vec4 colour;
@ -125,20 +125,20 @@ void main()
var fragmentBindings = new ShaderBindings( var fragmentBindings = new ShaderBindings(
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>(), Array.Empty<int>(),
new[] { Constants.MaxTexturesPerStage * 2 }, new[] { 0 },
Array.Empty<int>()); Array.Empty<int>());
_programColorBlit = gd.CreateProgram(new[] _programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, 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(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), new ShaderSource(ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl),
}, new ShaderInfo(-1)); });
var fragmentBindings2 = new ShaderBindings( var fragmentBindings2 = new ShaderBindings(
Array.Empty<int>(), Array.Empty<int>(),
@ -146,11 +146,11 @@ void main()
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>()); Array.Empty<int>());
_programColorClear = gd.CreateProgram(new[] _programColorClear = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl), new ShaderSource(ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl),
}, new ShaderInfo(-1)); });
} }
public void Blit( public void Blit(
@ -191,7 +191,7 @@ void main()
var sampler = linearFilter ? _samplerLinear : _samplerNearest; var sampler = linearFilter ? _samplerLinear : _samplerNearest;
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, Constants.MaxTexturesPerStage * 2, src, sampler); _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler);
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)]; Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
@ -328,7 +328,7 @@ void main()
{ {
const int RegionBufferSize = 16; const int RegionBufferSize = 16;
pipeline.SetTextureAndSampler(ShaderStage.Fragment, Constants.MaxTexturesPerStage * 2, src, srcSampler); pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler);
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)]; Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];

View file

@ -1,14 +1,25 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
class PipelineLayoutCache class PipelineLayoutCache
{ {
private PipelineLayoutCacheEntry[] _plce; private readonly PipelineLayoutCacheEntry[] _plce;
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
public PipelineLayoutCache() public PipelineLayoutCache()
{ {
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages]; _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
_plceMinimal = new List<PipelineLayoutCacheEntry>();
}
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) public PipelineLayoutCacheEntry GetOrCreate(VulkanGraphicsDevice gd, Device device, uint stages)
@ -29,6 +40,13 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_plce[i]?.Dispose(); _plce[i]?.Dispose();
} }
foreach (var plce in _plceMinimal)
{
plce.Dispose();
}
_plceMinimal.Clear();
} }
} }

View file

@ -1,3 +1,4 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System.Collections.Generic; using System.Collections.Generic;
@ -15,16 +16,10 @@ namespace Ryujinx.Graphics.Vulkan
private readonly int[] _dsCacheCursor; private readonly int[] _dsCacheCursor;
private int _dsLastCbIndex; private int _dsLastCbIndex;
private readonly uint _stages; private PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device)
public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, uint stages)
{ {
_gd = gd; _gd = gd;
_device = device; _device = device;
_stages = stages;
DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, out var pipelineLayout);
PipelineLayout = pipelineLayout;
_dsCache = new List<Auto<DescriptorSetCollection>>[CommandBufferPool.MaxCommandBuffers][]; _dsCache = new List<Auto<DescriptorSetCollection>>[CommandBufferPool.MaxCommandBuffers][];
@ -41,6 +36,18 @@ namespace Ryujinx.Graphics.Vulkan
_dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts]; _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<DescriptorSetCollection> GetNewDescriptorSetCollection( public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
VulkanGraphicsDevice gd, VulkanGraphicsDevice gd,
int commandBufferIndex, int commandBufferIndex,
@ -75,28 +82,25 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (disposing) 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][j][k].Dispose();
{
_dsCache[i][y][z].Dispose();
}
_dsCache[i][y].Clear();
} }
}
_gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null); _dsCache[i][j].Clear();
for (int i = 0; i < DescriptorSetLayouts.Length; i++)
{
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
} }
} }
_gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null);
for (int i = 0; i < DescriptorSetLayouts.Length; i++)
{
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
}
} }
} }

View file

@ -1,10 +1,17 @@
using Silk.NET.Vulkan; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System.Collections.Generic;
using System.Numerics; using System.Numerics;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
static class PipelineLayoutFactory 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) public static unsafe DescriptorSetLayout[] Create(VulkanGraphicsDevice gd, Device device, uint stages, out PipelineLayout layout)
{ {
int stagesCount = BitOperations.PopCount(stages); int stagesCount = BitOperations.PopCount(stages);
@ -23,7 +30,7 @@ namespace Ryujinx.Graphics.Vulkan
Binding = 0, Binding = 0,
DescriptorType = DescriptorType.UniformBuffer, DescriptorType = DescriptorType.UniformBuffer,
DescriptorCount = 1, DescriptorCount = 1,
StageFlags = ShaderStageFlags.ShaderStageVertexBit | ShaderStageFlags.ShaderStageFragmentBit | ShaderStageFlags.ShaderStageComputeBit StageFlags = SupportBufferStages
}; };
int iter = 0; int iter = 0;
@ -128,5 +135,126 @@ namespace Ryujinx.Graphics.Vulkan
return layouts; 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<int> 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;
}
} }
} }

View file

@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader;
using shaderc; using shaderc;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;

View file

@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
private Task _compileTask; private Task _compileTask;
private bool _firstBackgroundUse; private bool _firstBackgroundUse;
public ShaderCollection(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders) public ShaderCollection(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders, bool isMinimal = false)
{ {
_gd = gd; _gd = gd;
_device = device; _device = device;
@ -87,7 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
_shaders = internalShaders; _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; Stages = stages;

View file

@ -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) public ISampler CreateSampler(GAL.SamplerCreateInfo info)
{ {
return new SamplerHolder(this, _device, info); return new SamplerHolder(this, _device, info);