From fd2e64c0c74395c3e9c62ea7b3e4794624b9a979 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:24:10 +0100 Subject: [PATCH] Metal: Better Bindings (#29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tell GAL to use Vk model (and break everything) * ResourceBindingSegments * Set information on backend caps * Get ready to break everything * Refactor EncoderStateManager * Remove padding from helper shaders * Fix ref array sizes * Seperate vert & frag buffers * Shader-side changes * Fixes * Fix some helper shader resource layouts * Sort by binding id * Fix helper shader layouts * Don’t do inline vertex buffer updates * Check for null storage --- .../Shader/GpuAccessorBase.cs | 8 +- src/Ryujinx.Graphics.Metal/Constants.cs | 6 +- src/Ryujinx.Graphics.Metal/EncoderState.cs | 41 +- .../EncoderStateManager.cs | 658 +++++++++++------- src/Ryujinx.Graphics.Metal/HelperShader.cs | 20 +- src/Ryujinx.Graphics.Metal/MetalRenderer.cs | 17 +- src/Ryujinx.Graphics.Metal/Program.cs | 157 ++++- .../ResourceBindingSegment.cs | 22 + .../ResourceLayoutBuilder.cs | 59 ++ src/Ryujinx.Graphics.Metal/Shaders/Blit.metal | 126 ---- .../Shaders/ChangeBufferStride.metal | 1 - .../CodeGen/Msl/Declarations.cs | 47 +- 12 files changed, 709 insertions(+), 453 deletions(-) create mode 100644 src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs create mode 100644 src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index d89eebabf..701ff764a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer"); } @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { if (count == 1) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); } @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api == TargetApi.Vulkan) + if (_context.Capabilities.Api != TargetApi.OpenGL) { if (count == 1) { diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 032815359..1ee24e308 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Metal public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; public const int MaxTexturesPerStage = 64; + public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; + public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; // TODO: Check this value @@ -18,9 +20,11 @@ namespace Ryujinx.Graphics.Metal public const int MinResourceAlignment = 16; // Must match constants set in shader generation + public const uint ZeroBufferIndex = 18; + public const uint ConstantBuffersIndex = 20; public const uint StorageBuffersIndex = 21; - public const uint ZeroBufferIndex = 18; public const uint TexturesIndex = 22; + public const uint ImagessIndex = 23; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 95f0f21b5..448c9eaa2 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Metal.State; +using Ryujinx.Graphics.Shader; using SharpMetal.Metal; using System; using System.Linq; @@ -22,13 +23,13 @@ namespace Ryujinx.Graphics.Metal StencilRef = 1 << 7, Viewports = 1 << 8, Scissors = 1 << 9, - Buffers = 1 << 10, - VertexTextures = 1 << 11, - FragmentTextures = 1 << 12, - ComputeTextures = 1 << 13, + Uniforms = 1 << 10, + Storages = 1 << 11, + Textures = 1 << 12, + Images = 1 << 13, - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Buffers | VertexTextures | FragmentTextures, - ComputeAll = ComputePipeline | Buffers | ComputeTextures, + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Uniforms | Storages | Textures | Images, + ComputeAll = ComputePipeline | Uniforms | Storages | Textures | Images, All = RenderAll | ComputeAll, } @@ -49,6 +50,20 @@ namespace Ryujinx.Graphics.Metal } } + record struct TextureRef + { + public ShaderStage Stage; + public Texture Storage; + public Sampler Sampler; + + public TextureRef(ShaderStage stage, Texture storage, Sampler sampler) + { + Stage = stage; + Storage = storage; + Sampler = sampler; + } + } + struct PredrawState { public MTLCullMode CullMode; @@ -73,17 +88,9 @@ namespace Ryujinx.Graphics.Metal public PipelineState Pipeline; public DepthStencilUid DepthStencilUid; - public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTexturesPerStage]; - public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - - public BufferRef[] UniformBuffers = new BufferRef[Constants.MaxUniformBuffersPerStage]; - public BufferRef[] StorageBuffers = new BufferRef[Constants.MaxStorageBuffersPerStage]; + public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; + public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; + public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; public Auto IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index b76170068..dd0502f23 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -179,8 +179,8 @@ namespace Ryujinx.Graphics.Metal { if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) { - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); SetRenderPipelineState(renderCommandEncoder); + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); } if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil)) @@ -223,21 +223,26 @@ namespace Ryujinx.Graphics.Metal SetScissors(renderCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) { - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.VertexTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) { - SetRenderTextures(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.FragmentTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) { - SetRenderTextures(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); } + // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + // { + // UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); + // } + _currentState.Dirty &= ~DirtyFlags.RenderAll; } @@ -248,15 +253,27 @@ namespace Ryujinx.Graphics.Metal SetComputePipelineState(computeCommandEncoder); } - if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) { - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.UniformSetIndex); } - if (_currentState.Dirty.HasFlag(DirtyFlags.ComputeTextures)) + if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) { - SetComputeTextures(computeCommandEncoder, _currentState.ComputeTextures, _currentState.ComputeSamplers); + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.StorageSetIndex); } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) + { + UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex); + } + + // if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) + // { + // UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); + // } + + _currentState.Dirty &= ~DirtyFlags.ComputeAll; } private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) @@ -694,10 +711,10 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.UniformBuffers[index] = new BufferRef(mtlBuffer, ref buffer); + _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Uniforms; } public void UpdateStorageBuffers(ReadOnlySpan buffers) @@ -711,10 +728,10 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer, ref buffer); + _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Storages; } public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) @@ -724,10 +741,10 @@ namespace Ryujinx.Graphics.Metal var mtlBuffer = buffers[i]; int index = first + i; - _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer); + _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); } - _currentState.Dirty |= DirtyFlags.Buffers; + _currentState.Dirty |= DirtyFlags.Storages; } // Inlineable @@ -786,63 +803,22 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.StencilRef; } - public void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) - { - if (binding > Constants.MaxTexturesPerStage) - { - Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); - return; - } - - switch (stage) - { - case ShaderStage.Fragment: - _currentState.FragmentTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.FragmentTextures; - break; - case ShaderStage.Vertex: - _currentState.VertexTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.VertexTextures; - break; - case ShaderStage.Compute: - _currentState.ComputeTextures[binding] = texture; - _currentState.Dirty |= DirtyFlags.ComputeTextures; - break; - } - } - - public void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) - { - if (binding > Constants.MaxTexturesPerStage) - { - Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); - return; - } - switch (stage) - { - case ShaderStage.Fragment: - _currentState.FragmentSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.FragmentTextures; - break; - case ShaderStage.Vertex: - _currentState.VertexSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.VertexTextures; - break; - case ShaderStage.Compute: - _currentState.ComputeSamplers[binding] = sampler; - _currentState.Dirty |= DirtyFlags.ComputeTextures; - break; - } - } - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { - UpdateTexture(stage, binding, texture); - - if (sampler != null) + if (texture is TextureBuffer textureBuffer) { - UpdateSampler(stage, binding, sampler.GetSampler()); + // TODO: Texture buffers } + else if (texture is Texture view) + { + _currentState.TextureRefs[binding] = new(stage, view, sampler); + } + else + { + _currentState.TextureRefs[binding] = default; + } + + _currentState.Dirty |= DirtyFlags.Textures; } private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) @@ -999,119 +975,373 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) + private void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) { - var uniformArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, uniformBuffers, true); - var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + var bindingSegments = program.BindingSegments[setIndex]; - renderCommandEncoder.SetVertexBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - renderCommandEncoder.SetFragmentBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - - var storageArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, storageBuffers, false); - var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; - - renderCommandEncoder.SetVertexBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - renderCommandEncoder.SetFragmentBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - } - - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) - { - var uniformArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, uniformBuffers, true); - var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex); - - - var storageArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, storageBuffers, false); - var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex); - } - - private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool constant) - { - var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - - Span resourceIds = stackalloc ulong[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + if (bindingSegments.Length == 0) { - var range = buffers[i].Range; - var autoBuffer = buffers[i].Buffer; - var offset = 0; - - if (autoBuffer == null) - { - continue; - } - - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage, MTLRenderStages.RenderStageFragment | MTLRenderStages.RenderStageVertex); - resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; + return; } - var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + var vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + var fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); + Span vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; + Span fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]]; - return argBuffer.Range; - } + var vertResourceIdIndex = 0; + var fragResourceIdIndex = 0; - private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers, bool constant) - { - var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; - - Span resourceIds = stackalloc ulong[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (ResourceBindingSegment segment in bindingSegments) { - var range = buffers[i].Range; - var autoBuffer = buffers[i].Buffer; - var offset = 0; + int binding = segment.Binding; + int count = segment.Count; - if (autoBuffer == null) + switch (setIndex) { - continue; + case MetalRenderer.UniformSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + } + break; + case MetalRenderer.StorageSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + vertResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + fragResourceIdIndex++; + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); + } + break; + case MetalRenderer.TextureSetIndex: + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + MTLRenderStages renderStages = 0; + + if (segment.Stages.HasFlag(ResourceStages.Vertex)) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + + if (texture.Sampler != null) + { + vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + vertResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if (segment.Stages.HasFlag(ResourceStages.Fragment)) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + + if (texture.Sampler != null) + { + fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + fragResourceIdIndex++; + } + + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages); + } + } + else + { + // TODO: Buffer textures + } + } + else + { + // TODO: Texture arrays + } + break; + case MetalRenderer.ImageSetIndex: + // TODO: Images + break; } - - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage); - resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } - var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); + fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); + + var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; + + renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); + } + + private void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) + { + var bindingSegments = program.BindingSegments[setIndex]; + + if (bindingSegments.Length == 0) + { + return; + } + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); + Span resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; + var resourceIdIndex = 0; + + foreach (ResourceBindingSegment segment in bindingSegments) + { + int binding = segment.Binding; + int count = segment.Count; + + switch (setIndex) + { + case MetalRenderer.UniformSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resourceIdIndex++; + } + } + break; + case MetalRenderer.StorageSetIndex: + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; + + var range = buffer.Range; + var autoBuffer = buffer.Buffer; + var offset = 0; + + if (autoBuffer == null) + { + continue; + } + + MTLBuffer mtlBuffer; + + if (range.HasValue) + { + offset = range.Value.Offset; + mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; + + } + else + { + mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; + } + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; + resourceIdIndex++; + } + } + break; + case MetalRenderer.TextureSetIndex: + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var texture = ref _currentState.TextureRefs[index]; + + var storage = texture.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + + if (texture.Sampler != null) + { + resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl; + resourceIdIndex++; + } + } + } + } + else + { + // TODO: Buffer textures + } + } + else + { + // TODO: Texture arrays + } + break; + case MetalRenderer.ImageSetIndex: + // TODO: Images + break; + } + } - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - return argBuffer.Range; + var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; + + computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex)); } + private uint SetIndexToBindingIndex(int setIndex) + { + return setIndex switch + { + MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, + MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, + MetalRenderer.TextureSetIndex => Constants.TexturesIndex, + MetalRenderer.ImageSetIndex => Constants.ImagessIndex, + }; + } + + private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) { renderCommandEncoder.SetCullMode(_currentState.CullMode); @@ -1126,105 +1356,5 @@ namespace Ryujinx.Graphics.Metal { renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); } - - private readonly void SetRenderTextures(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) - { - var argBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, stage, textures, samplers); - var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - switch (stage) - { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - break; - } - } - - private readonly void SetComputeTextures(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) - { - var argBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, textures, samplers); - var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - - computeCommandEncoder.SetBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex); - } - - private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) - { - var renderStage = stage == ShaderStage.Vertex ? MTLRenderStages.RenderStageVertex : MTLRenderStages.RenderStageFragment; - - Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; - - for (int i = 0; i < textures.Length; i++) - { - if (textures[i] == null) - { - continue; - } - - var mtlTexture = textures[i].GetHandle(); - - renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStage); - resourceIds[i] = mtlTexture.GpuResourceID._impl; - } - - for (int i = 0; i < samplers.Length; i++) - { - if (samplers[i].NativePtr == IntPtr.Zero) - { - continue; - } - - var sampler = samplers[i]; - - resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; - } - - var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); - - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - - return argBuffer.Range; - } - - private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) - { - Span resourceIds = stackalloc ulong[textures.Length + samplers.Length]; - - for (int i = 0; i < textures.Length; i++) - { - if (textures[i] == null) - { - continue; - } - - var mtlTexture = textures[i].GetHandle(); - - computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); - resourceIds[i] = mtlTexture.GpuResourceID._impl; - } - - for (int i = 0; i < samplers.Length; i++) - { - if (samplers[i].NativePtr == IntPtr.Zero) - { - continue; - } - - var sampler = samplers[i]; - - resourceIds[i + textures.Length] = sampler.GpuResourceID._impl; - } - - var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length); - - var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - - return argBuffer.Range; - } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index 54ba9889d..d65aafc3e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -36,12 +36,19 @@ namespace Ryujinx.Graphics.Metal _samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); _samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + var blitResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); + var blitSource = ReadMsl("Blit.metal"); _programColorBlit = new Program( [ new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device); + ], blitResourceLayout, device); + + var colorClearResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); var colorClearSource = ReadMsl("ColorClear.metal"); for (int i = 0; i < Constants.MaxColorAttachments; i++) @@ -51,7 +58,7 @@ namespace Ryujinx.Graphics.Metal [ new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device)); + ], colorClearResourceLayout, device)); } var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); @@ -59,13 +66,18 @@ namespace Ryujinx.Graphics.Metal [ new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], device); + ], colorClearResourceLayout, device); + + var strideChangeResourceLayout = new ResourceLayoutBuilder() + .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); var strideChangeSource = ReadMsl("ChangeBufferStride.metal"); _programStrideChange = new Program( [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], device, new ComputeSize(64, 1, 1)); + ], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1)); } private static string ReadMsl(string fileName) diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index d9ac0118c..4d744bbe4 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -12,6 +12,13 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] public sealed class MetalRenderer : IRenderer { + public const int TotalSets = 4; + + public const int UniformSetIndex = 0; + public const int StorageSetIndex = 1; + public const int TextureSetIndex = 2; + public const int ImageSetIndex = 3; + private readonly MTLDevice _device; private readonly MTLCommandQueue _queue; private readonly Func _getMetalLayer; @@ -95,7 +102,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - return new Program(shaders, _device, info.ComputeLocalSize); + return new Program(shaders, info.ResourceLayout, _device, info.ComputeLocalSize); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -188,10 +195,10 @@ namespace Ryujinx.Graphics.Metal supportsViewportSwizzle: false, supportsIndirectParameters: true, supportsDepthClipControl: false, - uniformBufferSetIndex: 0, - storageBufferSetIndex: 1, - textureSetIndex: 2, - imageSetIndex: 3, + uniformBufferSetIndex: UniformSetIndex, + storageBufferSetIndex: StorageSetIndex, + textureSetIndex: TextureSetIndex, + imageSetIndex: ImageSetIndex, extraSetBaseIndex: 0, maximumExtraSets: 0, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 5635b711c..8f289392f 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -4,6 +4,8 @@ using Ryujinx.Graphics.Shader; using SharpMetal.Foundation; using SharpMetal.Metal; using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal @@ -21,7 +23,14 @@ namespace Ryujinx.Graphics.Metal private MTLComputePipelineState? _computePipelineCache; private bool _firstBackgroundUse; - public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default) + public ResourceBindingSegment[][] ClearSegments { get; } + public ResourceBindingSegment[][] BindingSegments { get; } + // Argument buffer sizes for Vertex or Compute stages + public int[] ArgumentBufferSizes { get; } + // Argument buffer sizes for Fragment stage + public int[] FragArgumentBufferSizes { get; } + + public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default) { ComputeLocalSize = computeLocalSize; @@ -56,9 +65,155 @@ namespace Ryujinx.Graphics.Metal } } + ClearSegments = BuildClearSegments(resourceLayout.Sets); + (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); + _status = ProgramLinkStatus.Success; } + private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection sets) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; + + for (int setIndex = 0; setIndex < sets.Count; setIndex++) + { + List currentSegments = new(); + + ResourceDescriptor currentDescriptor = default; + int currentCount = 0; + + for (int index = 0; index < sets[setIndex].Descriptors.Count; index++) + { + ResourceDescriptor descriptor = sets[setIndex].Descriptors[index]; + + if (currentDescriptor.Binding + currentCount != descriptor.Binding || + currentDescriptor.Type != descriptor.Type || + currentDescriptor.Stages != descriptor.Stages || + currentDescriptor.Count > 1 || + descriptor.Count > 1) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + currentDescriptor.Count > 1)); + } + + currentDescriptor = descriptor; + currentCount = descriptor.Count; + } + else + { + currentCount += descriptor.Count; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentDescriptor.Binding, + currentCount, + currentDescriptor.Type, + currentDescriptor.Stages, + currentDescriptor.Count > 1)); + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return segments; + } + + private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection setUsages) + { + ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; + int[] argBufferSizes = new int[setUsages.Count]; + int[] fragArgBufferSizes = new int[setUsages.Count]; + + for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) + { + List currentSegments = new(); + + ResourceUsage currentUsage = default; + int currentCount = 0; + + for (int index = 0; index < setUsages[setIndex].Usages.Count; index++) + { + ResourceUsage usage = setUsages[setIndex].Usages[index]; + + if (currentUsage.Binding + currentCount != usage.Binding || + currentUsage.Type != usage.Type || + currentUsage.Stages != usage.Stages || + currentUsage.ArrayLength > 1 || + usage.ArrayLength > 1) + { + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.ArrayLength > 1)); + + var size = currentCount * ResourcePointerSize(currentUsage.Type); + if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) + { + fragArgBufferSizes[setIndex] += size; + } + + if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || + currentUsage.Stages.HasFlag(ResourceStages.Compute)) + { + argBufferSizes[setIndex] += size; + } + } + + currentUsage = usage; + currentCount = usage.ArrayLength; + } + else + { + currentCount++; + } + } + + if (currentCount != 0) + { + currentSegments.Add(new ResourceBindingSegment( + currentUsage.Binding, + currentCount, + currentUsage.Type, + currentUsage.Stages, + currentUsage.ArrayLength > 1)); + + var size = currentCount * ResourcePointerSize(currentUsage.Type); + if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) + { + fragArgBufferSizes[setIndex] += size; + } + + if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || + currentUsage.Stages.HasFlag(ResourceStages.Compute)) + { + argBufferSizes[setIndex] += size; + } + } + + segments[setIndex] = currentSegments.ToArray(); + } + + return (segments, argBufferSizes, fragArgBufferSizes); + } + + private static int ResourcePointerSize(ResourceType type) + { + return (type == ResourceType.TextureAndSampler ? 2 : 1); + } + public ProgramLinkStatus CheckProgramLink(bool blocking) { return _status; diff --git a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs new file mode 100644 index 000000000..8e6d88c4b --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Metal +{ + readonly struct ResourceBindingSegment + { + public readonly int Binding; + public readonly int Count; + public readonly ResourceType Type; + public readonly ResourceStages Stages; + public readonly bool IsArray; + + public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray) + { + Binding = binding; + Count = count; + Type = type; + Stages = stages; + IsArray = isArray; + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs new file mode 100644 index 000000000..24ba1b6e6 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs @@ -0,0 +1,59 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + class ResourceLayoutBuilder + { + private const int TotalSets = MetalRenderer.TotalSets; + + private readonly List[] _resourceDescriptors; + private readonly List[] _resourceUsages; + + public ResourceLayoutBuilder() + { + _resourceDescriptors = new List[TotalSets]; + _resourceUsages = new List[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 => MetalRenderer.UniformSetIndex, + ResourceType.StorageBuffer => MetalRenderer.StorageSetIndex, + ResourceType.TextureAndSampler or ResourceType.BufferTexture => MetalRenderer.TextureSetIndex, + ResourceType.Image or ResourceType.BufferImage => MetalRenderer.ImageSetIndex, + _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), + }; + + _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); + + return this; + } + + 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()); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 3c40af737..37962bbf7 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -18,133 +18,7 @@ struct ConstantBuffers { struct Textures { texture2d texture; - ulong padding_1; - ulong padding_2; - ulong padding_3; - ulong padding_4; - ulong padding_5; - ulong padding_6; - ulong padding_7; - ulong padding_8; - ulong padding_9; - ulong padding_10; - ulong padding_11; - ulong padding_12; - ulong padding_13; - ulong padding_14; - ulong padding_15; - ulong padding_16; - ulong padding_17; - ulong padding_18; - ulong padding_19; - ulong padding_20; - ulong padding_21; - ulong padding_22; - ulong padding_23; - ulong padding_24; - ulong padding_25; - ulong padding_26; - ulong padding_27; - ulong padding_28; - ulong padding_29; - ulong padding_30; - ulong padding_31; - ulong padding_32; - ulong padding_33; - ulong padding_34; - ulong padding_35; - ulong padding_36; - ulong padding_37; - ulong padding_38; - ulong padding_39; - ulong padding_40; - ulong padding_41; - ulong padding_42; - ulong padding_43; - ulong padding_44; - ulong padding_45; - ulong padding_46; - ulong padding_47; - ulong padding_48; - ulong padding_49; - ulong padding_50; - ulong padding_51; - ulong padding_52; - ulong padding_53; - ulong padding_54; - ulong padding_55; - ulong padding_56; - ulong padding_57; - ulong padding_58; - ulong padding_59; - ulong padding_60; - ulong padding_61; - ulong padding_62; - ulong padding_63; sampler sampler; - ulong padding_65; - ulong padding_66; - ulong padding_67; - ulong padding_68; - ulong padding_69; - ulong padding_70; - ulong padding_71; - ulong padding_72; - ulong padding_73; - ulong padding_74; - ulong padding_75; - ulong padding_76; - ulong padding_77; - ulong padding_78; - ulong padding_79; - ulong padding_80; - ulong padding_81; - ulong padding_82; - ulong padding_83; - ulong padding_84; - ulong padding_85; - ulong padding_86; - ulong padding_87; - ulong padding_88; - ulong padding_89; - ulong padding_90; - ulong padding_91; - ulong padding_92; - ulong padding_93; - ulong padding_94; - ulong padding_95; - ulong padding_96; - ulong padding_97; - ulong padding_98; - ulong padding_99; - ulong padding_100; - ulong padding_101; - ulong padding_102; - ulong padding_103; - ulong padding_104; - ulong padding_105; - ulong padding_106; - ulong padding_107; - ulong padding_108; - ulong padding_109; - ulong padding_110; - ulong padding_111; - ulong padding_112; - ulong padding_113; - ulong padding_114; - ulong padding_115; - ulong padding_116; - ulong padding_117; - ulong padding_118; - ulong padding_119; - ulong padding_120; - ulong padding_121; - ulong padding_122; - ulong padding_123; - ulong padding_124; - ulong padding_125; - ulong padding_126; - ulong padding_127; }; vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 38eedefb7..492a27d21 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -19,7 +19,6 @@ struct ConstantBuffers { }; struct StorageBuffers { - ulong padding; device InData* in_data; device OutData* out_data; }; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 5fac994b3..18cf36968 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -164,16 +164,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers, bool constant) { var name = constant ? "ConstantBuffers" : "StorageBuffers"; - var count = constant ? Defaults.MaxUniformBuffersPerStage : Defaults.MaxStorageBuffersPerStage; var addressSpace = constant ? "constant" : "device"; - var argBufferPointers = new string[count]; + List argBufferPointers = []; - foreach (BufferDefinition buffer in buffers) + // TODO: Avoid Linq if we can + var sortedBuffers = buffers.OrderBy(x => x.Binding).ToArray(); + + foreach (BufferDefinition buffer in sortedBuffers) { var needsPadding = buffer.Layout == BufferLayout.Std140; - argBufferPointers[buffer.Binding] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"; + argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"); context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); @@ -211,18 +213,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine($"struct {name}"); context.EnterScope(); - for (int i = 0; i < argBufferPointers.Length; i++) + foreach (var pointer in argBufferPointers) { - if (argBufferPointers[i] == null) - { - // We need to pad the struct definition in order to read - // non-contiguous resources correctly. - context.AppendLine($"ulong padding_{i};"); - } - else - { - context.AppendLine(argBufferPointers[i]); - } + context.AppendLine(pointer); } context.LeaveScope(";"); @@ -234,31 +227,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine("struct Textures"); context.EnterScope(); - var argBufferPointers = new string[Defaults.MaxTexturesPerStage * 2]; + List argBufferPointers = []; - foreach (TextureDefinition texture in textures) + // TODO: Avoid Linq if we can + var sortedTextures = textures.OrderBy(x => x.Binding).ToArray(); + + foreach (TextureDefinition texture in sortedTextures) { var textureTypeName = texture.Type.ToMslTextureType(); - argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; + argBufferPointers.Add($"{textureTypeName} tex_{texture.Name};"); if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) { - argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; + argBufferPointers.Add($"sampler samp_{texture.Name};"); } } - for (int i = 0; i < argBufferPointers.Length; i++) + foreach (var pointer in argBufferPointers) { - if (argBufferPointers[i] == null) - { - // We need to pad the struct definition in order to read - // non-contiguous resources correctly. - context.AppendLine($"ulong padding_{i};"); - } - else - { - context.AppendLine(argBufferPointers[i]); - } + context.AppendLine(pointer); } context.LeaveScope(";");