diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu')
6 files changed, 279 insertions, 60 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index b4764d57..9f263e9d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -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); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 58e5c7b1..d80518b1 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -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); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs index 77e52667..2dc5c971 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs @@ -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>()); - } } }
\ No newline at end of file diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 7db627ba..e4e1e0d6 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -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 diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index d543f42a..b7ba159a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -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> @@ -718,25 +721,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. /// </summary> diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs new file mode 100644 index 00000000..39b31cf6 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -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); + } + } +}
\ No newline at end of file |