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 struct ShaderInfo
|
||||||
{
|
{
|
||||||
public int FragmentOutputMap { get; }
|
public int FragmentOutputMap { get; }
|
||||||
|
public ResourceLayout ResourceLayout { get; }
|
||||||
public ProgramPipelineState? State { get; }
|
public ProgramPipelineState? State { get; }
|
||||||
public bool FromCache { get; set; }
|
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;
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
|
ResourceLayout = resourceLayout;
|
||||||
State = state;
|
State = state;
|
||||||
FromCache = fromCache;
|
FromCache = fromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderInfo(int fragmentOutputMap, bool fromCache = false)
|
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
|
||||||
{
|
{
|
||||||
FragmentOutputMap = fragmentOutputMap;
|
FragmentOutputMap = fragmentOutputMap;
|
||||||
|
ResourceLayout = resourceLayout;
|
||||||
State = null;
|
State = null;
|
||||||
FromCache = fromCache;
|
FromCache = fromCache;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,24 +7,22 @@ namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
public byte[] BinaryCode { get; }
|
public byte[] BinaryCode { get; }
|
||||||
public ShaderBindings Bindings { get; }
|
|
||||||
public ShaderStage Stage { get; }
|
public ShaderStage Stage { get; }
|
||||||
public TargetLanguage Language { 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;
|
Code = code;
|
||||||
BinaryCode = binaryCode;
|
BinaryCode = binaryCode;
|
||||||
Bindings = bindings;
|
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Language = language;
|
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)
|
if (hostCode != null)
|
||||||
{
|
{
|
||||||
bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
|
ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState);
|
||||||
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);
|
|
||||||
|
|
||||||
IProgram hostProgram;
|
IProgram hostProgram;
|
||||||
|
|
||||||
|
@ -385,6 +380,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
|
||||||
|
|
||||||
hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
|
hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -491,23 +491,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
{
|
{
|
||||||
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
||||||
|
|
||||||
int fragmentOutputMap = -1;
|
ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context);
|
||||||
|
|
||||||
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
||||||
{
|
{
|
||||||
ShaderProgram shader = compilation.TranslatedStages[index];
|
ShaderProgram shader = compilation.TranslatedStages[index];
|
||||||
shaderSources[index] = CreateShaderSource(shader);
|
shaderSources[index] = CreateShaderSource(shader);
|
||||||
|
shaderInfoBuilder.AddStageInfo(shader.Info);
|
||||||
if (shader.Info.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
fragmentOutputMap = shader.Info.FragmentOutputMap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue
|
ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true);
|
||||||
? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true)
|
|
||||||
: new ShaderInfo(fragmentOutputMap, fromCache: true);
|
|
||||||
|
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
||||||
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
|
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
|
||||||
|
|
||||||
|
|
|
@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
int binaryCodeLength = reader.ReadInt32();
|
int binaryCodeLength = reader.ReadInt32();
|
||||||
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
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();
|
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}.");
|
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
|
// This is just a simple remapping to ensure that most frequently used shader stages
|
||||||
// have the lowest binding numbers.
|
// have the lowest binding numbers.
|
||||||
// This is useful because if we need to run on a system with a low limit on the bindings,
|
// 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.
|
// 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
|
4 => 1, // Fragment
|
||||||
3 => 2, // Geometry
|
3 => 2, // Geometry
|
||||||
|
|
|
@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
||||||
|
|
||||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||||
|
|
||||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
||||||
|
|
||||||
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
||||||
|
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||||
|
|
||||||
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
||||||
|
|
||||||
|
@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
TranslatorContext previousStage = null;
|
TranslatorContext previousStage = null;
|
||||||
|
|
||||||
|
ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||||
{
|
{
|
||||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||||
|
@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
if (program != null)
|
if (program != null)
|
||||||
{
|
{
|
||||||
shaderSources.Add(CreateShaderSource(program));
|
shaderSources.Add(CreateShaderSource(program));
|
||||||
|
infoBuilder.AddStageInfo(program.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
previousStage = currentStage;
|
previousStage = currentStage;
|
||||||
|
@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
|
ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
|
||||||
|
|
||||||
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
|
ShaderInfo info = infoBuilder.Build(pipeline);
|
||||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
|
|
||||||
|
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||||
|
|
||||||
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
|
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
|
||||||
|
|
||||||
|
@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <returns>Shader source</returns>
|
/// <returns>Shader source</returns>
|
||||||
public static ShaderSource CreateShaderSource(ShaderProgram program)
|
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>
|
/// <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>
|
/// <summary>
|
||||||
/// Creates shader translation options with the requested graphics API and flags.
|
/// Creates shader translation options with the requested graphics API and flags.
|
||||||
/// The shader language is choosen based on the current configuration and graphics API.
|
/// 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)
|
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
|
||||||
{
|
{
|
||||||
var program = _program;
|
var program = _program;
|
||||||
int stagesCount = program.Bindings[setIndex].Length;
|
var bindingSegments = program.BindingSegments[setIndex];
|
||||||
if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex)
|
|
||||||
|
if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -410,125 +411,113 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
{
|
||||||
var stageBindings = program.Bindings[setIndex][stageIndex];
|
int binding = segment.Binding;
|
||||||
int bindingsCount = stageBindings.Length;
|
int count = segment.Count;
|
||||||
int count;
|
|
||||||
|
|
||||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
if (setIndex == PipelineBase.UniformSetIndex)
|
||||||
{
|
{
|
||||||
int binding = stageBindings[bindingIndex];
|
for (int i = 0; i < count; i++)
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
|
||||||
{
|
{
|
||||||
count++;
|
int index = binding + i;
|
||||||
|
|
||||||
|
if (!_uniformSet[index])
|
||||||
|
{
|
||||||
|
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
||||||
|
|
||||||
|
_uniformSet[index] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setIndex == PipelineBase.UniformSetIndex)
|
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||||
|
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
int index = binding + i;
|
||||||
|
|
||||||
|
if (!_storageSet[index])
|
||||||
|
{
|
||||||
|
UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
|
||||||
|
|
||||||
|
_storageSet[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||||
|
if (program.HasMinimalLayout)
|
||||||
|
{
|
||||||
|
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferTexture)
|
||||||
|
{
|
||||||
|
Span<DescriptorImageInfo> textures = _textures;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int index = binding + i;
|
ref var texture = ref textures[i];
|
||||||
|
|
||||||
if (!_uniformSet[index])
|
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
|
if (texture.ImageView.Handle == 0)
|
||||||
{
|
{
|
||||||
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||||
|
}
|
||||||
|
|
||||||
_uniformSet[index] = true;
|
if (texture.Sampler.Handle == 0)
|
||||||
|
{
|
||||||
|
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
|
||||||
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
else
|
||||||
{
|
{
|
||||||
|
Span<BufferView> bufferTextures = _bufferTextures;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int index = binding + i;
|
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
||||||
|
|
||||||
if (!_storageSet[index])
|
|
||||||
{
|
|
||||||
UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
|
|
||||||
|
|
||||||
_storageSet[index] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
|
||||||
if (program.HasMinimalLayout)
|
|
||||||
{
|
|
||||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
}
|
||||||
|
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||||
|
{
|
||||||
|
if (segment.Type != ResourceType.BufferImage)
|
||||||
{
|
{
|
||||||
if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)
|
Span<DescriptorImageInfo> images = _images;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Span<DescriptorImageInfo> textures = _textures;
|
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
ref var texture = ref textures[i];
|
|
||||||
|
|
||||||
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
|
||||||
{
|
|
||||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.Sampler.Handle == 0)
|
|
||||||
{
|
|
||||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<BufferView> bufferTextures = _bufferTextures;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
|
||||||
{
|
|
||||||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
else
|
||||||
{
|
{
|
||||||
if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout)
|
Span<BufferView> bufferImages = _bufferImages;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Span<DescriptorImageInfo> images = _images;
|
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<BufferView> bufferImages = _bufferImages;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
|
||||||
{
|
|
||||||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||||
{
|
{
|
||||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
|
||||||
int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length;
|
|
||||||
|
|
||||||
if (!_uniformSet[0])
|
if (!_uniformSet[0])
|
||||||
{
|
{
|
||||||
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
||||||
|
@ -587,41 +573,32 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||||
|
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||||
|
|
||||||
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
{
|
||||||
var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex];
|
int binding = segment.Binding;
|
||||||
int bindingsCount = stageBindings.Length;
|
int count = segment.Count;
|
||||||
int count;
|
|
||||||
|
|
||||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
bool doUpdate = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
int binding = stageBindings[bindingIndex];
|
int index = binding + i;
|
||||||
count = 1;
|
|
||||||
|
|
||||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
if (!_uniformSet[index])
|
||||||
{
|
{
|
||||||
count++;
|
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
||||||
|
_uniformSet[index] = true;
|
||||||
|
doUpdate = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool doUpdate = false;
|
if (doUpdate)
|
||||||
|
{
|
||||||
for (int i = 0; i < count; i++)
|
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||||
{
|
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||||
int index = binding + i;
|
|
||||||
|
|
||||||
if (!_uniformSet[index])
|
|
||||||
{
|
|
||||||
UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
|
|
||||||
_uniformSet[index] = true;
|
|
||||||
doUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doUpdate)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
|
||||||
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,29 +54,29 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
||||||
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
||||||
|
|
||||||
var computeBindings = new ShaderBindings(
|
var scalingResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
var sharpeningBindings = new ShaderBindings(
|
var sharpeningResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2, 3, 4 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
});
|
}, scalingResourceLayout);
|
||||||
|
|
||||||
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
});
|
}, sharpeningResourceLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run(
|
public void Run(
|
||||||
|
@ -160,10 +160,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
// Sharpening pass
|
// Sharpening pass
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
|
||||||
_pipeline.SetProgram(_sharpeningProgram);
|
_pipeline.SetProgram(_sharpeningProgram);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
|
||||||
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
|
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
|
||||||
_pipeline.SetImage(0, destinationTexture);
|
_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 shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
|
||||||
|
|
||||||
var computeBindings = new ShaderBindings(
|
var resourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_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)
|
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 blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
|
||||||
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
||||||
|
|
||||||
var edgeBindings = new ShaderBindings(
|
var edgeResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
var blendBindings = new ShaderBindings(
|
var blendResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1, 3, 4 },
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
|
||||||
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
var neighbourBindings = new ShaderBindings(
|
var neighbourResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 2 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
||||||
new[] { 1, 3 },
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
|
||||||
new[] { 0 });
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
|
|
||||||
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
@ -117,18 +117,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
|
||||||
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, edgeResourceLayout, new[] { specInfo });
|
||||||
|
|
||||||
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, blendResourceLayout, new[] { specInfo });
|
||||||
|
|
||||||
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
}, new[] { specInfo });
|
}, neighbourResourceLayout, new[] { specInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeletePipelines()
|
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)
|
public static SamplerAddressMode Convert(this AddressMode mode)
|
||||||
{
|
{
|
||||||
return mode switch
|
return mode switch
|
||||||
|
@ -48,7 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
|
AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
|
||||||
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
|
AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
|
||||||
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
|
AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
|
||||||
_ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
|
_ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||||
|
|
||||||
var blitVertexBindings = new ShaderBindings(
|
var blitResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 1 },
|
.Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
var blitFragmentBindings = new ShaderBindings(
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
var colorClearFragmentBindings = new ShaderBindings(
|
var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
var strideChangeBindings = new ShaderBindings(
|
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
_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(
|
var colorCopyResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
|
||||||
new[] { 0 });
|
|
||||||
|
|
||||||
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
|
_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[]
|
_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[]
|
_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(
|
var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||||
Array.Empty<int>(),
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
var colorDrawToMsFragmentBindings = new ShaderBindings(
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>(),
|
|
||||||
new[] { 0 },
|
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
var convertD32S8ToD24S8Bindings = new ShaderBindings(
|
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
|
_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(
|
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||||
Array.Empty<int>());
|
|
||||||
|
|
||||||
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
_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(
|
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
|
||||||
new[] { 0 },
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
new[] { 1, 2, 3 },
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
Array.Empty<int>(),
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
|
||||||
Array.Empty<int>());
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
||||||
|
|
||||||
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
_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[]
|
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsShaderStencilExport)
|
if (gd.Capabilities.SupportsShaderStencilExport)
|
||||||
{
|
{
|
||||||
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, blitResourceLayout);
|
||||||
|
|
||||||
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
|
|
||||||
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
_programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
{
|
{
|
||||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
});
|
}, colorDrawToMsResourceLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,101 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
class PipelineLayoutCache
|
class PipelineLayoutCache
|
||||||
{
|
{
|
||||||
private readonly PipelineLayoutCacheEntry[] _plce;
|
private readonly struct PlceKey : IEquatable<PlceKey>
|
||||||
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
|
{
|
||||||
|
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()
|
public PipelineLayoutCache()
|
||||||
{
|
{
|
||||||
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
|
_plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>();
|
||||||
_plceMinimal = new List<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);
|
var key = new PlceKey(setDescriptors, usePushDescriptors);
|
||||||
_plceMinimal.Add(plce);
|
|
||||||
return plce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
|
return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors));
|
||||||
{
|
|
||||||
if (_plce[stages] == null)
|
|
||||||
{
|
|
||||||
_plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _plce[stages];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual unsafe void Dispose(bool disposing)
|
protected virtual unsafe void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _plce.Length; i++)
|
foreach (var plce in _plces.Values)
|
||||||
{
|
|
||||||
_plce[i]?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var plce in _plceMinimal)
|
|
||||||
{
|
{
|
||||||
plce.Dispose();
|
plce.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_plceMinimal.Clear();
|
_plces.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly int[] _dsCacheCursor;
|
private readonly int[] _dsCacheCursor;
|
||||||
private int _dsLastCbIndex;
|
private int _dsLastCbIndex;
|
||||||
|
|
||||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device)
|
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
|
@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
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>>();
|
_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);
|
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||||
PipelineLayout = pipelineLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry(VulkanRenderer 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(
|
||||||
|
@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_dsLastCbIndex = commandBufferIndex;
|
_dsLastCbIndex = commandBufferIndex;
|
||||||
|
|
||||||
for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++)
|
for (int i = 0; i < _dsCacheCursor.Length; i++)
|
||||||
{
|
{
|
||||||
_dsCacheCursor[i] = 0;
|
_dsCacheCursor[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,257 +1,74 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
static class PipelineLayoutFactory
|
static class PipelineLayoutFactory
|
||||||
{
|
{
|
||||||
private const ShaderStageFlags SupportBufferStages =
|
public static unsafe (DescriptorSetLayout[], PipelineLayout) Create(
|
||||||
ShaderStageFlags.VertexBit |
|
VulkanRenderer gd,
|
||||||
ShaderStageFlags.FragmentBit |
|
Device device,
|
||||||
ShaderStageFlags.ComputeBit;
|
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||||
|
bool usePushDescriptors)
|
||||||
private static ShaderStageFlags ActiveStages(uint stages)
|
|
||||||
{
|
{
|
||||||
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);
|
ResourceDescriptorCollection rdc = setDescriptors[setIndex];
|
||||||
stages &= ~(1u << stage);
|
|
||||||
|
|
||||||
stageFlags |= stage switch
|
ResourceStages activeStages = ResourceStages.None;
|
||||||
|
|
||||||
|
if (isMoltenVk)
|
||||||
{
|
{
|
||||||
1 => ShaderStageFlags.FragmentBit,
|
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||||
2 => ShaderStageFlags.GeometryBit,
|
|
||||||
3 => ShaderStageFlags.TessellationControlBit,
|
|
||||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
|
||||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return stageFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = BitOperations.PopCount(stages);
|
|
||||||
|
|
||||||
int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
|
|
||||||
int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
|
|
||||||
int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
uLayoutBindings[0] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = 0,
|
|
||||||
DescriptorType = DescriptorType.UniformBuffer,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = SupportBufferStages
|
|
||||||
};
|
|
||||||
|
|
||||||
int iter = 0;
|
|
||||||
var activeStages = ActiveStages(stages);
|
|
||||||
|
|
||||||
while (stages != 0)
|
|
||||||
{
|
|
||||||
int stage = BitOperations.TrailingZeroCount(stages);
|
|
||||||
stages &= ~(1u << stage);
|
|
||||||
|
|
||||||
var stageFlags = stage switch
|
|
||||||
{
|
|
||||||
1 => ShaderStageFlags.FragmentBit,
|
|
||||||
2 => ShaderStageFlags.GeometryBit,
|
|
||||||
3 => ShaderStageFlags.TessellationControlBit,
|
|
||||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
|
||||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
|
||||||
};
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
|
||||||
{
|
|
||||||
int totalPerStage = maxPerStage * skip;
|
|
||||||
|
|
||||||
for (int i = 0; i < maxPerStage; i++)
|
|
||||||
{
|
{
|
||||||
bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
|
activeStages |= rdc.Descriptors[descIndex].Stages;
|
||||||
{
|
|
||||||
Binding = (uint)(start + stage * totalPerStage + i),
|
|
||||||
DescriptorType = type,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
|
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
|
||||||
{
|
|
||||||
// There's a bug on MoltenVK where using the same buffer across different stages
|
|
||||||
// causes invalid resource errors, allow the binding on all active stages as workaround.
|
|
||||||
var flags = gd.IsMoltenVk ? activeStages : stageFlags;
|
|
||||||
|
|
||||||
bindings[start + iter] = new DescriptorSetLayoutBinding
|
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
|
||||||
|
{
|
||||||
|
ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
|
||||||
|
|
||||||
|
ResourceStages stages = descriptor.Stages;
|
||||||
|
|
||||||
|
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
|
||||||
{
|
{
|
||||||
Binding = (uint)(start + stage * maxPerStage),
|
// There's a bug on MoltenVK where using the same buffer across different stages
|
||||||
DescriptorType = DescriptorType.StorageBuffer,
|
// causes invalid resource errors, allow the binding on all active stages as workaround.
|
||||||
DescriptorCount = (uint)maxPerStage,
|
stages = activeStages;
|
||||||
StageFlags = flags
|
}
|
||||||
|
|
||||||
|
layoutBindings[descIndex] = new DescriptorSetLayoutBinding()
|
||||||
|
{
|
||||||
|
Binding = (uint)descriptor.Binding,
|
||||||
|
DescriptorType = descriptor.Type.Convert(),
|
||||||
|
DescriptorCount = (uint)descriptor.Count,
|
||||||
|
StageFlags = stages.Convert()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
|
fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
|
||||||
SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
|
|
||||||
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
|
||||||
|
|
||||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = uLayoutBindings,
|
|
||||||
BindingCount = (uint)uCount,
|
|
||||||
Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = sLayoutBindings,
|
|
||||||
BindingCount = (uint)stagesCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = tLayoutBindings,
|
|
||||||
BindingCount = (uint)tCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = iLayoutBindings,
|
|
||||||
BindingCount = (uint)iCount
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
|
||||||
{
|
|
||||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||||
PSetLayouts = pLayouts,
|
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layouts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = shaders.Length;
|
|
||||||
|
|
||||||
int uCount = 0;
|
|
||||||
int sCount = 0;
|
|
||||||
int tCount = 0;
|
|
||||||
int iCount = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
uCount += shader.Bindings.UniformBufferBindings.Count;
|
|
||||||
sCount += shader.Bindings.StorageBufferBindings.Count;
|
|
||||||
tCount += shader.Bindings.TextureBindings.Count;
|
|
||||||
iCount += shader.Bindings.ImageBindings.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
int uIndex = 0;
|
|
||||||
int sIndex = 0;
|
|
||||||
int tIndex = 0;
|
|
||||||
int iIndex = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
var stageFlags = shader.Stage.Convert();
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
|
|
||||||
{
|
|
||||||
foreach (var b in bds)
|
|
||||||
{
|
{
|
||||||
bindings[start++] = new DescriptorSetLayoutBinding
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
{
|
PBindings = pLayoutBindings,
|
||||||
Binding = (uint)b,
|
BindingCount = (uint)layoutBindings.Length,
|
||||||
DescriptorType = type,
|
Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None
|
||||||
DescriptorCount = 1,
|
};
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support buffer textures and images here.
|
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
|
||||||
// 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];
|
PipelineLayout layout;
|
||||||
|
|
||||||
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)
|
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||||
{
|
{
|
||||||
|
@ -259,13 +76,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
SType = StructureType.PipelineLayoutCreateInfo,
|
||||||
PSetLayouts = pLayouts,
|
PSetLayouts = pLayouts,
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
SetLayoutCount = (uint)layouts.Length
|
||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
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 ShaderStageFlags StageFlags => _stage;
|
||||||
|
|
||||||
public ShaderBindings Bindings { get; }
|
|
||||||
|
|
||||||
public ProgramLinkStatus CompileStatus { private set; get; }
|
public ProgramLinkStatus CompileStatus { private set; get; }
|
||||||
|
|
||||||
public readonly Task CompileTask;
|
public readonly Task CompileTask;
|
||||||
|
@ -36,7 +34,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
Bindings = shaderSource.Bindings;
|
|
||||||
|
|
||||||
CompileStatus = ProgramLinkStatus.Incomplete;
|
CompileStatus = ProgramLinkStatus.Incomplete;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public uint Stages { get; }
|
public uint Stages { get; }
|
||||||
|
|
||||||
public int[][][] Bindings { get; }
|
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||||
|
|
||||||
public ProgramLinkStatus LinkStatus { get; private set; }
|
public ProgramLinkStatus LinkStatus { get; private set; }
|
||||||
|
|
||||||
|
@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private Task _compileTask;
|
private Task _compileTask;
|
||||||
private bool _firstBackgroundUse;
|
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;
|
_gd = gd;
|
||||||
_device = device;
|
_device = device;
|
||||||
|
@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_shaders = internalShaders;
|
_shaders = internalShaders;
|
||||||
|
|
||||||
bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||||
|
|
||||||
_plce = isMinimal
|
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
|
||||||
? gd.PipelineLayoutCache.Create(gd, device, shaders)
|
|
||||||
: gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
|
|
||||||
|
|
||||||
HasMinimalLayout = isMinimal;
|
HasMinimalLayout = isMinimal;
|
||||||
UsePushDescriptors = usePd;
|
UsePushDescriptors = usePushDescriptors;
|
||||||
|
|
||||||
Stages = stages;
|
Stages = stages;
|
||||||
|
|
||||||
int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
|
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||||
{
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
|
|
||||||
_compileTask = Task.CompletedTask;
|
_compileTask = Task.CompletedTask;
|
||||||
_firstBackgroundUse = false;
|
_firstBackgroundUse = false;
|
||||||
|
@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
ShaderSource[] sources,
|
ShaderSource[] sources,
|
||||||
|
ResourceLayout resourceLayout,
|
||||||
ProgramPipelineState state,
|
ProgramPipelineState state,
|
||||||
bool fromCache) : this(gd, device, sources)
|
bool fromCache) : this(gd, device, sources, resourceLayout)
|
||||||
{
|
{
|
||||||
_state = state;
|
_state = state;
|
||||||
|
|
||||||
|
@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_firstBackgroundUse = !fromCache;
|
_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()
|
private async Task BackgroundCompilation()
|
||||||
{
|
{
|
||||||
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
|
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 Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
@ -398,17 +397,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
if (info.State.HasValue || isCompute)
|
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
|
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)
|
public ISampler CreateSampler(GAL.SamplerCreateInfo info)
|
||||||
|
@ -658,7 +657,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
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
|
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
|
return topology switch
|
||||||
{
|
{
|
||||||
|
|
Reference in a new issue