Replace ShaderBindings with new ResourceLayout structure for Vulkan (#5025)
* Introduce ResourceLayout * Part 1: Use new ResourceSegments array on UpdateAndBind * Part 2: Use ResourceLayout to build PipelineLayout * Delete old code * XML docs * Fix shader cache load NRE * Fix typo
This commit is contained in:
parent
402f05b8ef
commit
5626f2ca1c
24 changed files with 1047 additions and 677 deletions
179
src/Ryujinx.Graphics.GAL/ResourceLayout.cs
Normal file
179
src/Ryujinx.Graphics.GAL/ResourceLayout.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum ResourceType : byte
|
||||
{
|
||||
UniformBuffer,
|
||||
StorageBuffer,
|
||||
Texture,
|
||||
Sampler,
|
||||
TextureAndSampler,
|
||||
Image,
|
||||
BufferTexture,
|
||||
BufferImage
|
||||
}
|
||||
|
||||
public enum ResourceAccess : byte
|
||||
{
|
||||
None = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
ReadWrite = Read | Write
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ResourceStages : byte
|
||||
{
|
||||
None = 0,
|
||||
Compute = 1 << 0,
|
||||
Vertex = 1 << 1,
|
||||
TessellationControl = 1 << 2,
|
||||
TessellationEvaluation = 1 << 3,
|
||||
Geometry = 1 << 4,
|
||||
Fragment = 1 << 5
|
||||
}
|
||||
|
||||
public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor>
|
||||
{
|
||||
public int Binding { get; }
|
||||
public int Count { get; }
|
||||
public ResourceType Type { get; }
|
||||
public ResourceStages Stages { get; }
|
||||
|
||||
public ResourceDescriptor(int binding, int count, ResourceType type, ResourceStages stages)
|
||||
{
|
||||
Binding = binding;
|
||||
Count = count;
|
||||
Type = type;
|
||||
Stages = stages;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Binding, Count, Type, Stages);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ResourceDescriptor other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(ResourceDescriptor other)
|
||||
{
|
||||
return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceUsage : IEquatable<ResourceUsage>
|
||||
{
|
||||
public int Binding { get; }
|
||||
public ResourceType Type { get; }
|
||||
public ResourceStages Stages { get; }
|
||||
public ResourceAccess Access { get; }
|
||||
|
||||
public ResourceUsage(int binding, ResourceType type, ResourceStages stages, ResourceAccess access)
|
||||
{
|
||||
Binding = binding;
|
||||
Type = type;
|
||||
Stages = stages;
|
||||
Access = access;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Binding, Type, Stages, Access);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ResourceUsage other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(ResourceUsage other)
|
||||
{
|
||||
return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceDescriptorCollection
|
||||
{
|
||||
public ReadOnlyCollection<ResourceDescriptor> Descriptors { get; }
|
||||
|
||||
public ResourceDescriptorCollection(ReadOnlyCollection<ResourceDescriptor> descriptors)
|
||||
{
|
||||
Descriptors = descriptors;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
HashCode hasher = new HashCode();
|
||||
|
||||
if (Descriptors != null)
|
||||
{
|
||||
foreach (var descriptor in Descriptors)
|
||||
{
|
||||
hasher.Add(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return hasher.ToHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ResourceDescriptorCollection other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(ResourceDescriptorCollection other)
|
||||
{
|
||||
if ((Descriptors == null) != (other.Descriptors == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Descriptors != null)
|
||||
{
|
||||
if (Descriptors.Count != other.Descriptors.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < Descriptors.Count; index++)
|
||||
{
|
||||
if (!Descriptors[index].Equals(other.Descriptors[index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceUsageCollection
|
||||
{
|
||||
public ReadOnlyCollection<ResourceUsage> Usages { get; }
|
||||
|
||||
public ResourceUsageCollection(ReadOnlyCollection<ResourceUsage> usages)
|
||||
{
|
||||
Usages = usages;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceLayout
|
||||
{
|
||||
public ReadOnlyCollection<ResourceDescriptorCollection> Sets { get; }
|
||||
public ReadOnlyCollection<ResourceUsageCollection> SetUsages { get; }
|
||||
|
||||
public ResourceLayout(
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> sets,
|
||||
ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||
{
|
||||
Sets = sets;
|
||||
SetUsages = setUsages;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public readonly struct ShaderBindings
|
||||
{
|
||||
public IReadOnlyCollection<int> UniformBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> StorageBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> TextureBindings { get; }
|
||||
public IReadOnlyCollection<int> ImageBindings { get; }
|
||||
|
||||
public ShaderBindings(
|
||||
IReadOnlyCollection<int> uniformBufferBindings,
|
||||
IReadOnlyCollection<int> storageBufferBindings,
|
||||
IReadOnlyCollection<int> textureBindings,
|
||||
IReadOnlyCollection<int> imageBindings)
|
||||
{
|
||||
UniformBufferBindings = uniformBufferBindings;
|
||||
StorageBufferBindings = storageBufferBindings;
|
||||
TextureBindings = textureBindings;
|
||||
ImageBindings = imageBindings;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
int binaryCodeLength = reader.ReadInt32();
|
||||
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
||||
|
||||
output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
|
||||
output.Add(new ShaderSource(binaryCode, stage, TargetLanguage.Spirv));
|
||||
}
|
||||
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
|
||||
{
|
||||
for (int i = 0; i < stages.Length; i++)
|
||||
{
|
||||
CachedShaderStage currentStage = stages[i];
|
||||
|
||||
if (currentStage?.Info != null && currentStage.Info.Stage == stage)
|
||||
{
|
||||
return ShaderCache.GetBindings(currentStage.Info);
|
||||
}
|
||||
}
|
||||
|
||||
return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
||||
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||
|
||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
||||
|
||||
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
||||
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
|
||||
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||
|
||||
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
||||
|
||||
|
@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
TranslatorContext previousStage = null;
|
||||
|
||||
ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
|
||||
|
||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||
{
|
||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||
|
@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
if (program != null)
|
||||
{
|
||||
shaderSources.Add(CreateShaderSource(program));
|
||||
infoBuilder.AddStageInfo(program.Info);
|
||||
}
|
||||
|
||||
previousStage = currentStage;
|
||||
|
@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
|
||||
|
||||
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
|
||||
ShaderInfo info = infoBuilder.Build(pipeline);
|
||||
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||
|
||||
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
|
||||
|
||||
|
@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>Shader source</returns>
|
||||
public static ShaderSource CreateShaderSource(ShaderProgram program)
|
||||
{
|
||||
return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
|
||||
return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -717,25 +720,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the bindings used by a shader program.
|
||||
/// </summary>
|
||||
/// <param name="info">Shader program information to get the information from</param>
|
||||
/// <returns>Shader bindings</returns>
|
||||
public static ShaderBindings GetBindings(ShaderProgramInfo info)
|
||||
{
|
||||
var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
|
||||
var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
|
||||
var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
|
||||
var imageBindings = info.Images.Select(x => x.Binding).ToArray();
|
||||
|
||||
return new ShaderBindings(
|
||||
uniformBufferBindings,
|
||||
storageBufferBindings,
|
||||
textureBindings,
|
||||
imageBindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates shader translation options with the requested graphics API and flags.
|
||||
/// The shader language is choosen based on the current configuration and graphics API.
|
||||
|
|
260
src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
Normal file
260
src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
Normal file
|
@ -0,0 +1,260 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
/// <summary>
|
||||
/// Shader info structure builder.
|
||||
/// </summary>
|
||||
class ShaderInfoBuilder
|
||||
{
|
||||
private const int TotalSets = 4;
|
||||
|
||||
private const int UniformSetIndex = 0;
|
||||
private const int StorageSetIndex = 1;
|
||||
private const int TextureSetIndex = 2;
|
||||
private const int ImageSetIndex = 3;
|
||||
|
||||
private const ResourceStages SupportBufferStags =
|
||||
ResourceStages.Compute |
|
||||
ResourceStages.Vertex |
|
||||
ResourceStages.Fragment;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
|
||||
private int _fragmentOutputMap;
|
||||
|
||||
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader info builder.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
|
||||
public ShaderInfoBuilder(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_fragmentOutputMap = -1;
|
||||
|
||||
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
_resourceDescriptors[index] = new();
|
||||
_resourceUsages[index] = new();
|
||||
}
|
||||
|
||||
AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds information from a given shader stage.
|
||||
/// </summary>
|
||||
/// <param name="info">Shader stage information</param>
|
||||
public void AddStageInfo(ShaderProgramInfo info)
|
||||
{
|
||||
if (info.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
_fragmentOutputMap = info.FragmentOutputMap;
|
||||
}
|
||||
|
||||
int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
|
||||
{
|
||||
ShaderStage.TessellationControl => 1,
|
||||
ShaderStage.TessellationEvaluation => 2,
|
||||
ShaderStage.Geometry => 3,
|
||||
ShaderStage.Fragment => 4,
|
||||
_ => 0
|
||||
});
|
||||
|
||||
ResourceStages stages = info.Stage switch
|
||||
{
|
||||
ShaderStage.Compute => ResourceStages.Compute,
|
||||
ShaderStage.Vertex => ResourceStages.Vertex,
|
||||
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
|
||||
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
|
||||
ShaderStage.Geometry => ResourceStages.Geometry,
|
||||
ShaderStage.Fragment => ResourceStages.Fragment,
|
||||
_ => ResourceStages.None
|
||||
};
|
||||
|
||||
int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
|
||||
int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
|
||||
int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
|
||||
int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
|
||||
|
||||
int uniformBinding = 1 + stageIndex * uniformsPerStage;
|
||||
int storageBinding = stageIndex * storagesPerStage;
|
||||
int textureBinding = stageIndex * texturesPerStage * 2;
|
||||
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||
|
||||
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
|
||||
AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
|
||||
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
|
||||
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
|
||||
|
||||
AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
|
||||
AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
|
||||
AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
|
||||
AddUsage(info.Images, stages, ImageSetIndex, isImage: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a resource descriptor to the list of descriptors.
|
||||
/// </summary>
|
||||
/// <param name="stages">Shader stages where the resource is used</param>
|
||||
/// <param name="type">Type of the resource</param>
|
||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||
/// <param name="count">Number of resources bound at the binding location</param>
|
||||
private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
||||
{
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two interleaved groups of resources to the list of descriptors.
|
||||
/// </summary>
|
||||
/// <param name="stages">Shader stages where the resource is used</param>
|
||||
/// <param name="type">Type of the first interleaved resource</param>
|
||||
/// <param name="type2">Type of the second interleaved resource</param>
|
||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||
/// <param name="count">Number of resources bound at the binding location</param>
|
||||
private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
|
||||
{
|
||||
AddDescriptor(stages, type, setIndex, binding, count);
|
||||
AddDescriptor(stages, type2, setIndex, binding + count, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an array resource to the list of descriptors.
|
||||
/// </summary>
|
||||
/// <param name="stages">Shader stages where the resource is used</param>
|
||||
/// <param name="type">Type of the resource</param>
|
||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||
/// <param name="count">Number of resources bound at the binding location</param>
|
||||
private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
||||
{
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds buffer usage information to the list of usages.
|
||||
/// </summary>
|
||||
/// <param name="buffers">Buffers to be added</param>
|
||||
/// <param name="stages">Stages where the buffers are used</param>
|
||||
/// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
|
||||
/// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
|
||||
private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage)
|
||||
{
|
||||
foreach (BufferDescriptor buffer in buffers)
|
||||
{
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(
|
||||
buffer.Binding,
|
||||
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
|
||||
stages,
|
||||
buffer.Flags.HasFlag(BufferUsageFlags.Write) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds texture usage information to the list of usages.
|
||||
/// </summary>
|
||||
/// <param name="textures">Textures to be added</param>
|
||||
/// <param name="stages">Stages where the textures are used</param>
|
||||
/// <param name="setIndex">Descriptor set index where the textures will be bound</param>
|
||||
/// <param name="isImage">True for images, false for textures</param>
|
||||
private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
|
||||
{
|
||||
foreach (TextureDescriptor texture in textures)
|
||||
{
|
||||
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||
|
||||
ResourceType type = isBuffer
|
||||
? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
|
||||
: (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
|
||||
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(
|
||||
texture.Binding,
|
||||
type,
|
||||
stages,
|
||||
texture.Flags.HasFlag(TextureUsageFlags.ImageStore) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader information structure from the added information.
|
||||
/// </summary>
|
||||
/// <param name="pipeline">Optional pipeline state for background shader compilation</param>
|
||||
/// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
|
||||
/// <returns>Shader information</returns>
|
||||
public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
|
||||
{
|
||||
var descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||
var usages = new ResourceUsageCollection[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||
}
|
||||
|
||||
ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||
|
||||
if (pipeline.HasValue)
|
||||
{
|
||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds shader information for shaders from the disk cache.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that owns the shaders</param>
|
||||
/// <param name="programs">Shaders from the disk cache</param>
|
||||
/// <param name="pipeline">Optional pipeline for background compilation</param>
|
||||
/// <returns>Shader information</returns>
|
||||
public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline)
|
||||
{
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||
|
||||
foreach (CachedShaderStage program in programs)
|
||||
{
|
||||
if (program?.Info != null)
|
||||
{
|
||||
builder.AddStageInfo(program.Info);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.Build(pipeline, fromCache: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds shader information for a compute shader.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that owns the shader</param>
|
||||
/// <param name="info">Compute shader information</param>
|
||||
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
|
||||
/// <returns>Shader information</returns>
|
||||
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
|
||||
{
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||
|
||||
builder.AddStageInfo(info);
|
||||
|
||||
return builder.Build(null, fromCache);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,21 +411,10 @@ 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;
|
||||
|
||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
||||
{
|
||||
int binding = stageBindings[bindingIndex];
|
||||
count = 1;
|
||||
|
||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
if (setIndex == PipelineBase.UniformSetIndex)
|
||||
{
|
||||
|
@ -469,7 +459,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
Span<DescriptorImageInfo> textures = _textures;
|
||||
|
||||
|
@ -507,7 +497,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||
{
|
||||
if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout)
|
||||
if (segment.Type != ResourceType.BufferImage)
|
||||
{
|
||||
Span<DescriptorImageInfo> images = _images;
|
||||
|
||||
|
@ -531,7 +521,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sets = dsc.GetSets();
|
||||
|
||||
|
@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||
{
|
||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||
int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length;
|
||||
|
||||
if (!_uniformSet[0])
|
||||
{
|
||||
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
||||
|
@ -587,21 +573,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
||||
}
|
||||
|
||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
||||
{
|
||||
var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex];
|
||||
int bindingsCount = stageBindings.Length;
|
||||
int count;
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||
|
||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = stageBindings[bindingIndex];
|
||||
count = 1;
|
||||
|
||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
bool doUpdate = false;
|
||||
|
||||
|
@ -624,7 +602,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
|
||||
|
|
|
@ -54,29 +54,29 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
||||
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
||||
|
||||
var computeBindings = new ShaderBindings(
|
||||
new[] { 2 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 1 },
|
||||
new[] { 0 });
|
||||
var 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<int>(),
|
||||
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);
|
||||
|
|
|
@ -38,18 +38,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||
|
||||
var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
|
||||
|
||||
var computeBindings = new ShaderBindings(
|
||||
new[] { 2 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 1 },
|
||||
new[] { 0 });
|
||||
var resourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||
|
||||
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||
|
||||
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||
});
|
||||
new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||
}, resourceLayout);
|
||||
}
|
||||
|
||||
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
|
||||
|
|
|
@ -77,23 +77,23 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||
var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
|
||||
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
||||
|
||||
var edgeBindings = new ShaderBindings(
|
||||
new[] { 2 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 1 },
|
||||
new[] { 0 });
|
||||
var 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<int>(),
|
||||
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<int>(),
|
||||
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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||
|
||||
var blitVertexBindings = new ShaderBindings(
|
||||
new[] { 1 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var blitFragmentBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
Array.Empty<int>());
|
||||
var blitResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
|
||||
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||
|
||||
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
var colorClearFragmentBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
|
||||
|
||||
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorClearResourceLayout);
|
||||
|
||||
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorClearResourceLayout);
|
||||
|
||||
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorClearResourceLayout);
|
||||
|
||||
var strideChangeBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||
|
||||
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, strideChangeResourceLayout);
|
||||
|
||||
var colorCopyBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
new[] { 0 });
|
||||
var colorCopyResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||
|
||||
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, colorCopyResourceLayout);
|
||||
|
||||
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, colorCopyResourceLayout);
|
||||
|
||||
_programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, colorCopyResourceLayout);
|
||||
|
||||
var colorDrawToMsVertexBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var colorDrawToMsFragmentBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
Array.Empty<int>());
|
||||
var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||
|
||||
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorDrawToMsResourceLayout);
|
||||
|
||||
var convertD32S8ToD24S8Bindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||
|
||||
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, convertD32S8ToD24S8ResourceLayout);
|
||||
|
||||
var convertIndexBufferBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||
|
||||
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, convertIndexBufferResourceLayout);
|
||||
|
||||
var convertIndirectDataBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2, 3 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
||||
|
||||
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
}, convertIndirectDataResourceLayout);
|
||||
|
||||
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorDrawToMsResourceLayout);
|
||||
|
||||
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorDrawToMsResourceLayout);
|
||||
|
||||
if (gd.Capabilities.SupportsShaderStencilExport)
|
||||
{
|
||||
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, blitResourceLayout);
|
||||
|
||||
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorDrawToMsResourceLayout);
|
||||
|
||||
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
}, colorDrawToMsResourceLayout);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,52 +1,101 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class PipelineLayoutCache
|
||||
{
|
||||
private readonly PipelineLayoutCacheEntry[] _plce;
|
||||
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
|
||||
private readonly struct PlceKey : IEquatable<PlceKey>
|
||||
{
|
||||
public readonly ReadOnlyCollection<ResourceDescriptorCollection> SetDescriptors;
|
||||
public readonly bool UsePushDescriptors;
|
||||
|
||||
public PlceKey(ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, bool usePushDescriptors)
|
||||
{
|
||||
SetDescriptors = setDescriptors;
|
||||
UsePushDescriptors = usePushDescriptors;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
HashCode hasher = new HashCode();
|
||||
|
||||
if (SetDescriptors != null)
|
||||
{
|
||||
foreach (var setDescriptor in SetDescriptors)
|
||||
{
|
||||
hasher.Add(setDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
hasher.Add(UsePushDescriptors);
|
||||
|
||||
return hasher.ToHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is PlceKey other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(PlceKey other)
|
||||
{
|
||||
if ((SetDescriptors == null) != (other.SetDescriptors == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SetDescriptors != null)
|
||||
{
|
||||
if (SetDescriptors.Count != other.SetDescriptors.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < SetDescriptors.Count; index++)
|
||||
{
|
||||
if (!SetDescriptors[index].Equals(other.SetDescriptors[index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UsePushDescriptors == other.UsePushDescriptors;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry> _plces;
|
||||
|
||||
public PipelineLayoutCache()
|
||||
{
|
||||
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
|
||||
_plceMinimal = new List<PipelineLayoutCacheEntry>();
|
||||
_plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>();
|
||||
}
|
||||
|
||||
public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
|
||||
public PipelineLayoutCacheEntry GetOrCreate(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
|
@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
private readonly int[] _dsCacheCursor;
|
||||
private int _dsLastCbIndex;
|
||||
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device)
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
||||
{
|
||||
_dsCache[i] = new List<Auto<DescriptorSetCollection>>[PipelineBase.DescriptorSetLayouts];
|
||||
_dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount];
|
||||
|
||||
for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++)
|
||||
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||
{
|
||||
_dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
|
||||
}
|
||||
}
|
||||
|
||||
_dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts];
|
||||
_dsCacheCursor = new int[setsCount];
|
||||
}
|
||||
|
||||
public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device)
|
||||
public PipelineLayoutCacheEntry(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> 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<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
||||
|
@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
_dsLastCbIndex = commandBufferIndex;
|
||||
|
||||
for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++)
|
||||
for (int i = 0; i < _dsCacheCursor.Length; i++)
|
||||
{
|
||||
_dsCacheCursor[i] = 0;
|
||||
}
|
||||
|
|
|
@ -1,153 +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<ResourceDescriptorCollection> 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)
|
||||
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
|
||||
|
||||
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||
{
|
||||
ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
|
||||
|
||||
ResourceStages stages = descriptor.Stages;
|
||||
|
||||
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
|
||||
{
|
||||
// There's a bug on MoltenVK where using the same buffer across different stages
|
||||
// causes invalid resource errors, allow the binding on all active stages as workaround.
|
||||
var flags = gd.IsMoltenVk ? activeStages : stageFlags;
|
||||
stages = activeStages;
|
||||
}
|
||||
|
||||
bindings[start + iter] = new DescriptorSetLayoutBinding
|
||||
layoutBindings[descIndex] = new DescriptorSetLayoutBinding()
|
||||
{
|
||||
Binding = (uint)(start + stage * maxPerStage),
|
||||
DescriptorType = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = (uint)maxPerStage,
|
||||
StageFlags = flags
|
||||
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);
|
||||
fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
|
||||
{
|
||||
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = pLayoutBindings,
|
||||
BindingCount = (uint)layoutBindings.Length,
|
||||
Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None
|
||||
};
|
||||
|
||||
iter++;
|
||||
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,
|
||||
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();
|
||||
PipelineLayout layout;
|
||||
|
||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||
{
|
||||
|
@ -155,117 +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;
|
||||
}
|
||||
|
||||
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<int> bds)
|
||||
{
|
||||
foreach (var b in bds)
|
||||
{
|
||||
bindings[start++] = new DescriptorSetLayoutBinding
|
||||
{
|
||||
Binding = (uint)b,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
StageFlags = stageFlags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||
{
|
||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.PipelineLayoutCreateInfo,
|
||||
PSetLayouts = pLayouts,
|
||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
||||
};
|
||||
|
||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
||||
}
|
||||
|
||||
return layouts;
|
||||
return (layouts, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
Normal file
22
src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
67
src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
Normal file
67
src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class ResourceLayoutBuilder
|
||||
{
|
||||
private const int TotalSets = PipelineBase.DescriptorSetLayouts;
|
||||
|
||||
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||
|
||||
public ResourceLayoutBuilder()
|
||||
{
|
||||
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
_resourceDescriptors[index] = new();
|
||||
_resourceUsages[index] = new();
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
|
||||
{
|
||||
int setIndex = type switch
|
||||
{
|
||||
ResourceType.UniformBuffer => PipelineBase.UniformSetIndex,
|
||||
ResourceType.StorageBuffer => PipelineBase.StorageSetIndex,
|
||||
ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex,
|
||||
ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex,
|
||||
_ => throw new ArgumentException($"Invalid resource type \"{type}\".")
|
||||
};
|
||||
|
||||
ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite;
|
||||
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages, access));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static bool IsReadOnlyType(ResourceType type)
|
||||
{
|
||||
return type == ResourceType.UniformBuffer ||
|
||||
type == ResourceType.Sampler ||
|
||||
type == ResourceType.TextureAndSampler ||
|
||||
type == ResourceType.BufferTexture;
|
||||
}
|
||||
|
||||
public ResourceLayout Build()
|
||||
{
|
||||
var descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||
var usages = new ResourceUsageCollection[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||
}
|
||||
|
||||
return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
public uint Stages { get; }
|
||||
|
||||
public int[][][] Bindings { get; }
|
||||
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||
|
||||
public ProgramLinkStatus LinkStatus { get; private set; }
|
||||
|
||||
|
@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
private Task _compileTask;
|
||||
private bool _firstBackgroundUse;
|
||||
|
||||
public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
|
||||
public ShaderCollection(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ShaderSource[] shaders,
|
||||
ResourceLayout resourceLayout,
|
||||
SpecDescription[] specDescription = null,
|
||||
bool isMinimal = false)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
_shaders = internalShaders;
|
||||
|
||||
bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||
|
||||
_plce = isMinimal
|
||||
? gd.PipelineLayoutCache.Create(gd, device, shaders)
|
||||
: gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
|
||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
|
||||
|
||||
HasMinimalLayout = isMinimal;
|
||||
UsePushDescriptors = usePd;
|
||||
UsePushDescriptors = usePushDescriptors;
|
||||
|
||||
Stages = stages;
|
||||
|
||||
int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
|
||||
{
|
||||
bool hasAny = false;
|
||||
int[][] bindings = new int[internalShaders.Length][];
|
||||
|
||||
for (int i = 0; i < internalShaders.Length; i++)
|
||||
{
|
||||
var collection = selector(internalShaders[i].Bindings);
|
||||
hasAny |= collection.Count != 0;
|
||||
bindings[i] = collection.ToArray();
|
||||
}
|
||||
|
||||
return hasAny ? bindings : Array.Empty<int[]>();
|
||||
}
|
||||
|
||||
Bindings = new[]
|
||||
{
|
||||
GrabAll(x => x.UniformBufferBindings),
|
||||
GrabAll(x => x.StorageBufferBindings),
|
||||
GrabAll(x => x.TextureBindings),
|
||||
GrabAll(x => x.ImageBindings)
|
||||
};
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
|
||||
_compileTask = Task.CompletedTask;
|
||||
_firstBackgroundUse = false;
|
||||
|
@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ShaderSource[] sources,
|
||||
ResourceLayout resourceLayout,
|
||||
ProgramPipelineState state,
|
||||
bool fromCache) : this(gd, device, sources)
|
||||
bool fromCache) : this(gd, device, sources, resourceLayout)
|
||||
{
|
||||
_state = state;
|
||||
|
||||
|
@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_firstBackgroundUse = !fromCache;
|
||||
}
|
||||
|
||||
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||
|
||||
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||
{
|
||||
List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>();
|
||||
|
||||
ResourceUsage currentUsage = default;
|
||||
int currentCount = 0;
|
||||
|
||||
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
|
||||
{
|
||||
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||
|
||||
// If the resource is not accessed, we don't need to update it.
|
||||
if (usage.Access == ResourceAccess.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||
currentUsage.Type != usage.Type ||
|
||||
currentUsage.Stages != usage.Stages ||
|
||||
currentUsage.Access != usage.Access)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.Access));
|
||||
}
|
||||
|
||||
currentUsage = usage;
|
||||
currentCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.Access));
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
private async Task BackgroundCompilation()
|
||||
{
|
||||
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Reference in a new issue