mirror of
				https://github.com/Ryujinx/Ryujinx.git
				synced 2025-11-04 01:03:43 +00:00 
			
		
		
		
	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
					
				
							
								
								
									
										179
									
								
								src/Ryujinx.Graphics.GAL/ResourceLayout.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/Ryujinx.Graphics.GAL/ResourceLayout.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,179 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL
 | 
			
		||||
{
 | 
			
		||||
    public enum ResourceType : byte
 | 
			
		||||
    {
 | 
			
		||||
        UniformBuffer,
 | 
			
		||||
        StorageBuffer,
 | 
			
		||||
        Texture,
 | 
			
		||||
        Sampler,
 | 
			
		||||
        TextureAndSampler,
 | 
			
		||||
        Image,
 | 
			
		||||
        BufferTexture,
 | 
			
		||||
        BufferImage
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum ResourceAccess : byte
 | 
			
		||||
    {
 | 
			
		||||
        None = 0,
 | 
			
		||||
        Read = 1,
 | 
			
		||||
        Write = 2,
 | 
			
		||||
        ReadWrite = Read | Write
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Flags]
 | 
			
		||||
    public enum ResourceStages : byte
 | 
			
		||||
    {
 | 
			
		||||
        None = 0,
 | 
			
		||||
        Compute = 1 << 0,
 | 
			
		||||
        Vertex = 1 << 1,
 | 
			
		||||
        TessellationControl = 1 << 2,
 | 
			
		||||
        TessellationEvaluation = 1 << 3,
 | 
			
		||||
        Geometry = 1 << 4,
 | 
			
		||||
        Fragment = 1 << 5
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly struct ResourceDescriptor : IEquatable<ResourceDescriptor>
 | 
			
		||||
    {
 | 
			
		||||
        public int Binding { get; }
 | 
			
		||||
        public int Count { get; }
 | 
			
		||||
        public ResourceType Type { get; }
 | 
			
		||||
        public ResourceStages Stages { get; }
 | 
			
		||||
 | 
			
		||||
        public ResourceDescriptor(int binding, int count, ResourceType type, ResourceStages stages)
 | 
			
		||||
        {
 | 
			
		||||
            Binding = binding;
 | 
			
		||||
            Count = count;
 | 
			
		||||
            Type = type;
 | 
			
		||||
            Stages = stages;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int GetHashCode()
 | 
			
		||||
        {
 | 
			
		||||
            return HashCode.Combine(Binding, Count, Type, Stages);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            return obj is ResourceDescriptor other && Equals(other);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Equals(ResourceDescriptor other)
 | 
			
		||||
        {
 | 
			
		||||
            return Binding == other.Binding && Count == other.Count && Type == other.Type && Stages == other.Stages;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly struct ResourceUsage : IEquatable<ResourceUsage>
 | 
			
		||||
    {
 | 
			
		||||
        public int Binding { get; }
 | 
			
		||||
        public ResourceType Type { get; }
 | 
			
		||||
        public ResourceStages Stages { get; }
 | 
			
		||||
        public ResourceAccess Access { get; }
 | 
			
		||||
 | 
			
		||||
        public ResourceUsage(int binding, ResourceType type, ResourceStages stages, ResourceAccess access)
 | 
			
		||||
        {
 | 
			
		||||
            Binding = binding;
 | 
			
		||||
            Type = type;
 | 
			
		||||
            Stages = stages;
 | 
			
		||||
            Access = access;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int GetHashCode()
 | 
			
		||||
        {
 | 
			
		||||
            return HashCode.Combine(Binding, Type, Stages, Access);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            return obj is ResourceUsage other && Equals(other);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Equals(ResourceUsage other)
 | 
			
		||||
        {
 | 
			
		||||
            return Binding == other.Binding && Type == other.Type && Stages == other.Stages && Access == other.Access;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly struct ResourceDescriptorCollection
 | 
			
		||||
    {
 | 
			
		||||
        public ReadOnlyCollection<ResourceDescriptor> Descriptors { get; }
 | 
			
		||||
 | 
			
		||||
        public ResourceDescriptorCollection(ReadOnlyCollection<ResourceDescriptor> descriptors)
 | 
			
		||||
        {
 | 
			
		||||
            Descriptors = descriptors;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int GetHashCode()
 | 
			
		||||
        {
 | 
			
		||||
            HashCode hasher = new HashCode();
 | 
			
		||||
 | 
			
		||||
            if (Descriptors != null)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var descriptor in Descriptors)
 | 
			
		||||
                {
 | 
			
		||||
                    hasher.Add(descriptor);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return hasher.ToHashCode();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            return obj is ResourceDescriptorCollection other && Equals(other);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Equals(ResourceDescriptorCollection other)
 | 
			
		||||
        {
 | 
			
		||||
            if ((Descriptors == null) != (other.Descriptors == null))
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Descriptors != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (Descriptors.Count != other.Descriptors.Count)
 | 
			
		||||
                {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (int index = 0; index < Descriptors.Count; index++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (!Descriptors[index].Equals(other.Descriptors[index]))
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly struct ResourceUsageCollection
 | 
			
		||||
    {
 | 
			
		||||
        public ReadOnlyCollection<ResourceUsage> Usages { get; }
 | 
			
		||||
 | 
			
		||||
        public ResourceUsageCollection(ReadOnlyCollection<ResourceUsage> usages)
 | 
			
		||||
        {
 | 
			
		||||
            Usages = usages;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly struct ResourceLayout
 | 
			
		||||
    {
 | 
			
		||||
        public ReadOnlyCollection<ResourceDescriptorCollection> Sets { get; }
 | 
			
		||||
        public ReadOnlyCollection<ResourceUsageCollection> SetUsages { get; }
 | 
			
		||||
 | 
			
		||||
        public ResourceLayout(
 | 
			
		||||
            ReadOnlyCollection<ResourceDescriptorCollection> sets,
 | 
			
		||||
            ReadOnlyCollection<ResourceUsageCollection> setUsages)
 | 
			
		||||
        {
 | 
			
		||||
            Sets = sets;
 | 
			
		||||
            SetUsages = setUsages;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL
 | 
			
		||||
{
 | 
			
		||||
    public readonly struct ShaderBindings
 | 
			
		||||
    {
 | 
			
		||||
        public IReadOnlyCollection<int> UniformBufferBindings { get; }
 | 
			
		||||
        public IReadOnlyCollection<int> StorageBufferBindings { get; }
 | 
			
		||||
        public IReadOnlyCollection<int> TextureBindings { get; }
 | 
			
		||||
        public IReadOnlyCollection<int> ImageBindings { get; }
 | 
			
		||||
 | 
			
		||||
        public ShaderBindings(
 | 
			
		||||
            IReadOnlyCollection<int> uniformBufferBindings,
 | 
			
		||||
            IReadOnlyCollection<int> storageBufferBindings,
 | 
			
		||||
            IReadOnlyCollection<int> textureBindings,
 | 
			
		||||
            IReadOnlyCollection<int> imageBindings)
 | 
			
		||||
        {
 | 
			
		||||
            UniformBufferBindings = uniformBufferBindings;
 | 
			
		||||
            StorageBufferBindings = storageBufferBindings;
 | 
			
		||||
            TextureBindings = textureBindings;
 | 
			
		||||
            ImageBindings = imageBindings;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,19 +3,22 @@ namespace Ryujinx.Graphics.GAL
 | 
			
		||||
    public struct ShaderInfo
 | 
			
		||||
    {
 | 
			
		||||
        public int FragmentOutputMap { get; }
 | 
			
		||||
        public ResourceLayout ResourceLayout { get; }
 | 
			
		||||
        public ProgramPipelineState? State { get; }
 | 
			
		||||
        public bool FromCache { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state, bool fromCache = false)
 | 
			
		||||
        public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
 | 
			
		||||
        {
 | 
			
		||||
            FragmentOutputMap = fragmentOutputMap;
 | 
			
		||||
            ResourceLayout = resourceLayout;
 | 
			
		||||
            State = state;
 | 
			
		||||
            FromCache = fromCache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ShaderInfo(int fragmentOutputMap, bool fromCache = false)
 | 
			
		||||
        public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
 | 
			
		||||
        {
 | 
			
		||||
            FragmentOutputMap = fragmentOutputMap;
 | 
			
		||||
            ResourceLayout = resourceLayout;
 | 
			
		||||
            State = null;
 | 
			
		||||
            FromCache = fromCache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7,24 +7,22 @@ namespace Ryujinx.Graphics.GAL
 | 
			
		||||
    {
 | 
			
		||||
        public string Code { get; }
 | 
			
		||||
        public byte[] BinaryCode { get; }
 | 
			
		||||
        public ShaderBindings Bindings { get; }
 | 
			
		||||
        public ShaderStage Stage { get; }
 | 
			
		||||
        public TargetLanguage Language { get; }
 | 
			
		||||
 | 
			
		||||
        public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language)
 | 
			
		||||
        public ShaderSource(string code, byte[] binaryCode, ShaderStage stage, TargetLanguage language)
 | 
			
		||||
        {
 | 
			
		||||
            Code = code;
 | 
			
		||||
            BinaryCode = binaryCode;
 | 
			
		||||
            Bindings = bindings;
 | 
			
		||||
            Stage = stage;
 | 
			
		||||
            Language = language;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language)
 | 
			
		||||
        public ShaderSource(string code, ShaderStage stage, TargetLanguage language) : this(code, null, stage, language)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language)
 | 
			
		||||
        public ShaderSource(byte[] binaryCode, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, stage, language)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -368,12 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
 | 
			
		||||
 | 
			
		||||
                        if (hostCode != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
 | 
			
		||||
                            int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
 | 
			
		||||
 | 
			
		||||
                            ShaderInfo shaderInfo = specState.PipelineState.HasValue
 | 
			
		||||
                                ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
 | 
			
		||||
                                : new ShaderInfo(fragmentOutputMap, fromCache: true);
 | 
			
		||||
                            ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState);
 | 
			
		||||
 | 
			
		||||
                            IProgram hostProgram;
 | 
			
		||||
 | 
			
		||||
@ -385,6 +380,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
 | 
			
		||||
 | 
			
		||||
                                hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -491,23 +491,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
 | 
			
		||||
            {
 | 
			
		||||
                ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
 | 
			
		||||
 | 
			
		||||
                int fragmentOutputMap = -1;
 | 
			
		||||
                ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context);
 | 
			
		||||
 | 
			
		||||
                for (int index = 0; index < compilation.TranslatedStages.Length; index++)
 | 
			
		||||
                {
 | 
			
		||||
                    ShaderProgram shader = compilation.TranslatedStages[index];
 | 
			
		||||
                    shaderSources[index] = CreateShaderSource(shader);
 | 
			
		||||
 | 
			
		||||
                    if (shader.Info.Stage == ShaderStage.Fragment)
 | 
			
		||||
                    {
 | 
			
		||||
                        fragmentOutputMap = shader.Info.FragmentOutputMap;
 | 
			
		||||
                    }
 | 
			
		||||
                    shaderInfoBuilder.AddStageInfo(shader.Info);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue
 | 
			
		||||
                    ? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true)
 | 
			
		||||
                    : new ShaderInfo(fragmentOutputMap, fromCache: true);
 | 
			
		||||
 | 
			
		||||
                ShaderInfo shaderInfo = shaderInfoBuilder.Build(compilation.SpecializationState.PipelineState, fromCache: true);
 | 
			
		||||
                IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
 | 
			
		||||
                CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,25 +42,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
 | 
			
		||||
                int binaryCodeLength = reader.ReadInt32();
 | 
			
		||||
                byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
 | 
			
		||||
 | 
			
		||||
                output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
 | 
			
		||||
                output.Add(new ShaderSource(binaryCode, stage, TargetLanguage.Spirv));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return output.ToArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < stages.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                CachedShaderStage currentStage = stages[i];
 | 
			
		||||
 | 
			
		||||
                if (currentStage?.Info != null && currentStage.Info.Stage == stage)
 | 
			
		||||
                {
 | 
			
		||||
                    return ShaderCache.GetBindings(currentStage.Info);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -110,16 +110,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
                Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GetStageIndex() * (int)maxPerStage + index;
 | 
			
		||||
            return GetStageIndex(_stageIndex) * (int)maxPerStage + index;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private int GetStageIndex()
 | 
			
		||||
        public static int GetStageIndex(int stageIndex)
 | 
			
		||||
        {
 | 
			
		||||
            // This is just a simple remapping to ensure that most frequently used shader stages
 | 
			
		||||
            // have the lowest binding numbers.
 | 
			
		||||
            // This is useful because if we need to run on a system with a low limit on the bindings,
 | 
			
		||||
            // then we can still get most games working as the most common shaders will have low binding numbers.
 | 
			
		||||
            return _stageIndex switch
 | 
			
		||||
            return stageIndex switch
 | 
			
		||||
            {
 | 
			
		||||
                4 => 1, // Fragment
 | 
			
		||||
                3 => 2, // Geometry
 | 
			
		||||
 | 
			
		||||
@ -219,12 +219,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
            GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
 | 
			
		||||
 | 
			
		||||
            TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
 | 
			
		||||
 | 
			
		||||
            TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
 | 
			
		||||
 | 
			
		||||
            ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
 | 
			
		||||
 | 
			
		||||
            IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
 | 
			
		||||
            ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
 | 
			
		||||
            IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
 | 
			
		||||
 | 
			
		||||
            cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
 | 
			
		||||
 | 
			
		||||
@ -363,6 +362,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
 | 
			
		||||
            TranslatorContext previousStage = null;
 | 
			
		||||
 | 
			
		||||
            ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
 | 
			
		||||
 | 
			
		||||
            for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
 | 
			
		||||
            {
 | 
			
		||||
                TranslatorContext currentStage = translatorContexts[stageIndex + 1];
 | 
			
		||||
@ -398,6 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
                    if (program != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        shaderSources.Add(CreateShaderSource(program));
 | 
			
		||||
                        infoBuilder.AddStageInfo(program.Info);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    previousStage = currentStage;
 | 
			
		||||
@ -414,8 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
 | 
			
		||||
            ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
 | 
			
		||||
 | 
			
		||||
            int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
 | 
			
		||||
            IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
 | 
			
		||||
            ShaderInfo info = infoBuilder.Build(pipeline);
 | 
			
		||||
 | 
			
		||||
            IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
 | 
			
		||||
 | 
			
		||||
            gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
 | 
			
		||||
 | 
			
		||||
@ -466,7 +469,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
        /// <returns>Shader source</returns>
 | 
			
		||||
        public static ShaderSource CreateShaderSource(ShaderProgram program)
 | 
			
		||||
        {
 | 
			
		||||
            return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
 | 
			
		||||
            return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -717,25 +720,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets information about the bindings used by a shader program.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="info">Shader program information to get the information from</param>
 | 
			
		||||
        /// <returns>Shader bindings</returns>
 | 
			
		||||
        public static ShaderBindings GetBindings(ShaderProgramInfo info)
 | 
			
		||||
        {
 | 
			
		||||
            var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
 | 
			
		||||
            var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
 | 
			
		||||
            var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
 | 
			
		||||
            var imageBindings = info.Images.Select(x => x.Binding).ToArray();
 | 
			
		||||
 | 
			
		||||
            return new ShaderBindings(
 | 
			
		||||
                uniformBufferBindings,
 | 
			
		||||
                storageBufferBindings,
 | 
			
		||||
                textureBindings,
 | 
			
		||||
                imageBindings);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates shader translation options with the requested graphics API and flags.
 | 
			
		||||
        /// The shader language is choosen based on the current configuration and graphics API.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										260
									
								
								src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,260 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Ryujinx.Graphics.Shader;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Gpu.Shader
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Shader info structure builder.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    class ShaderInfoBuilder
 | 
			
		||||
    {
 | 
			
		||||
        private const int TotalSets = 4;
 | 
			
		||||
 | 
			
		||||
        private const int UniformSetIndex = 0;
 | 
			
		||||
        private const int StorageSetIndex = 1;
 | 
			
		||||
        private const int TextureSetIndex = 2;
 | 
			
		||||
        private const int ImageSetIndex = 3;
 | 
			
		||||
 | 
			
		||||
        private const ResourceStages SupportBufferStags =
 | 
			
		||||
            ResourceStages.Compute |
 | 
			
		||||
            ResourceStages.Vertex |
 | 
			
		||||
            ResourceStages.Fragment;
 | 
			
		||||
 | 
			
		||||
        private readonly GpuContext _context;
 | 
			
		||||
 | 
			
		||||
        private int _fragmentOutputMap;
 | 
			
		||||
 | 
			
		||||
        private readonly List<ResourceDescriptor>[] _resourceDescriptors;
 | 
			
		||||
        private readonly List<ResourceUsage>[] _resourceUsages;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates a new shader info builder.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
 | 
			
		||||
        public ShaderInfoBuilder(GpuContext context)
 | 
			
		||||
        {
 | 
			
		||||
            _context = context;
 | 
			
		||||
 | 
			
		||||
            _fragmentOutputMap = -1;
 | 
			
		||||
 | 
			
		||||
            _resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
 | 
			
		||||
            _resourceUsages = new List<ResourceUsage>[TotalSets];
 | 
			
		||||
 | 
			
		||||
            for (int index = 0; index < TotalSets; index++)
 | 
			
		||||
            {
 | 
			
		||||
                _resourceDescriptors[index] = new();
 | 
			
		||||
                _resourceUsages[index] = new();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds information from a given shader stage.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="info">Shader stage information</param>
 | 
			
		||||
        public void AddStageInfo(ShaderProgramInfo info)
 | 
			
		||||
        {
 | 
			
		||||
            if (info.Stage == ShaderStage.Fragment)
 | 
			
		||||
            {
 | 
			
		||||
                _fragmentOutputMap = info.FragmentOutputMap;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
 | 
			
		||||
            {
 | 
			
		||||
                ShaderStage.TessellationControl => 1,
 | 
			
		||||
                ShaderStage.TessellationEvaluation => 2,
 | 
			
		||||
                ShaderStage.Geometry => 3,
 | 
			
		||||
                ShaderStage.Fragment => 4,
 | 
			
		||||
                _ => 0
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            ResourceStages stages = info.Stage switch
 | 
			
		||||
            {
 | 
			
		||||
                ShaderStage.Compute => ResourceStages.Compute,
 | 
			
		||||
                ShaderStage.Vertex => ResourceStages.Vertex,
 | 
			
		||||
                ShaderStage.TessellationControl => ResourceStages.TessellationControl,
 | 
			
		||||
                ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
 | 
			
		||||
                ShaderStage.Geometry => ResourceStages.Geometry,
 | 
			
		||||
                ShaderStage.Fragment => ResourceStages.Fragment,
 | 
			
		||||
                _ => ResourceStages.None
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
 | 
			
		||||
            int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
 | 
			
		||||
            int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
 | 
			
		||||
            int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
 | 
			
		||||
 | 
			
		||||
            int uniformBinding = 1 + stageIndex * uniformsPerStage;
 | 
			
		||||
            int storageBinding = stageIndex * storagesPerStage;
 | 
			
		||||
            int textureBinding = stageIndex * texturesPerStage * 2;
 | 
			
		||||
            int imageBinding = stageIndex * imagesPerStage * 2;
 | 
			
		||||
 | 
			
		||||
            AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
 | 
			
		||||
            AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
 | 
			
		||||
            AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
 | 
			
		||||
            AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
 | 
			
		||||
 | 
			
		||||
            AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
 | 
			
		||||
            AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
 | 
			
		||||
            AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
 | 
			
		||||
            AddUsage(info.Images, stages, ImageSetIndex, isImage: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds a resource descriptor to the list of descriptors.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stages">Shader stages where the resource is used</param>
 | 
			
		||||
        /// <param name="type">Type of the resource</param>
 | 
			
		||||
        /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="binding">Binding number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="count">Number of resources bound at the binding location</param>
 | 
			
		||||
        private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
 | 
			
		||||
        {
 | 
			
		||||
            for (int index = 0; index < count; index++)
 | 
			
		||||
            {
 | 
			
		||||
                _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds two interleaved groups of resources to the list of descriptors.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stages">Shader stages where the resource is used</param>
 | 
			
		||||
        /// <param name="type">Type of the first interleaved resource</param>
 | 
			
		||||
        /// <param name="type2">Type of the second interleaved resource</param>
 | 
			
		||||
        /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="binding">Binding number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="count">Number of resources bound at the binding location</param>
 | 
			
		||||
        private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
 | 
			
		||||
        {
 | 
			
		||||
            AddDescriptor(stages, type, setIndex, binding, count);
 | 
			
		||||
            AddDescriptor(stages, type2, setIndex, binding + count, count);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds an array resource to the list of descriptors.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stages">Shader stages where the resource is used</param>
 | 
			
		||||
        /// <param name="type">Type of the resource</param>
 | 
			
		||||
        /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="binding">Binding number where the resource will be bound</param>
 | 
			
		||||
        /// <param name="count">Number of resources bound at the binding location</param>
 | 
			
		||||
        private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
 | 
			
		||||
        {
 | 
			
		||||
            _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds buffer usage information to the list of usages.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffers">Buffers to be added</param>
 | 
			
		||||
        /// <param name="stages">Stages where the buffers are used</param>
 | 
			
		||||
        /// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
 | 
			
		||||
        /// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
 | 
			
		||||
        private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (BufferDescriptor buffer in buffers)
 | 
			
		||||
            {
 | 
			
		||||
                _resourceUsages[setIndex].Add(new ResourceUsage(
 | 
			
		||||
                    buffer.Binding,
 | 
			
		||||
                    isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
 | 
			
		||||
                    stages,
 | 
			
		||||
                    buffer.Flags.HasFlag(BufferUsageFlags.Write) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Adds texture usage information to the list of usages.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="textures">Textures to be added</param>
 | 
			
		||||
        /// <param name="stages">Stages where the textures are used</param>
 | 
			
		||||
        /// <param name="setIndex">Descriptor set index where the textures will be bound</param>
 | 
			
		||||
        /// <param name="isImage">True for images, false for textures</param>
 | 
			
		||||
        private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (TextureDescriptor texture in textures)
 | 
			
		||||
            {
 | 
			
		||||
                bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
 | 
			
		||||
 | 
			
		||||
                ResourceType type = isBuffer
 | 
			
		||||
                    ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
 | 
			
		||||
                    : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
 | 
			
		||||
 | 
			
		||||
                _resourceUsages[setIndex].Add(new ResourceUsage(
 | 
			
		||||
                    texture.Binding,
 | 
			
		||||
                    type,
 | 
			
		||||
                    stages,
 | 
			
		||||
                    texture.Flags.HasFlag(TextureUsageFlags.ImageStore) ? ResourceAccess.ReadWrite : ResourceAccess.Read));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates a new shader information structure from the added information.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="pipeline">Optional pipeline state for background shader compilation</param>
 | 
			
		||||
        /// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
 | 
			
		||||
        /// <returns>Shader information</returns>
 | 
			
		||||
        public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
 | 
			
		||||
        {
 | 
			
		||||
            var descriptors = new ResourceDescriptorCollection[TotalSets];
 | 
			
		||||
            var usages = new ResourceUsageCollection[TotalSets];
 | 
			
		||||
 | 
			
		||||
            for (int index = 0; index < TotalSets; index++)
 | 
			
		||||
            {
 | 
			
		||||
                descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
 | 
			
		||||
                usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ResourceLayout resourceLayout = new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
 | 
			
		||||
 | 
			
		||||
            if (pipeline.HasValue)
 | 
			
		||||
            {
 | 
			
		||||
                return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Builds shader information for shaders from the disk cache.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="context">GPU context that owns the shaders</param>
 | 
			
		||||
        /// <param name="programs">Shaders from the disk cache</param>
 | 
			
		||||
        /// <param name="pipeline">Optional pipeline for background compilation</param>
 | 
			
		||||
        /// <returns>Shader information</returns>
 | 
			
		||||
        public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline)
 | 
			
		||||
        {
 | 
			
		||||
            ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
 | 
			
		||||
 | 
			
		||||
            foreach (CachedShaderStage program in programs)
 | 
			
		||||
            {
 | 
			
		||||
                if (program?.Info != null)
 | 
			
		||||
                {
 | 
			
		||||
                    builder.AddStageInfo(program.Info);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return builder.Build(pipeline, fromCache: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Builds shader information for a compute shader.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="context">GPU context that owns the shader</param>
 | 
			
		||||
        /// <param name="info">Compute shader information</param>
 | 
			
		||||
        /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
 | 
			
		||||
        /// <returns>Shader information</returns>
 | 
			
		||||
        public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
 | 
			
		||||
        {
 | 
			
		||||
            ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
 | 
			
		||||
 | 
			
		||||
            builder.AddStageInfo(info);
 | 
			
		||||
 | 
			
		||||
            return builder.Build(null, fromCache);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -372,8 +372,9 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp)
 | 
			
		||||
        {
 | 
			
		||||
            var program = _program;
 | 
			
		||||
            int stagesCount = program.Bindings[setIndex].Length;
 | 
			
		||||
            if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex)
 | 
			
		||||
            var bindingSegments = program.BindingSegments[setIndex];
 | 
			
		||||
 | 
			
		||||
            if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
@ -410,125 +411,113 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
 | 
			
		||||
            foreach (ResourceBindingSegment segment in bindingSegments)
 | 
			
		||||
            {
 | 
			
		||||
                var stageBindings = program.Bindings[setIndex][stageIndex];
 | 
			
		||||
                int bindingsCount = stageBindings.Length;
 | 
			
		||||
                int count;
 | 
			
		||||
                int binding = segment.Binding;
 | 
			
		||||
                int count = segment.Count;
 | 
			
		||||
 | 
			
		||||
                for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
 | 
			
		||||
                if (setIndex == PipelineBase.UniformSetIndex)
 | 
			
		||||
                {
 | 
			
		||||
                    int binding = stageBindings[bindingIndex];
 | 
			
		||||
                    count = 1;
 | 
			
		||||
 | 
			
		||||
                    while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
 | 
			
		||||
                    for (int i = 0; i < count; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        count++;
 | 
			
		||||
                        int index = binding + i;
 | 
			
		||||
 | 
			
		||||
                        if (!_uniformSet[index])
 | 
			
		||||
                        {
 | 
			
		||||
                            UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
 | 
			
		||||
 | 
			
		||||
                            _uniformSet[index] = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (setIndex == PipelineBase.UniformSetIndex)
 | 
			
		||||
                    ReadOnlySpan<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++)
 | 
			
		||||
                        {
 | 
			
		||||
                            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.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
 | 
			
		||||
                        dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (setIndex == PipelineBase.StorageSetIndex)
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        Span<BufferView> bufferTextures = _bufferTextures;
 | 
			
		||||
 | 
			
		||||
                        for (int i = 0; i < count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            int index = binding + i;
 | 
			
		||||
 | 
			
		||||
                            if (!_storageSet[index])
 | 
			
		||||
                            {
 | 
			
		||||
                                UpdateBuffer(cbs, ref _storageBuffers[index], _storageBufferRefs[index], dummyBuffer);
 | 
			
		||||
 | 
			
		||||
                                _storageSet[index] = true;
 | 
			
		||||
                            }
 | 
			
		||||
                            bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        ReadOnlySpan<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));
 | 
			
		||||
                        }
 | 
			
		||||
                        dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (setIndex == PipelineBase.TextureSetIndex)
 | 
			
		||||
                }
 | 
			
		||||
                else if (setIndex == PipelineBase.ImageSetIndex)
 | 
			
		||||
                {
 | 
			
		||||
                    if (segment.Type != ResourceType.BufferImage)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (((uint)binding % (Constants.MaxTexturesPerStage * 2)) < Constants.MaxTexturesPerStage || program.HasMinimalLayout)
 | 
			
		||||
                        Span<DescriptorImageInfo> images = _images;
 | 
			
		||||
 | 
			
		||||
                        for (int i = 0; i < count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            Span<DescriptorImageInfo> textures = _textures;
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < count; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                ref var texture = ref textures[i];
 | 
			
		||||
 | 
			
		||||
                                texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
 | 
			
		||||
                                texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
 | 
			
		||||
 | 
			
		||||
                                if (texture.ImageView.Handle == 0)
 | 
			
		||||
                                {
 | 
			
		||||
                                    texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                if (texture.Sampler.Handle == 0)
 | 
			
		||||
                                {
 | 
			
		||||
                                    texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            dsc.UpdateImages(0, binding, textures.Slice(0, count), DescriptorType.CombinedImageSampler);
 | 
			
		||||
                            images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            Span<BufferView> bufferTextures = _bufferTextures;
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < count; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(0, count), DescriptorType.UniformTexelBuffer);
 | 
			
		||||
                        }
 | 
			
		||||
                        dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (setIndex == PipelineBase.ImageSetIndex)
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (((uint)binding % (Constants.MaxImagesPerStage * 2)) < Constants.MaxImagesPerStage || program.HasMinimalLayout)
 | 
			
		||||
                        Span<BufferView> bufferImages = _bufferImages;
 | 
			
		||||
 | 
			
		||||
                        for (int i = 0; i < count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            Span<DescriptorImageInfo> images = _images;
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < count; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            dsc.UpdateImages(0, binding, images.Slice(0, count), DescriptorType.StorageImage);
 | 
			
		||||
                            bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            Span<BufferView> bufferImages = _bufferImages;
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < count; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i]) ?? default;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
 | 
			
		||||
                        }
 | 
			
		||||
                        dsc.UpdateBufferImages(0, binding, bufferImages.Slice(0, count), DescriptorType.StorageTexelBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -568,9 +557,6 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
 | 
			
		||||
        {
 | 
			
		||||
            var dummyBuffer = _dummyBuffer?.GetBuffer();
 | 
			
		||||
            int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length;
 | 
			
		||||
 | 
			
		||||
            if (!_uniformSet[0])
 | 
			
		||||
            {
 | 
			
		||||
                Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
 | 
			
		||||
@ -587,41 +573,32 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
 | 
			
		||||
            var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
 | 
			
		||||
            var dummyBuffer = _dummyBuffer?.GetBuffer();
 | 
			
		||||
 | 
			
		||||
            foreach (ResourceBindingSegment segment in bindingSegments)
 | 
			
		||||
            {
 | 
			
		||||
                var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex];
 | 
			
		||||
                int bindingsCount = stageBindings.Length;
 | 
			
		||||
                int count;
 | 
			
		||||
                int binding = segment.Binding;
 | 
			
		||||
                int count = segment.Count;
 | 
			
		||||
 | 
			
		||||
                for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
 | 
			
		||||
                bool doUpdate = false;
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < count; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    int binding = stageBindings[bindingIndex];
 | 
			
		||||
                    count = 1;
 | 
			
		||||
                    int index = binding + i;
 | 
			
		||||
 | 
			
		||||
                    while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
 | 
			
		||||
                    if (!_uniformSet[index])
 | 
			
		||||
                    {
 | 
			
		||||
                        count++;
 | 
			
		||||
                        UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
 | 
			
		||||
                        _uniformSet[index] = true;
 | 
			
		||||
                        doUpdate = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                    bool doUpdate = false;
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < count; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        int index = binding + i;
 | 
			
		||||
 | 
			
		||||
                        if (!_uniformSet[index])
 | 
			
		||||
                        {
 | 
			
		||||
                            UpdateBuffer(cbs, ref _uniformBuffers[index], _uniformBufferRefs[index], dummyBuffer);
 | 
			
		||||
                            _uniformSet[index] = true;
 | 
			
		||||
                            doUpdate = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (doUpdate)
 | 
			
		||||
                    {
 | 
			
		||||
                        ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
 | 
			
		||||
                        UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                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 sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
 | 
			
		||||
 | 
			
		||||
            var computeBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var scalingResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            var sharpeningBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2, 3, 4 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var sharpeningResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
 | 
			
		||||
 | 
			
		||||
            _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, scalingResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(sharpeningShader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, sharpeningResourceLayout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Run(
 | 
			
		||||
@ -160,10 +160,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
 | 
			
		||||
            _pipeline.ComputeBarrier();
 | 
			
		||||
 | 
			
		||||
            // Sharpening pass
 | 
			
		||||
            _pipeline.SetCommandBuffer(cbs);
 | 
			
		||||
            _pipeline.SetProgram(_sharpeningProgram);
 | 
			
		||||
            _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
 | 
			
		||||
            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
 | 
			
		||||
            var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
 | 
			
		||||
            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
 | 
			
		||||
            _pipeline.SetImage(0, destinationTexture);
 | 
			
		||||
 | 
			
		||||
@ -38,18 +38,17 @@ namespace Ryujinx.Graphics.Vulkan.Effects
 | 
			
		||||
 | 
			
		||||
            var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
 | 
			
		||||
 | 
			
		||||
            var computeBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var resourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
 | 
			
		||||
 | 
			
		||||
            _shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(shader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, resourceLayout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
 | 
			
		||||
 | 
			
		||||
@ -77,23 +77,23 @@ namespace Ryujinx.Graphics.Vulkan.Effects
 | 
			
		||||
            var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
 | 
			
		||||
            var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
 | 
			
		||||
 | 
			
		||||
            var edgeBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var edgeResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            var blendBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1, 3, 4 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var blendResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            var neighbourBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 1, 3 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var neighbourResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
 | 
			
		||||
 | 
			
		||||
@ -117,18 +117,18 @@ namespace Ryujinx.Graphics.Vulkan.Effects
 | 
			
		||||
 | 
			
		||||
            _edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, new[] { specInfo });
 | 
			
		||||
                new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, edgeResourceLayout, new[] { specInfo });
 | 
			
		||||
 | 
			
		||||
            _blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, new[] { specInfo });
 | 
			
		||||
                new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, blendResourceLayout, new[] { specInfo });
 | 
			
		||||
 | 
			
		||||
            _neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, new[] { specInfo });
 | 
			
		||||
                new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv)
 | 
			
		||||
            }, neighbourResourceLayout, new[] { specInfo });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void DeletePipelines()
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,56 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static ShaderStageFlags Convert(this ResourceStages stages)
 | 
			
		||||
        {
 | 
			
		||||
            ShaderStageFlags stageFlags = stages.HasFlag(ResourceStages.Compute)
 | 
			
		||||
                ? ShaderStageFlags.ComputeBit
 | 
			
		||||
                : ShaderStageFlags.None;
 | 
			
		||||
 | 
			
		||||
            if (stages.HasFlag(ResourceStages.Vertex))
 | 
			
		||||
            {
 | 
			
		||||
                stageFlags |= ShaderStageFlags.VertexBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (stages.HasFlag(ResourceStages.TessellationControl))
 | 
			
		||||
            {
 | 
			
		||||
                stageFlags |= ShaderStageFlags.TessellationControlBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (stages.HasFlag(ResourceStages.TessellationEvaluation))
 | 
			
		||||
            {
 | 
			
		||||
                stageFlags |= ShaderStageFlags.TessellationEvaluationBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (stages.HasFlag(ResourceStages.Geometry))
 | 
			
		||||
            {
 | 
			
		||||
                stageFlags |= ShaderStageFlags.GeometryBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (stages.HasFlag(ResourceStages.Fragment))
 | 
			
		||||
            {
 | 
			
		||||
                stageFlags |= ShaderStageFlags.FragmentBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return stageFlags;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static DescriptorType Convert(this ResourceType type)
 | 
			
		||||
        {
 | 
			
		||||
            return type switch
 | 
			
		||||
            {
 | 
			
		||||
                ResourceType.UniformBuffer => DescriptorType.UniformBuffer,
 | 
			
		||||
                ResourceType.StorageBuffer => DescriptorType.StorageBuffer,
 | 
			
		||||
                ResourceType.Texture => DescriptorType.SampledImage,
 | 
			
		||||
                ResourceType.Sampler => DescriptorType.Sampler,
 | 
			
		||||
                ResourceType.TextureAndSampler => DescriptorType.CombinedImageSampler,
 | 
			
		||||
                ResourceType.Image => DescriptorType.StorageImage,
 | 
			
		||||
                ResourceType.BufferTexture => DescriptorType.UniformTexelBuffer,
 | 
			
		||||
                ResourceType.BufferImage => DescriptorType.StorageTexelBuffer,
 | 
			
		||||
                _ => throw new ArgumentException($"Invalid resource type \"{type}\".")
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static SamplerAddressMode Convert(this AddressMode mode)
 | 
			
		||||
        {
 | 
			
		||||
            return mode switch
 | 
			
		||||
@ -48,7 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                AddressMode.ClampToBorder => SamplerAddressMode.ClampToBorder,
 | 
			
		||||
                AddressMode.MirroredRepeat => SamplerAddressMode.MirroredRepeat,
 | 
			
		||||
                AddressMode.ClampToEdge => SamplerAddressMode.ClampToEdge,
 | 
			
		||||
                _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge)  // TODO: Should be clamp.
 | 
			
		||||
                _ => LogInvalidAndReturn(mode, nameof(AddressMode), SamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -55,192 +55,168 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
 | 
			
		||||
            _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
 | 
			
		||||
 | 
			
		||||
            var blitVertexBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 1 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
 | 
			
		||||
            var blitFragmentBindings = new ShaderBindings(
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var blitResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
 | 
			
		||||
                .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var colorClearFragmentBindings = new ShaderBindings(
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
 | 
			
		||||
 | 
			
		||||
            _programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorClearResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorClearResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorClearResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var strideChangeBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                new[] { 1, 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var strideChangeResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
 | 
			
		||||
 | 
			
		||||
            _programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, strideChangeResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var colorCopyBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                new[] { 0 });
 | 
			
		||||
            var colorCopyResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorCopyResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorCopyResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorCopyResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var colorDrawToMsVertexBindings = new ShaderBindings(
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
 | 
			
		||||
            var colorDrawToMsFragmentBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
 | 
			
		||||
 | 
			
		||||
            _programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorDrawToMsResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var convertD32S8ToD24S8Bindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                new[] { 1, 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
 | 
			
		||||
 | 
			
		||||
            _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, convertD32S8ToD24S8Bindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertD32S8ToD24S8ShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, convertD32S8ToD24S8ResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var convertIndexBufferBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                new[] { 1, 2 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
 | 
			
		||||
 | 
			
		||||
            _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, convertIndexBufferResourceLayout);
 | 
			
		||||
 | 
			
		||||
            var convertIndirectDataBindings = new ShaderBindings(
 | 
			
		||||
                new[] { 0 },
 | 
			
		||||
                new[] { 1, 2, 3 },
 | 
			
		||||
                Array.Empty<int>(),
 | 
			
		||||
                Array.Empty<int>());
 | 
			
		||||
            var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
 | 
			
		||||
                .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
 | 
			
		||||
 | 
			
		||||
            _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, ShaderStage.Compute, TargetLanguage.Spirv),
 | 
			
		||||
            }, convertIndirectDataResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorDrawToMsResourceLayout);
 | 
			
		||||
 | 
			
		||||
            _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
            {
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            });
 | 
			
		||||
                new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
            }, colorDrawToMsResourceLayout);
 | 
			
		||||
 | 
			
		||||
            if (gd.Capabilities.SupportsShaderStencilExport)
 | 
			
		||||
            {
 | 
			
		||||
                _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                });
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
                _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                });
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                }, blitResourceLayout);
 | 
			
		||||
 | 
			
		||||
                _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                });
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                }, colorDrawToMsResourceLayout);
 | 
			
		||||
 | 
			
		||||
                _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                });
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, ShaderStage.Vertex, TargetLanguage.Spirv),
 | 
			
		||||
                    new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, ShaderStage.Fragment, TargetLanguage.Spirv),
 | 
			
		||||
                }, colorDrawToMsResourceLayout);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,52 +1,101 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    class PipelineLayoutCache
 | 
			
		||||
    {
 | 
			
		||||
        private readonly PipelineLayoutCacheEntry[] _plce;
 | 
			
		||||
        private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
 | 
			
		||||
        private readonly struct PlceKey : IEquatable<PlceKey>
 | 
			
		||||
        {
 | 
			
		||||
            public readonly ReadOnlyCollection<ResourceDescriptorCollection> SetDescriptors;
 | 
			
		||||
            public readonly bool UsePushDescriptors;
 | 
			
		||||
 | 
			
		||||
            public PlceKey(ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors, bool usePushDescriptors)
 | 
			
		||||
            {
 | 
			
		||||
                SetDescriptors = setDescriptors;
 | 
			
		||||
                UsePushDescriptors = usePushDescriptors;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public override int GetHashCode()
 | 
			
		||||
            {
 | 
			
		||||
                HashCode hasher = new HashCode();
 | 
			
		||||
 | 
			
		||||
                if (SetDescriptors != null)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var setDescriptor in SetDescriptors)
 | 
			
		||||
                    {
 | 
			
		||||
                        hasher.Add(setDescriptor);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                hasher.Add(UsePushDescriptors);
 | 
			
		||||
 | 
			
		||||
                return hasher.ToHashCode();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public override bool Equals(object obj)
 | 
			
		||||
            {
 | 
			
		||||
                return obj is PlceKey other && Equals(other);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public bool Equals(PlceKey other)
 | 
			
		||||
            {
 | 
			
		||||
                if ((SetDescriptors == null) != (other.SetDescriptors == null))
 | 
			
		||||
                {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (SetDescriptors != null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (SetDescriptors.Count != other.SetDescriptors.Count)
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    for (int index = 0; index < SetDescriptors.Count; index++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!SetDescriptors[index].Equals(other.SetDescriptors[index]))
 | 
			
		||||
                        {
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return UsePushDescriptors == other.UsePushDescriptors;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry> _plces;
 | 
			
		||||
 | 
			
		||||
        public PipelineLayoutCache()
 | 
			
		||||
        {
 | 
			
		||||
            _plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
 | 
			
		||||
            _plceMinimal = new List<PipelineLayoutCacheEntry>();
 | 
			
		||||
            _plces = new ConcurrentDictionary<PlceKey, PipelineLayoutCacheEntry>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
 | 
			
		||||
        public PipelineLayoutCacheEntry GetOrCreate(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
            ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
 | 
			
		||||
            bool usePushDescriptors)
 | 
			
		||||
        {
 | 
			
		||||
            var plce = new PipelineLayoutCacheEntry(gd, device, shaders);
 | 
			
		||||
            _plceMinimal.Add(plce);
 | 
			
		||||
            return plce;
 | 
			
		||||
        }
 | 
			
		||||
            var key = new PlceKey(setDescriptors, usePushDescriptors);
 | 
			
		||||
 | 
			
		||||
        public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
 | 
			
		||||
        {
 | 
			
		||||
            if (_plce[stages] == null)
 | 
			
		||||
            {
 | 
			
		||||
                _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _plce[stages];
 | 
			
		||||
            return _plces.GetOrAdd(key, (newKey) => new PipelineLayoutCacheEntry(gd, device, setDescriptors, usePushDescriptors));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected virtual unsafe void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            if (disposing)
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < _plce.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    _plce[i]?.Dispose();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach (var plce in _plceMinimal)
 | 
			
		||||
                foreach (var plce in _plces.Values)
 | 
			
		||||
                {
 | 
			
		||||
                    plce.Dispose();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _plceMinimal.Clear();
 | 
			
		||||
                _plces.Clear();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
@ -16,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private readonly int[] _dsCacheCursor;
 | 
			
		||||
        private int _dsLastCbIndex;
 | 
			
		||||
 | 
			
		||||
        private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device)
 | 
			
		||||
        private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
 | 
			
		||||
        {
 | 
			
		||||
            _gd = gd;
 | 
			
		||||
            _device = device;
 | 
			
		||||
@ -25,27 +26,24 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
 | 
			
		||||
            {
 | 
			
		||||
                _dsCache[i] = new List<Auto<DescriptorSetCollection>>[PipelineBase.DescriptorSetLayouts];
 | 
			
		||||
                _dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount];
 | 
			
		||||
 | 
			
		||||
                for (int j = 0; j < PipelineBase.DescriptorSetLayouts; j++)
 | 
			
		||||
                for (int j = 0; j < _dsCache[i].Length; j++)
 | 
			
		||||
                {
 | 
			
		||||
                    _dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts];
 | 
			
		||||
            _dsCacheCursor = new int[setsCount];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, uint stages, bool usePd) : this(gd, device)
 | 
			
		||||
        public PipelineLayoutCacheEntry(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
            ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
 | 
			
		||||
            bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
 | 
			
		||||
        {
 | 
			
		||||
            DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout);
 | 
			
		||||
            PipelineLayout = pipelineLayout;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, ShaderSource[] shaders) : this(gd, device)
 | 
			
		||||
        {
 | 
			
		||||
            DescriptorSetLayouts = PipelineLayoutFactory.CreateMinimal(gd, device, shaders, out var pipelineLayout);
 | 
			
		||||
            PipelineLayout = pipelineLayout;
 | 
			
		||||
            (DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
 | 
			
		||||
@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            {
 | 
			
		||||
                _dsLastCbIndex = commandBufferIndex;
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < PipelineBase.DescriptorSetLayouts; i++)
 | 
			
		||||
                for (int i = 0; i < _dsCacheCursor.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    _dsCacheCursor[i] = 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -1,257 +1,74 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    static class PipelineLayoutFactory
 | 
			
		||||
    {
 | 
			
		||||
        private const ShaderStageFlags SupportBufferStages =
 | 
			
		||||
            ShaderStageFlags.VertexBit |
 | 
			
		||||
            ShaderStageFlags.FragmentBit |
 | 
			
		||||
            ShaderStageFlags.ComputeBit;
 | 
			
		||||
 | 
			
		||||
        private static ShaderStageFlags ActiveStages(uint stages)
 | 
			
		||||
        public static unsafe (DescriptorSetLayout[], PipelineLayout) Create(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
            ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
 | 
			
		||||
            bool usePushDescriptors)
 | 
			
		||||
        {
 | 
			
		||||
            ShaderStageFlags stageFlags = 0;
 | 
			
		||||
            DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
 | 
			
		||||
 | 
			
		||||
            while (stages != 0)
 | 
			
		||||
            bool isMoltenVk = gd.IsMoltenVk;
 | 
			
		||||
 | 
			
		||||
            for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
 | 
			
		||||
            {
 | 
			
		||||
                int stage = BitOperations.TrailingZeroCount(stages);
 | 
			
		||||
                stages &= ~(1u << stage);
 | 
			
		||||
                ResourceDescriptorCollection rdc = setDescriptors[setIndex];
 | 
			
		||||
 | 
			
		||||
                stageFlags |= stage switch
 | 
			
		||||
                ResourceStages activeStages = ResourceStages.None;
 | 
			
		||||
 | 
			
		||||
                if (isMoltenVk)
 | 
			
		||||
                {
 | 
			
		||||
                    1 => ShaderStageFlags.FragmentBit,
 | 
			
		||||
                    2 => ShaderStageFlags.GeometryBit,
 | 
			
		||||
                    3 => ShaderStageFlags.TessellationControlBit,
 | 
			
		||||
                    4 => ShaderStageFlags.TessellationEvaluationBit,
 | 
			
		||||
                    _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return stageFlags;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
 | 
			
		||||
        {
 | 
			
		||||
            int stagesCount = BitOperations.PopCount(stages);
 | 
			
		||||
 | 
			
		||||
            int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
 | 
			
		||||
            int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
 | 
			
		||||
            int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
 | 
			
		||||
 | 
			
		||||
            DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
 | 
			
		||||
 | 
			
		||||
            uLayoutBindings[0] = new DescriptorSetLayoutBinding
 | 
			
		||||
            {
 | 
			
		||||
                Binding = 0,
 | 
			
		||||
                DescriptorType = DescriptorType.UniformBuffer,
 | 
			
		||||
                DescriptorCount = 1,
 | 
			
		||||
                StageFlags = SupportBufferStages
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            int iter = 0;
 | 
			
		||||
            var activeStages = ActiveStages(stages);
 | 
			
		||||
 | 
			
		||||
            while (stages != 0)
 | 
			
		||||
            {
 | 
			
		||||
                int stage = BitOperations.TrailingZeroCount(stages);
 | 
			
		||||
                stages &= ~(1u << stage);
 | 
			
		||||
 | 
			
		||||
                var stageFlags = stage switch
 | 
			
		||||
                {
 | 
			
		||||
                    1 => ShaderStageFlags.FragmentBit,
 | 
			
		||||
                    2 => ShaderStageFlags.GeometryBit,
 | 
			
		||||
                    3 => ShaderStageFlags.TessellationControlBit,
 | 
			
		||||
                    4 => ShaderStageFlags.TessellationEvaluationBit,
 | 
			
		||||
                    _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
 | 
			
		||||
                {
 | 
			
		||||
                    int totalPerStage = maxPerStage * skip;
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < maxPerStage; i++)
 | 
			
		||||
                    for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
 | 
			
		||||
                    {
 | 
			
		||||
                        bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
 | 
			
		||||
                        {
 | 
			
		||||
                            Binding = (uint)(start + stage * totalPerStage + i),
 | 
			
		||||
                            DescriptorType = type,
 | 
			
		||||
                            DescriptorCount = 1,
 | 
			
		||||
                            StageFlags = stageFlags
 | 
			
		||||
                        };
 | 
			
		||||
                        activeStages |= rdc.Descriptors[descIndex].Stages;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // There's a bug on MoltenVK where using the same buffer across different stages
 | 
			
		||||
                    // causes invalid resource errors, allow the binding on all active stages as workaround.
 | 
			
		||||
                    var flags = gd.IsMoltenVk ? activeStages : stageFlags;
 | 
			
		||||
                DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
 | 
			
		||||
 | 
			
		||||
                    bindings[start + iter] = new DescriptorSetLayoutBinding
 | 
			
		||||
                for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
 | 
			
		||||
                {
 | 
			
		||||
                    ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
 | 
			
		||||
 | 
			
		||||
                    ResourceStages stages = descriptor.Stages;
 | 
			
		||||
 | 
			
		||||
                    if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
 | 
			
		||||
                    {
 | 
			
		||||
                        Binding = (uint)(start + stage * maxPerStage),
 | 
			
		||||
                        DescriptorType = DescriptorType.StorageBuffer,
 | 
			
		||||
                        DescriptorCount = (uint)maxPerStage,
 | 
			
		||||
                        StageFlags = flags
 | 
			
		||||
                        // There's a bug on MoltenVK where using the same buffer across different stages
 | 
			
		||||
                        // causes invalid resource errors, allow the binding on all active stages as workaround.
 | 
			
		||||
                        stages = activeStages;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    layoutBindings[descIndex] = new DescriptorSetLayoutBinding()
 | 
			
		||||
                    {
 | 
			
		||||
                        Binding = (uint)descriptor.Binding,
 | 
			
		||||
                        DescriptorType = descriptor.Type.Convert(),
 | 
			
		||||
                        DescriptorCount = (uint)descriptor.Count,
 | 
			
		||||
                        StageFlags = stages.Convert()
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
 | 
			
		||||
                SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
 | 
			
		||||
                Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
 | 
			
		||||
                Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
 | 
			
		||||
                Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
 | 
			
		||||
                Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
 | 
			
		||||
 | 
			
		||||
                iter++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
 | 
			
		||||
 | 
			
		||||
            var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = uLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)uCount,
 | 
			
		||||
                Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = sLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)stagesCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = tLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)tCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = iLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)iCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
 | 
			
		||||
 | 
			
		||||
            fixed (DescriptorSetLayout* pLayouts = layouts)
 | 
			
		||||
            {
 | 
			
		||||
                var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
 | 
			
		||||
                fixed (DescriptorSetLayoutBinding* pLayoutBindings = layoutBindings)
 | 
			
		||||
                {
 | 
			
		||||
                    SType = StructureType.PipelineLayoutCreateInfo,
 | 
			
		||||
                    PSetLayouts = pLayouts,
 | 
			
		||||
                    SetLayoutCount = PipelineBase.DescriptorSetLayouts
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return layouts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
 | 
			
		||||
        {
 | 
			
		||||
            int stagesCount = shaders.Length;
 | 
			
		||||
 | 
			
		||||
            int uCount = 0;
 | 
			
		||||
            int sCount = 0;
 | 
			
		||||
            int tCount = 0;
 | 
			
		||||
            int iCount = 0;
 | 
			
		||||
 | 
			
		||||
            foreach (var shader in shaders)
 | 
			
		||||
            {
 | 
			
		||||
                uCount += shader.Bindings.UniformBufferBindings.Count;
 | 
			
		||||
                sCount += shader.Bindings.StorageBufferBindings.Count;
 | 
			
		||||
                tCount += shader.Bindings.TextureBindings.Count;
 | 
			
		||||
                iCount += shader.Bindings.ImageBindings.Count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
 | 
			
		||||
            DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
 | 
			
		||||
 | 
			
		||||
            int uIndex = 0;
 | 
			
		||||
            int sIndex = 0;
 | 
			
		||||
            int tIndex = 0;
 | 
			
		||||
            int iIndex = 0;
 | 
			
		||||
 | 
			
		||||
            foreach (var shader in shaders)
 | 
			
		||||
            {
 | 
			
		||||
                var stageFlags = shader.Stage.Convert();
 | 
			
		||||
 | 
			
		||||
                void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var b in bds)
 | 
			
		||||
                    var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
                    {
 | 
			
		||||
                        bindings[start++] = new DescriptorSetLayoutBinding
 | 
			
		||||
                        {
 | 
			
		||||
                            Binding = (uint)b,
 | 
			
		||||
                            DescriptorType = type,
 | 
			
		||||
                            DescriptorCount = 1,
 | 
			
		||||
                            StageFlags = stageFlags
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                        SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                        PBindings = pLayoutBindings,
 | 
			
		||||
                        BindingCount = (uint)layoutBindings.Length,
 | 
			
		||||
                        Flags = usePushDescriptors && setIndex == 0 ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : DescriptorSetLayoutCreateFlags.None
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                // TODO: Support buffer textures and images here.
 | 
			
		||||
                // This is only used for the helper shaders on the backend, and we don't use buffer textures on them
 | 
			
		||||
                // so far, so it's not really necessary right now.
 | 
			
		||||
                Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
 | 
			
		||||
                Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
 | 
			
		||||
                Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
 | 
			
		||||
                Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
 | 
			
		||||
                    gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
 | 
			
		||||
 | 
			
		||||
            var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = uLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)uCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = sLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)sCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = tLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)tCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.DescriptorSetLayoutCreateInfo,
 | 
			
		||||
                PBindings = iLayoutBindings,
 | 
			
		||||
                BindingCount = (uint)iCount
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
 | 
			
		||||
            gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
 | 
			
		||||
            PipelineLayout layout;
 | 
			
		||||
 | 
			
		||||
            fixed (DescriptorSetLayout* pLayouts = layouts)
 | 
			
		||||
            {
 | 
			
		||||
@ -259,13 +76,13 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                {
 | 
			
		||||
                    SType = StructureType.PipelineLayoutCreateInfo,
 | 
			
		||||
                    PSetLayouts = pLayouts,
 | 
			
		||||
                    SetLayoutCount = PipelineBase.DescriptorSetLayouts
 | 
			
		||||
                    SetLayoutCount = (uint)layouts.Length
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return layouts;
 | 
			
		||||
            return (layouts, layout);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ryujinx.Graphics.Vulkan/ResourceBindingSegment.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    readonly struct ResourceBindingSegment
 | 
			
		||||
    {
 | 
			
		||||
        public readonly int Binding;
 | 
			
		||||
        public readonly int Count;
 | 
			
		||||
        public readonly ResourceType Type;
 | 
			
		||||
        public readonly ResourceStages Stages;
 | 
			
		||||
        public readonly ResourceAccess Access;
 | 
			
		||||
 | 
			
		||||
        public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, ResourceAccess access)
 | 
			
		||||
        {
 | 
			
		||||
            Binding = binding;
 | 
			
		||||
            Count = count;
 | 
			
		||||
            Type = type;
 | 
			
		||||
            Stages = stages;
 | 
			
		||||
            Access = access;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    class ResourceLayoutBuilder
 | 
			
		||||
    {
 | 
			
		||||
        private const int TotalSets = PipelineBase.DescriptorSetLayouts;
 | 
			
		||||
 | 
			
		||||
        private readonly List<ResourceDescriptor>[] _resourceDescriptors;
 | 
			
		||||
        private readonly List<ResourceUsage>[] _resourceUsages;
 | 
			
		||||
 | 
			
		||||
        public ResourceLayoutBuilder()
 | 
			
		||||
        {
 | 
			
		||||
            _resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
 | 
			
		||||
            _resourceUsages = new List<ResourceUsage>[TotalSets];
 | 
			
		||||
 | 
			
		||||
            for (int index = 0; index < TotalSets; index++)
 | 
			
		||||
            {
 | 
			
		||||
                _resourceDescriptors[index] = new();
 | 
			
		||||
                _resourceUsages[index] = new();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
 | 
			
		||||
        {
 | 
			
		||||
            int setIndex = type switch
 | 
			
		||||
            {
 | 
			
		||||
                ResourceType.UniformBuffer => PipelineBase.UniformSetIndex,
 | 
			
		||||
                ResourceType.StorageBuffer => PipelineBase.StorageSetIndex,
 | 
			
		||||
                ResourceType.TextureAndSampler or ResourceType.BufferTexture => PipelineBase.TextureSetIndex,
 | 
			
		||||
                ResourceType.Image or ResourceType.BufferImage => PipelineBase.ImageSetIndex,
 | 
			
		||||
                _ => throw new ArgumentException($"Invalid resource type \"{type}\".")
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            ResourceAccess access = IsReadOnlyType(type) ? ResourceAccess.Read : ResourceAccess.ReadWrite;
 | 
			
		||||
 | 
			
		||||
            _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
 | 
			
		||||
            _resourceUsages[setIndex].Add(new ResourceUsage(binding, type, stages, access));
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool IsReadOnlyType(ResourceType type)
 | 
			
		||||
        {
 | 
			
		||||
            return type == ResourceType.UniformBuffer ||
 | 
			
		||||
                   type == ResourceType.Sampler ||
 | 
			
		||||
                   type == ResourceType.TextureAndSampler ||
 | 
			
		||||
                   type == ResourceType.BufferTexture;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ResourceLayout Build()
 | 
			
		||||
        {
 | 
			
		||||
            var descriptors = new ResourceDescriptorCollection[TotalSets];
 | 
			
		||||
            var usages = new ResourceUsageCollection[TotalSets];
 | 
			
		||||
 | 
			
		||||
            for (int index = 0; index < TotalSets; index++)
 | 
			
		||||
            {
 | 
			
		||||
                descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
 | 
			
		||||
                usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -26,8 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        public ShaderStageFlags StageFlags => _stage;
 | 
			
		||||
 | 
			
		||||
        public ShaderBindings Bindings { get; }
 | 
			
		||||
 | 
			
		||||
        public ProgramLinkStatus CompileStatus { private set; get; }
 | 
			
		||||
 | 
			
		||||
        public readonly Task CompileTask;
 | 
			
		||||
@ -36,7 +34,6 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        {
 | 
			
		||||
            _api = api;
 | 
			
		||||
            _device = device;
 | 
			
		||||
            Bindings = shaderSource.Bindings;
 | 
			
		||||
 | 
			
		||||
            CompileStatus = ProgramLinkStatus.Incomplete;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        public uint Stages { get; }
 | 
			
		||||
 | 
			
		||||
        public int[][][] Bindings { get; }
 | 
			
		||||
        public ResourceBindingSegment[][] BindingSegments { get; }
 | 
			
		||||
 | 
			
		||||
        public ProgramLinkStatus LinkStatus { get; private set; }
 | 
			
		||||
 | 
			
		||||
@ -54,7 +55,13 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private Task _compileTask;
 | 
			
		||||
        private bool _firstBackgroundUse;
 | 
			
		||||
 | 
			
		||||
        public ShaderCollection(VulkanRenderer gd, Device device, ShaderSource[] shaders, SpecDescription[] specDescription = null, bool isMinimal = false)
 | 
			
		||||
        public ShaderCollection(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
            ShaderSource[] shaders,
 | 
			
		||||
            ResourceLayout resourceLayout,
 | 
			
		||||
            SpecDescription[] specDescription = null,
 | 
			
		||||
            bool isMinimal = false)
 | 
			
		||||
        {
 | 
			
		||||
            _gd = gd;
 | 
			
		||||
            _device = device;
 | 
			
		||||
@ -99,39 +106,16 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
            _shaders = internalShaders;
 | 
			
		||||
 | 
			
		||||
            bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
 | 
			
		||||
            bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
 | 
			
		||||
 | 
			
		||||
            _plce = isMinimal
 | 
			
		||||
                ? gd.PipelineLayoutCache.Create(gd, device, shaders)
 | 
			
		||||
                : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd);
 | 
			
		||||
            _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
 | 
			
		||||
 | 
			
		||||
            HasMinimalLayout = isMinimal;
 | 
			
		||||
            UsePushDescriptors = usePd;
 | 
			
		||||
            UsePushDescriptors = usePushDescriptors;
 | 
			
		||||
 | 
			
		||||
            Stages = stages;
 | 
			
		||||
 | 
			
		||||
            int[][] GrabAll(Func<ShaderBindings, IReadOnlyCollection<int>> selector)
 | 
			
		||||
            {
 | 
			
		||||
                bool hasAny = false;
 | 
			
		||||
                int[][] bindings = new int[internalShaders.Length][];
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < internalShaders.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    var collection = selector(internalShaders[i].Bindings);
 | 
			
		||||
                    hasAny |= collection.Count != 0;
 | 
			
		||||
                    bindings[i] = collection.ToArray();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return hasAny ? bindings : Array.Empty<int[]>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Bindings = new[]
 | 
			
		||||
            {
 | 
			
		||||
                GrabAll(x => x.UniformBufferBindings),
 | 
			
		||||
                GrabAll(x => x.StorageBufferBindings),
 | 
			
		||||
                GrabAll(x => x.TextureBindings),
 | 
			
		||||
                GrabAll(x => x.ImageBindings)
 | 
			
		||||
            };
 | 
			
		||||
            BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
 | 
			
		||||
 | 
			
		||||
            _compileTask = Task.CompletedTask;
 | 
			
		||||
            _firstBackgroundUse = false;
 | 
			
		||||
@ -141,8 +125,9 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
            ShaderSource[] sources,
 | 
			
		||||
            ResourceLayout resourceLayout,
 | 
			
		||||
            ProgramPipelineState state,
 | 
			
		||||
            bool fromCache) : this(gd, device, sources)
 | 
			
		||||
            bool fromCache) : this(gd, device, sources, resourceLayout)
 | 
			
		||||
        {
 | 
			
		||||
            _state = state;
 | 
			
		||||
 | 
			
		||||
@ -150,6 +135,67 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _firstBackgroundUse = !fromCache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
 | 
			
		||||
        {
 | 
			
		||||
            ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
 | 
			
		||||
 | 
			
		||||
            for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
 | 
			
		||||
            {
 | 
			
		||||
                List<ResourceBindingSegment> currentSegments = new List<ResourceBindingSegment>();
 | 
			
		||||
 | 
			
		||||
                ResourceUsage currentUsage = default;
 | 
			
		||||
                int currentCount = 0;
 | 
			
		||||
 | 
			
		||||
                for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
 | 
			
		||||
                {
 | 
			
		||||
                    ResourceUsage usage = setUsages[setIndex].Usages[index];
 | 
			
		||||
 | 
			
		||||
                    // If the resource is not accessed, we don't need to update it.
 | 
			
		||||
                    if (usage.Access == ResourceAccess.None)
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (currentUsage.Binding + currentCount != usage.Binding ||
 | 
			
		||||
                        currentUsage.Type != usage.Type ||
 | 
			
		||||
                        currentUsage.Stages != usage.Stages ||
 | 
			
		||||
                        currentUsage.Access != usage.Access)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (currentCount != 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            currentSegments.Add(new ResourceBindingSegment(
 | 
			
		||||
                                currentUsage.Binding,
 | 
			
		||||
                                currentCount,
 | 
			
		||||
                                currentUsage.Type,
 | 
			
		||||
                                currentUsage.Stages,
 | 
			
		||||
                                currentUsage.Access));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        currentUsage = usage;
 | 
			
		||||
                        currentCount = 1;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        currentCount++;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (currentCount != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    currentSegments.Add(new ResourceBindingSegment(
 | 
			
		||||
                        currentUsage.Binding,
 | 
			
		||||
                        currentCount,
 | 
			
		||||
                        currentUsage.Type,
 | 
			
		||||
                        currentUsage.Stages,
 | 
			
		||||
                        currentUsage.Access));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                segments[setIndex] = currentSegments.ToArray();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return segments;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task BackgroundCompilation()
 | 
			
		||||
        {
 | 
			
		||||
            await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,6 @@ using Silk.NET.Vulkan.Extensions.EXT;
 | 
			
		||||
using Silk.NET.Vulkan.Extensions.KHR;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
@ -398,17 +397,17 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
            if (info.State.HasValue || isCompute)
 | 
			
		||||
            {
 | 
			
		||||
                return new ShaderCollection(this, _device, sources, info.State ?? default, info.FromCache);
 | 
			
		||||
                return new ShaderCollection(this, _device, sources, info.ResourceLayout, info.State ?? default, info.FromCache);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new ShaderCollection(this, _device, sources);
 | 
			
		||||
                return new ShaderCollection(this, _device, sources, info.ResourceLayout);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, SpecDescription[] specDescription = null)
 | 
			
		||||
        internal ShaderCollection CreateProgramWithMinimalLayout(ShaderSource[] sources, ResourceLayout resourceLayout, SpecDescription[] specDescription = null)
 | 
			
		||||
        {
 | 
			
		||||
            return new ShaderCollection(this, _device, sources, specDescription: specDescription, isMinimal: true);
 | 
			
		||||
            return new ShaderCollection(this, _device, sources, resourceLayout, specDescription, isMinimal: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ISampler CreateSampler(GAL.SamplerCreateInfo info)
 | 
			
		||||
@ -658,7 +657,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
 | 
			
		||||
        internal GAL.PrimitiveTopology TopologyRemap(GAL.PrimitiveTopology topology)
 | 
			
		||||
        {
 | 
			
		||||
            return topology switch
 | 
			
		||||
            {
 | 
			
		||||
@ -669,7 +668,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool TopologyUnsupported(GAL.PrimitiveTopology topology)
 | 
			
		||||
        internal bool TopologyUnsupported(GAL.PrimitiveTopology topology)
 | 
			
		||||
        {
 | 
			
		||||
            return topology switch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user