diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index e1f858a847..032815359f 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -4,19 +4,23 @@ namespace Ryujinx.Graphics.Metal { // TODO: Check these values, these were largely copied from Vulkan public const int MaxShaderStages = 5; + public const int MaxVertexBuffers = 16; public const int MaxUniformBuffersPerStage = 18; public const int MaxStorageBuffersPerStage = 16; public const int MaxTexturesPerStage = 64; - public const int MaxCommandBuffersPerQueue = 16; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxColorAttachments = 8; // TODO: Check this value public const int MaxVertexAttributes = 31; // TODO: Check this value public const int MaxVertexLayouts = 31; - public const int MaxTextures = 31; - public const int MaxSamplers = 16; public const int MinResourceAlignment = 16; + + // Must match constants set in shader generation + public const uint ConstantBuffersIndex = 20; + public const uint StorageBuffersIndex = 21; + public const uint ZeroBufferIndex = 18; + public const uint TexturesIndex = 22; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 72422c70d7..1ba7e26201 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -1,42 +1,49 @@ using Ryujinx.Graphics.GAL; using SharpMetal.Metal; +using System; using System.Linq; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { - struct DirtyFlags + [Flags] + enum DirtyFlags { - public bool RenderPipeline = false; - public bool ComputePipeline = false; - public bool DepthStencil = false; + None = 0, + RenderPipeline = 1 << 0, + ComputePipeline = 1 << 1, + DepthStencil = 1 << 2, + DepthClamp = 1 << 3, + DepthBias = 1 << 4, + CullMode = 1 << 5, + FrontFace = 1 << 6, + StencilRef = 1 << 7, + Viewports = 1 << 8, + Scissors = 1 << 9, + VertexBuffers = 1 << 10, + Buffers = 1 << 11, + VertexTextures = 1 << 12, + FragmentTextures = 1 << 13, + ComputeTextures = 1 << 14, - public DirtyFlags() { } - - public void MarkAll() - { - RenderPipeline = true; - ComputePipeline = true; - DepthStencil = true; - } + RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | VertexBuffers | Buffers | VertexTextures | FragmentTextures, + ComputeAll = ComputePipeline | Buffers | ComputeTextures, + All = RenderAll | ComputeAll, } record struct BufferRef { public Auto Buffer; - public int Index; public BufferRange? Range; - public BufferRef(Auto buffer, int index) + public BufferRef(Auto buffer) { Buffer = buffer; - Index = index; } - public BufferRef(Auto buffer, int index, ref BufferRange range) + public BufferRef(Auto buffer, ref BufferRange range) { Buffer = buffer; - Index = index; Range = range; } } @@ -48,17 +55,17 @@ namespace Ryujinx.Graphics.Metal public MTLFunction? FragmentFunction = null; public MTLFunction? ComputeFunction = null; - public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTextures]; - public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxSamplers]; + public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTexturesPerStage]; + public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage]; - public BufferRef[] UniformBuffers = []; - public BufferRef[] StorageBuffers = []; + public BufferRef[] UniformBuffers = new BufferRef[Constants.MaxUniformBuffersPerStage]; + public BufferRef[] StorageBuffers = new BufferRef[Constants.MaxStorageBuffersPerStage]; public Auto IndexBuffer = default; public MTLIndexType IndexType = MTLIndexType.UInt16; @@ -99,7 +106,7 @@ namespace Ryujinx.Graphics.Metal public VertexAttribDescriptor[] VertexAttribs = []; // Dirty flags - public DirtyFlags Dirty = new(); + public DirtyFlags Dirty = DirtyFlags.None; // Only to be used for present public bool ClearLoadAction = false; @@ -119,6 +126,8 @@ namespace Ryujinx.Graphics.Metal clone.BlendDescriptors = (BlendDescriptor?[])BlendDescriptors.Clone(); clone.VertexBuffers = (VertexBufferDescriptor[])VertexBuffers.Clone(); clone.VertexAttribs = (VertexAttribDescriptor[])VertexAttribs.Clone(); + clone.UniformBuffers = (BufferRef[])UniformBuffers.Clone(); + clone.StorageBuffers = (BufferRef[])StorageBuffers.Clone(); return clone; } diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 52d8df57cd..218e378b07 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -79,21 +79,8 @@ namespace Ryujinx.Graphics.Metal { _currentState = _backStates.Pop(); - // Set all the inline state, since it might have changed - var renderCommandEncoder = _pipeline.GetOrCreateRenderEncoder(); - SetDepthClamp(renderCommandEncoder); - SetDepthBias(renderCommandEncoder); - SetScissors(renderCommandEncoder); - SetViewports(renderCommandEncoder); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - SetCullMode(renderCommandEncoder); - SetFrontFace(renderCommandEncoder); - SetStencilRefValue(renderCommandEncoder); - // Mark the other state as dirty - _currentState.Dirty.MarkAll(); + _currentState.Dirty |= DirtyFlags.All; } else { @@ -165,29 +152,7 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); // Mark all state as dirty to ensure it is set on the encoder - _currentState.Dirty.MarkAll(); - - // Rebind all the state - SetDepthClamp(renderCommandEncoder); - SetDepthBias(renderCommandEncoder); - SetCullMode(renderCommandEncoder); - SetFrontFace(renderCommandEncoder); - SetStencilRefValue(renderCommandEncoder); - SetViewports(renderCommandEncoder); - SetScissors(renderCommandEncoder); - SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - for (ulong i = 0; i < Constants.MaxTextures; i++) - { - SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexTextures[i]); - SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentTextures[i]); - } - for (ulong i = 0; i < Constants.MaxSamplers; i++) - { - SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, i, _currentState.VertexSamplers[i]); - SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, i, _currentState.FragmentSamplers[i]); - } + _currentState.Dirty |= DirtyFlags.RenderAll; // Cleanup renderPassDescriptor.Dispose(); @@ -195,22 +160,13 @@ namespace Ryujinx.Graphics.Metal return renderCommandEncoder; } - public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() + public MTLComputeCommandEncoder CreateComputeCommandEncoder() { var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); - // Rebind all the state - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - for (ulong i = 0; i < Constants.MaxTextures; i++) - { - SetComputeTexture(computeCommandEncoder, i, _currentState.ComputeTextures[i]); - } - for (ulong i = 0; i < Constants.MaxSamplers; i++) - { - SetComputeSampler(computeCommandEncoder, i, _currentState.ComputeSamplers[i]); - } + // Mark all state as dirty to ensure it is set on the encoder + _currentState.Dirty |= DirtyFlags.ComputeAll; // Cleanup descriptor.Dispose(); @@ -220,33 +176,93 @@ namespace Ryujinx.Graphics.Metal public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) { - if (_currentState.Dirty.RenderPipeline) + if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline)) { SetRenderPipelineState(renderCommandEncoder); } - if (_currentState.Dirty.DepthStencil) + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil)) { SetDepthStencilState(renderCommandEncoder); } - // Clear the dirty flags - _currentState.Dirty.RenderPipeline = false; - _currentState.Dirty.DepthStencil = false; + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthClamp)) + { + SetDepthClamp(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.DepthBias)) + { + SetDepthBias(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.CullMode)) + { + SetCullMode(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.FrontFace)) + { + SetFrontFace(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.StencilRef)) + { + SetStencilRefValue(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Viewports)) + { + SetViewports(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Scissors)) + { + SetScissors(renderCommandEncoder); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.VertexBuffers)) + { + SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + { + SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.VertexTextures)) + { + SetRenderTextures(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.FragmentTextures)) + { + SetRenderTextures(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + } + + _currentState.Dirty &= ~DirtyFlags.RenderAll; } public void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) { - if (_currentState.Dirty.ComputePipeline) + if (_currentState.Dirty.HasFlag(DirtyFlags.ComputePipeline)) { SetComputePipelineState(computeCommandEncoder); } - // Clear the dirty flags - _currentState.Dirty.ComputePipeline = false; + if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers)) + { + SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers); + } + + if (_currentState.Dirty.HasFlag(DirtyFlags.ComputeTextures)) + { + SetComputeTextures(computeCommandEncoder, _currentState.ComputeTextures, _currentState.ComputeSamplers); + } } - private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) + private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) { var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); @@ -340,7 +356,7 @@ namespace Ryujinx.Graphics.Metal } } - private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) + private void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) { if (_currentState.ComputeFunction == null) { @@ -398,15 +414,13 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexFunction = prg.VertexFunction; _currentState.FragmentFunction = prg.FragmentFunction; - // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline; } if (prg.ComputeFunction != IntPtr.Zero) { _currentState.ComputeFunction = prg.ComputeFunction; - // Mark dirty - _currentState.Dirty.ComputePipeline = true; + _currentState.Dirty |= DirtyFlags.ComputePipeline; } } @@ -473,7 +487,7 @@ namespace Ryujinx.Graphics.Metal _currentState.VertexAttribs = vertexAttribs.ToArray(); // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline; } public void UpdateBlendDescriptors(int index, BlendDescriptor blend) @@ -524,13 +538,12 @@ namespace Ryujinx.Graphics.Metal UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); // Mark dirty - _currentState.Dirty.DepthStencil = true; + _currentState.Dirty |= DirtyFlags.DepthStencil; // Cleanup descriptor.Dispose(); } - // Inlineable public void UpdateDepthState(DepthTestDescriptor depthTest) { _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; @@ -551,7 +564,7 @@ namespace Ryujinx.Graphics.Metal _currentState.DepthStencilState = _depthStencilCache.GetOrCreate(descriptor); // Mark dirty - _currentState.Dirty.DepthStencil = true; + _currentState.Dirty |= DirtyFlags.DepthStencil; // Cleanup descriptor.Dispose(); @@ -567,7 +580,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthClamp(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.DepthClamp; } // Inlineable @@ -582,7 +599,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetDepthBias(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.DepthBias; } // Inlineable @@ -610,7 +631,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetScissors(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.Scissors; } // Inlineable @@ -643,7 +668,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetViewports(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.Viewports; } public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) @@ -655,20 +684,17 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers); + return; } // Mark dirty - _currentState.Dirty.RenderPipeline = true; + _currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.VertexBuffers; } - // Inlineable public void UpdateUniformBuffers(ReadOnlySpan buffers) { - _currentState.UniformBuffers = new BufferRef[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (BufferAssignment assignment in buffers) { - var assignment = buffers[i]; var buffer = assignment.Range; int index = assignment.Binding; @@ -676,87 +702,40 @@ namespace Ryujinx.Graphics.Metal ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.UniformBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + _currentState.UniformBuffers[index] = new BufferRef(mtlBuffer, ref buffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } - // Inlineable public void UpdateStorageBuffers(ReadOnlySpan buffers) { - _currentState.StorageBuffers = new BufferRef[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) + foreach (BufferAssignment assignment in buffers) { - var assignment = buffers[i]; var buffer = assignment.Range; - // TODO: Dont do this - int index = assignment.Binding + 15; + int index = assignment.Binding; Auto mtlBuffer = buffer.Handle == BufferHandle.Null ? null : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index, ref buffer); + _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer, ref buffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } - // Inlineable public void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) { - _currentState.StorageBuffers = new BufferRef[buffers.Length]; - for (int i = 0; i < buffers.Length; i++) { var mtlBuffer = buffers[i]; int index = first + i; - _currentState.StorageBuffers[i] = new BufferRef(mtlBuffer, index); + _currentState.StorageBuffers[index] = new BufferRef(mtlBuffer); } - // Inline update - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeBuffers(computeCommandEncoder, _currentState.StorageBuffers); - } - } + _currentState.Dirty |= DirtyFlags.Buffers; } // Inlineable @@ -769,7 +748,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetCullMode(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.CullMode; } // Inlineable @@ -782,7 +765,11 @@ namespace Ryujinx.Graphics.Metal { var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetFrontFace(renderCommandEncoder); + return; } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.FrontFace; } private void UpdateStencilRefValue(int frontRef, int backRef) @@ -796,84 +783,60 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); SetStencilRefValue(renderCommandEncoder); } + + // Mark dirty + _currentState.Dirty |= DirtyFlags.StencilRef; } - // Inlineable - public readonly void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) + public void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture) { - if (binding > 30) + if (binding > Constants.MaxTexturesPerStage) { - Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= 30"); + 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; } - - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderTexture(renderCommandEncoder, ShaderStage.Vertex, binding, texture); - SetRenderTexture(renderCommandEncoder, ShaderStage.Fragment, binding, texture); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeTexture(computeCommandEncoder, binding, texture); - } - } } - // Inlineable - public readonly void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) + public void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler) { - if (binding > 15) + if (binding > Constants.MaxTexturesPerStage) { - Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= 15"); + 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; } - - if (_pipeline.CurrentEncoder != null) - { - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); - SetRenderSampler(renderCommandEncoder, ShaderStage.Vertex, binding, sampler); - SetRenderSampler(renderCommandEncoder, ShaderStage.Fragment, binding, sampler); - } - else if (_pipeline.CurrentEncoderType == EncoderType.Compute) - { - var computeCommandEncoder = new MTLComputeCommandEncoder(_pipeline.CurrentEncoder.Value); - SetComputeSampler(computeCommandEncoder, binding, sampler); - } - } } - // Inlineable - public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) { UpdateTexture(stage, binding, texture); UpdateSampler(stage, binding, sampler); @@ -930,8 +893,8 @@ namespace Ryujinx.Graphics.Metal { var attrib = vertexDescriptor.Attributes.Object((ulong)i); attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << bufferDescriptors.Length; - attrib.BufferIndex = (ulong)bufferDescriptors.Length; + indexMask |= 1u << (int)Constants.ZeroBufferIndex; + attrib.BufferIndex = Constants.ZeroBufferIndex; attrib.Offset = 0; } else @@ -979,9 +942,9 @@ namespace Ryujinx.Graphics.Metal } // Zero buffer - if ((indexMask & (1u << bufferDescriptors.Length)) != 0) + if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) { - var layout = vertexDescriptor.Layouts.Object((ulong)bufferDescriptors.Length); + var layout = vertexDescriptor.Layouts.Object(Constants.ZeroBufferIndex); layout.Stride = 1; layout.StepFunction = MTLVertexStepFunction.Constant; layout.StepRate = 0; @@ -992,39 +955,77 @@ namespace Ryujinx.Graphics.Metal private void SetVertexBuffers(MTLRenderCommandEncoder renderCommandEncoder, VertexBufferDescriptor[] bufferDescriptors) { - var buffers = new List(); - for (int i = 0; i < bufferDescriptors.Length; i++) { - Auto mtlBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null + Auto autoBuffer = bufferDescriptors[i].Buffer.Handle == BufferHandle.Null ? null : _bufferManager.GetBuffer(bufferDescriptors[i].Buffer.Handle, bufferDescriptors[i].Buffer.Write); var range = bufferDescriptors[i].Buffer; + var offset = range.Offset; - buffers.Add(new BufferRef(mtlBuffer, i, ref range)); + if (autoBuffer == null) + { + continue; + } + + var mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Size, range.Write).Value; + renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)i); } - var zeroBufferRange = new BufferRange(_zeroBuffer, 0, ZeroBufferSize); - - Auto zeroBuffer = _zeroBuffer == BufferHandle.Null + Auto autoZeroBuffer = _zeroBuffer == BufferHandle.Null ? null : _bufferManager.GetBuffer(_zeroBuffer, false); - // Zero buffer - buffers.Add(new BufferRef(zeroBuffer, bufferDescriptors.Length, ref zeroBufferRange)); + if (autoZeroBuffer == null) + { + return; + } - SetRenderBuffers(renderCommandEncoder, buffers.ToArray()); + var zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; + renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); } - private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool fragment = false) + private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers) { + var uniformArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, uniformBuffers, true); + var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value; + + 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; + + ulong[] resourceIds = new ulong[buffers.Length]; + for (int i = 0; i < buffers.Length; i++) { var range = buffers[i].Range; var autoBuffer = buffers[i].Buffer; var offset = 0; - var index = buffers[i].Index; if (autoBuffer == null) { @@ -1044,23 +1045,29 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - renderCommandEncoder.SetVertexBuffer(mtlBuffer, (ulong)offset, (ulong)index); - - if (fragment) - { - renderCommandEncoder.SetFragmentBuffer(mtlBuffer, (ulong)offset, (ulong)index); - } + renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage, MTLRenderStages.RenderStageFragment | MTLRenderStages.RenderStageVertex); + resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } + + var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } - private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers) + private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers, bool constant) { + var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write; + + ulong[] resourceIds = new ulong[buffers.Length]; + for (int i = 0; i < buffers.Length; i++) { var range = buffers[i].Range; var autoBuffer = buffers[i].Buffer; var offset = 0; - var index = buffers[i].Index; if (autoBuffer == null) { @@ -1080,8 +1087,16 @@ namespace Ryujinx.Graphics.Metal mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; } - computeCommandEncoder.SetBuffer(mtlBuffer, (ulong)offset, (ulong)index); + computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage); + resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset; } + + var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length; + + var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer); + argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) @@ -1099,64 +1114,104 @@ namespace Ryujinx.Graphics.Metal renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); } - private static void SetRenderTexture(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, TextureBase texture) + private readonly void SetRenderTextures(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers) { - if (texture == null) - { - return; - } + var argBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, stage, textures, samplers); + var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value; - var textureHandle = texture.GetHandle(); - if (textureHandle != IntPtr.Zero) + switch (stage) { - 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; + + ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + + for (int i = 0; i < textures.Length; i++) + { + if (textures[i] == null) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexTexture(textureHandle, binding); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentTexture(textureHandle, binding); - break; + continue; } - } - } - private static void SetRenderSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, ulong binding, MTLSamplerState sampler) - { - if (sampler != IntPtr.Zero) + 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++) { - switch (stage) + if (samplers[i].NativePtr == IntPtr.Zero) { - case ShaderStage.Vertex: - renderCommandEncoder.SetVertexSamplerState(sampler, binding); - break; - case ShaderStage.Fragment: - renderCommandEncoder.SetFragmentSamplerState(sampler, binding); - break; + 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, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } - private static void SetComputeTexture(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, TextureBase texture) + private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers) { - if (texture == null) + ulong[] resourceIds = new ulong[textures.Length + samplers.Length]; + + for (int i = 0; i < textures.Length; i++) { - return; + if (textures[i] == null) + { + continue; + } + + var mtlTexture = textures[i].GetHandle(); + + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); + resourceIds[i] = mtlTexture.GpuResourceID._impl; } - var textureHandle = texture.GetHandle(); - if (textureHandle != IntPtr.Zero) + for (int i = 0; i < samplers.Length; i++) { - computeCommandEncoder.SetTexture(textureHandle, binding); - } - } + if (samplers[i].NativePtr == IntPtr.Zero) + { + continue; + } - private static void SetComputeSampler(MTLComputeCommandEncoder computeCommandEncoder, ulong binding, MTLSamplerState sampler) - { - if (sampler != IntPtr.Zero) - { - computeCommandEncoder.SetSamplerState(sampler, binding); + 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, new ReadOnlySpan(resourceIds)); + + return argBuffer.Range; } } } diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index dcb5a4f62e..6f953a583f 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -77,7 +77,8 @@ namespace Ryujinx.Graphics.Metal ITexture dst, Extents2D srcRegion, Extents2D dstRegion, - bool linearFilter) + bool linearFilter, + bool clear = false) { // Save current state _pipeline.SaveAndResetState(); @@ -134,7 +135,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetRenderTargets([dst], null); _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); - _pipeline.SetClearLoadAction(true); + _pipeline.SetClearLoadAction(clear); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -305,13 +306,15 @@ namespace Ryujinx.Graphics.Metal int dstWidth, int dstHeight) { - const int ClearDepthBufferSize = 4; - - IntPtr ptr = new(&depthValue); - // Save current state _pipeline.SaveState(); + const int ClearDepthBufferSize = 4; + + using var buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize); + buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan(ref depthValue)); + _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); + Span viewports = stackalloc Viewport[1]; viewports[0] = new Viewport( @@ -330,7 +333,6 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetViewports(viewports); _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.GetOrCreateRenderEncoder(true).SetFragmentBytes(ptr, ClearDepthBufferSize, 0); _pipeline.Draw(4, 1, 0, 0); // Restore previous state diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd4a15c6d8..93064e60a5 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Metal return BeginBlitPass(); } - public MTLComputeCommandEncoder GetOrCreateComputeEncoder() + public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) { MTLComputeCommandEncoder computeCommandEncoder; if (CurrentEncoder == null || CurrentEncoderType != EncoderType.Compute) @@ -116,7 +116,10 @@ namespace Ryujinx.Graphics.Metal computeCommandEncoder = new MTLComputeCommandEncoder(CurrentEncoder.Value); } - _encoderStateManager.RebindComputeState(computeCommandEncoder); + if (forDispatch) + { + _encoderStateManager.RebindComputeState(computeCommandEncoder); + } return computeCommandEncoder; } @@ -190,7 +193,7 @@ namespace Ryujinx.Graphics.Metal var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear); + _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true); EndCurrentPass(); @@ -348,7 +351,7 @@ namespace Ryujinx.Graphics.Metal public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) { - var computeCommandEncoder = GetOrCreateComputeEncoder(); + var computeCommandEncoder = GetOrCreateComputeEncoder(true); computeCommandEncoder.DispatchThreadgroups( new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index f416b5da50..9f8ae74b48 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -27,7 +27,8 @@ namespace Ryujinx.Graphics.Metal MaxAnisotropy = Math.Max((uint)info.MaxAnisotropy, 1), SAddressMode = info.AddressU.Convert(), TAddressMode = info.AddressV.Convert(), - RAddressMode = info.AddressP.Convert() + RAddressMode = info.AddressP.Convert(), + SupportArgumentBuffers = true }); _mtlSamplerState = samplerState; diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index 3d86a27a8d..3c40af7375 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -7,14 +7,154 @@ struct CopyVertexOut { float2 uv; }; +struct TexCoords { + float data[4]; +}; + +struct ConstantBuffers { + constant TexCoords* texCoord; +}; + +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]], - const device float* texCoord [[buffer(0)]]) { + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { CopyVertexOut out; int low = vid & 1; int high = vid >> 1; - out.uv.x = texCoord[low]; - out.uv.y = texCoord[2 + high]; + out.uv.x = constant_buffers.texCoord->data[low]; + out.uv.y = constant_buffers.texCoord->data[2 + high]; out.position.x = (float(low) - 0.5f) * 2.0f; out.position.y = (float(high) - 0.5f) * 2.0f; out.position.z = 0.0f; @@ -24,7 +164,6 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], } fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], - texture2d texture [[texture(0)]], - sampler sampler [[sampler(0)]]) { - return texture.sample(sampler, in.uv); + constant Textures &textures [[buffer(22)]]) { + return textures.texture.sample(textures.sampler, in.uv); } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal index 64e8320926..38eedefb7f 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal @@ -2,19 +2,40 @@ using namespace metal; -kernel void kernelMain(constant int4& stride_arguments [[buffer(0)]], - device uint8_t* in_data [[buffer(1)]], - device uint8_t* out_data [[buffer(2)]], +struct StrideArguments { + int4 data; +}; + +struct InData { + uint8_t data[1]; +}; + +struct OutData { + uint8_t data[1]; +}; + +struct ConstantBuffers { + constant StrideArguments* stride_arguments; +}; + +struct StorageBuffers { + ulong padding; + device InData* in_data; + device OutData* out_data; +}; + +kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(20)]], + device StorageBuffers &storage_buffers [[buffer(21)]], uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 threads_per_threadgroup [[threads_per_threadgroup]], uint3 threadgroups_per_grid [[threads_per_grid]]) { // Determine what slice of the stride copies this invocation will perform. - int sourceStride = stride_arguments.x; - int targetStride = stride_arguments.y; - int bufferSize = stride_arguments.z; - int sourceOffset = stride_arguments.w; + int sourceStride = constant_buffers.stride_arguments->data.x; + int targetStride = constant_buffers.stride_arguments->data.y; + int bufferSize = constant_buffers.stride_arguments->data.z; + int sourceOffset = constant_buffers.stride_arguments->data.w; int strideRemainder = targetStride - sourceStride; int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); @@ -42,11 +63,11 @@ kernel void kernelMain(constant int4& stride_arguments [[buffer(0)]], // Perform the copies for this region for (int i = 0; i < copyCount; i++) { for (int j = 0; j < sourceStride; j++) { - out_data[dstOffset++] = in_data[srcOffset++]; + storage_buffers.out_data->data[dstOffset++] = storage_buffers.in_data->data[srcOffset++]; } for (int j = 0; j < strideRemainder; j++) { - out_data[dstOffset++] = uint8_t(0); + storage_buffers.out_data->data[dstOffset++] = uint8_t(0); } } } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal index 087c48606b..d3ef9603f4 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal @@ -6,6 +6,14 @@ struct VertexOut { float4 position [[position]]; }; +struct ClearColor { + float4 data; +}; + +struct ConstantBuffers { + constant ClearColor* clear_color; +}; + vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -25,6 +33,6 @@ struct FragmentOut { }; fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant float4& clear_color [[buffer(0)]]) { - return {clear_color}; + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { + return {constant_buffers.clear_color->data}; } diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal index 019bf78d41..0fb3bd858f 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal @@ -11,6 +11,14 @@ struct FragmentOut { uint stencil [[stencil]]; }; +struct ClearDepth { + float data; +}; + +struct ConstantBuffers { + constant ClearDepth* clear_depth; +}; + vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { int low = vid & 1; int high = vid >> 1; @@ -26,10 +34,10 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { } fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant float& clear_depth [[buffer(0)]]) { + constant ConstantBuffers &constant_buffers [[buffer(20)]]) { FragmentOut out; - out.depth = clear_depth; + out.depth = constant_buffers.clear_depth->data; // out.stencil = stencil_clear; return out; diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 656e67811f..57e446ce67 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Metal levels.length = (ulong)Info.Levels; NSRange slices; slices.location = (ulong)firstLayer; - slices.length = (ulong)info.GetDepthOrLayers(); + slices.length = textureType == MTLTextureType.Type3D ? 1 : (ulong)info.GetDepthOrLayers(); var swizzle = GetSwizzle(info, pixelFormat); @@ -287,14 +287,15 @@ namespace Ryujinx.Graphics.Metal } } - public unsafe void SetData(IMemoryOwner data) + public void SetData(IMemoryOwner data) { var blitCommandEncoder = _pipeline.GetOrCreateBlitEncoder(); var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; int width = Info.Width; int height = Info.Height; @@ -342,7 +343,7 @@ namespace Ryujinx.Graphics.Metal } // Cleanup - mtlBuffer.Dispose(); + buffer.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level) @@ -356,28 +357,26 @@ namespace Ryujinx.Graphics.Metal bytesPerImage = bytesPerRow * (ulong)Info.Height; } - unsafe - { - var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + var dataSpan = data.Memory.Span; - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - // Cleanup - mtlBuffer.Dispose(); - } + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = _mtlTexture.Width, height = _mtlTexture.Height, depth = _mtlTexture.Depth }, + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin() + ); + + // Cleanup + buffer.Dispose(); } public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) @@ -391,28 +390,26 @@ namespace Ryujinx.Graphics.Metal bytesPerImage = bytesPerRow * (ulong)Info.Height; } - unsafe - { - var dataSpan = data.Memory.Span; - var mtlBuffer = _device.NewBuffer((ulong)dataSpan.Length, MTLResourceOptions.ResourceStorageModeShared); - var bufferSpan = new Span(mtlBuffer.Contents.ToPointer(), dataSpan.Length); - dataSpan.CopyTo(bufferSpan); + var dataSpan = data.Memory.Span; - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - _mtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } - ); + var buffer = _renderer.BufferManager.Create(dataSpan.Length); + buffer.SetDataUnchecked(0, dataSpan); + var mtlBuffer = buffer.GetBuffer(false).Get(_pipeline.Cbs).Value; - // Cleanup - mtlBuffer.Dispose(); - } + blitCommandEncoder.CopyFromBuffer( + mtlBuffer, + 0, + bytesPerRow, + bytesPerImage, + new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, + _mtlTexture, + (ulong)layer, + (ulong)level, + new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } + ); + + // Cleanup + buffer.Dispose(); } public void SetStorage(BufferRange buffer) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs index 0ae6313eb7..79c13964ca 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string Tab = " "; // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int AdditionalArgCount = 1; + public const int AdditionalArgCount = 2; public StructuredFunction CurrentFunction { get; set; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index fc199da2cb..59cc5c56be 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -56,8 +56,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values); + DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true); + DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false); + DeclareTextures(context, context.Properties.Textures.Values); if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) { @@ -112,11 +113,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { string name = context.OperandManager.DeclareLocal(decl); - context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";"); + context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); } } - public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool atomic = false) + public static string GetVarTypeName(AggregateType type, bool atomic = false) { var s32 = atomic ? "atomic_int" : "int"; var u32 = atomic ? "atomic_uint" : "uint"; @@ -155,21 +156,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { arraySize = $"[{memory.ArrayLength}]"; } - var typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); + var typeName = GetVarTypeName(memory.Type & ~AggregateType.Array); context.AppendLine($"{prefix}{typeName} {memory.Name}{arraySize};"); } } - private static void DeclareBufferStructures(CodeGenContext context, IEnumerable buffers) + 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]; + foreach (BufferDefinition buffer in buffers) { - context.AppendLine($"struct {DefaultNames.StructPrefix}_{buffer.Name}"); + var needsPadding = buffer.Layout == BufferLayout.Std140; + + argBufferPointers[buffer.Binding] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};"; + + context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.EnterScope(); foreach (StructureField field in buffer.Type.Fields) { - string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); + var type = field.Type; + type |= (needsPadding && (field.Type & AggregateType.Array) != 0) ? AggregateType.Vector4 : AggregateType.Invalid; + + type &= ~AggregateType.Array; + + string typeName = GetVarTypeName(type); string arraySuffix = ""; if (field.Type.HasFlag(AggregateType.Array)) @@ -191,6 +207,62 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.LeaveScope(";"); context.AppendLine(); } + + context.AppendLine($"struct {name}"); + context.EnterScope(); + + for (int i = 0; i < argBufferPointers.Length; i++) + { + 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.LeaveScope(";"); + context.AppendLine(); + } + + private static void DeclareTextures(CodeGenContext context, IEnumerable textures) + { + context.AppendLine("struct Textures"); + context.EnterScope(); + + var argBufferPointers = new string[Defaults.MaxTexturesPerStage * 2]; + + foreach (TextureDefinition texture in textures) + { + var textureTypeName = texture.Type.ToMslTextureType(); + argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; + + if (!texture.Separate) + { + argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; + } + } + + for (int i = 0; i < argBufferPointers.Length; i++) + { + 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.LeaveScope(";"); + context.AppendLine(); } private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) @@ -201,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } else { - if (inputs.Any() || context.Definitions.Stage == ShaderStage.Fragment) + if (inputs.Any() || context.Definitions.Stage != ShaderStage.Compute) { string prefix = ""; @@ -220,7 +292,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (context.Definitions.Stage == ShaderStage.Fragment) { // TODO: check if it's needed - context.AppendLine("float4 position [[position]];"); + context.AppendLine("float4 position [[position, invariant]];"); context.AppendLine("bool front_facing [[front_facing]];"); } @@ -233,7 +305,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "uint", IoVariable.VertexIndex => "uint", IoVariable.PointCoord => "float2", - _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) }; string name = ioDefinition.IoVariable switch { @@ -242,11 +314,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.VertexId => "vertex_id", IoVariable.VertexIndex => "vertex_index", IoVariable.PointCoord => "point_coord", - _ => $"{DefaultNames.IAttributePrefix}{ioDefinition.Location}" + _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch { - // IoVariable.Position => "[[position]]", + // IoVariable.Position => "[[position, invariant]]", IoVariable.GlobalId => "[[thread_position_in_grid]]", IoVariable.VertexId => "[[vertex_id]]", // TODO: Avoid potential redeclaration @@ -297,9 +369,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { IoVariable.Position => "float4", IoVariable.PointSize => "float", - IoVariable.FragmentOutputColor => GetVarTypeName(context, context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), + IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), IoVariable.FragmentOutputDepth => "float", - _ => GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) + _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) }; string name = ioDefinition.IoVariable switch { @@ -307,11 +379,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl IoVariable.PointSize => "point_size", IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", IoVariable.FragmentOutputDepth => "depth", - _ => $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}" + _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" }; string suffix = ioDefinition.IoVariable switch { - IoVariable.Position => "[[position]]", + IoVariable.Position => "[[position, invariant]]", IoVariable.PointSize => "[[point_size]]", IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs similarity index 57% rename from src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs rename to src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs index 0b946c3aaa..c01242ffe1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/DefaultNames.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { - static class DefaultNames + static class Defaults { public const string LocalNamePrefix = "temp"; @@ -13,5 +13,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public const string ArgumentNamePrefix = "a"; public const string UndefinedName = "0"; + + public const int MaxUniformBuffersPerStage = 18; + public const int MaxStorageBuffersPerStage = 16; + public const int MaxTexturesPerStage = 64; + + public const uint ConstantBuffersIndex = 20; + public const uint StorageBuffersIndex = 21; + public const uint TexturesIndex = 22; } } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index 6c983445b4..8d4ef0e372 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -49,8 +49,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions ? AggregateType.S32 : AggregateType.U32; - builder.Append($"(device {Declarations.GetVarTypeName(context, dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); - + builder.Append($"(device {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs index c063ff4589..0bad36f734 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs @@ -21,11 +21,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions if (context.Definitions.Stage != ShaderStage.Compute) { args[0] = "in"; - args[1] = "support_buffer"; + args[1] = "constant_buffers"; + args[2] = "storage_buffers"; } else { - args[0] = "support_buffer"; + args[0] = "constant_buffers"; + args[1] = "storage_buffers"; } int argIndex = additionalArgCount; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs index bb1a699399..93eaee5dd9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions int srcIndex = 0; bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; + bool fieldHasPadding = false; if (operation.Inst == Instruction.AtomicCompareAndSwap) { @@ -46,7 +47,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } StructureField field = buffer.Type.Fields[fieldIndex.Value]; - varName = buffer.Name; + + fieldHasPadding = buffer.Layout == BufferLayout.Std140 + && ((field.Type & AggregateType.Vector4) == 0) + && ((field.Type & AggregateType.Array) != 0); + + varName = storageKind == StorageKind.ConstantBuffer + ? "constant_buffers" + : "storage_buffers"; + varName += "." + buffer.Name; varName += "->" + field.Name; varType = field.Type; break; @@ -130,6 +139,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } } varName += fieldName; + varName += fieldHasPadding ? ".x" : ""; if (isStore) { @@ -173,7 +183,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); } - return $"tex_{samplerName}.calculate_unclamped_lod(samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; + return $"textures.tex_{samplerName}.calculate_unclamped_lod(textures.samp_{samplerName}, {coordsExpr}){GetMaskMultiDest(texOp.Index)}"; } public static string Store(CodeGenContext context, AstOperation operation) @@ -199,7 +209,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions bool colorIsVector = isGather || !isShadow; string samplerName = GetSamplerName(context.Properties, texOp); - string texCall = $"tex_{samplerName}"; + string texCall = $"textures.tex_{samplerName}"; texCall += "."; int srcIndex = 0; @@ -229,7 +239,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions texCall += "_compare"; } - texCall += $"(samp_{samplerName}, "; + texCall += $"(textures.samp_{samplerName}, "; } int coordsCount = texOp.Type.GetDimensions(); @@ -385,7 +395,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions } string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"tex_{samplerName}"; + string textureName = $"textures.tex_{samplerName}"; string texCall = textureName + "."; texCall += "get_num_samples()"; @@ -397,7 +407,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions AstTextureOperation texOp = (AstTextureOperation)operation; string samplerName = GetSamplerName(context.Properties, texOp); - string textureName = $"tex_{samplerName}"; + string textureName = $"textures.tex_{samplerName}"; string texCall = textureName + "."; if (texOp.Index == 3) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index f9d0a96d92..bb0f7f0105 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -60,8 +60,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) { string name = isPerPatch - ? DefaultNames.PerPatchAttributePrefix - : (isOutput ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix); + ? Defaults.PerPatchAttributePrefix + : (isOutput ? Defaults.OAttributePrefix : Defaults.IAttributePrefix); if (location < 0) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index a3e09d3cb6..248b7159c1 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -73,18 +73,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (stage != ShaderStage.Compute) { args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; - args[1] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + args[1] = "constant ConstantBuffers &constant_buffers"; + args[2] = "device StorageBuffers &storage_buffers"; } else { - args[0] = $"constant {DefaultNames.StructPrefix}_support_buffer* support_buffer"; + args[0] = "constant ConstantBuffers &constant_buffers"; + args[1] = "device StorageBuffers &storage_buffers"; } } int argIndex = additionalArgCount; for (int i = 0; i < function.InArguments.Length; i++) { - args[argIndex++] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; + args[argIndex++] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; } for (int i = 0; i < function.OutArguments.Length; i++) @@ -92,12 +94,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl int j = i + function.InArguments.Length; // Likely need to be made into pointers - args[argIndex++] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; + args[argIndex++] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; } string funcKeyword = "inline"; string funcName = null; - string returnType = Declarations.GetVarTypeName(context, function.ReturnType); + string returnType = Declarations.GetVarTypeName(function.ReturnType); if (isMainFunc) { @@ -122,10 +124,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl if (stage == ShaderStage.Vertex) { - if (context.AttributeUsage.UsedInputAttributes != 0) - { - args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); - } + args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); } else if (stage == ShaderStage.Fragment) { @@ -148,27 +147,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl args = args.Append("uint thread_index_in_simdgroup [[thread_index_in_simdgroup]]").ToArray(); } - foreach (var constantBuffer in context.Properties.ConstantBuffers.Values) - { - args = args.Append($"constant {DefaultNames.StructPrefix}_{constantBuffer.Name}* {constantBuffer.Name} [[buffer({constantBuffer.Binding})]]").ToArray(); - } - - foreach (var storageBuffers in context.Properties.StorageBuffers.Values) - { - // Offset the binding by 15 to avoid clashing with the constant buffers - args = args.Append($"device {DefaultNames.StructPrefix}_{storageBuffers.Name}* {storageBuffers.Name} [[buffer({storageBuffers.Binding + 15})]]").ToArray(); - } - - foreach (var texture in context.Properties.Textures.Values) - { - var textureTypeName = texture.Type.ToMslTextureType(); - args = args.Append($"{textureTypeName} tex_{texture.Name} [[texture({texture.Binding})]]").ToArray(); - // If the texture is not separate, we need to declare a sampler - if (!texture.Separate) - { - args = args.Append($"sampler samp_{texture.Name} [[sampler({texture.Binding})]]").ToArray(); - } - } + args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); + args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); + args = args.Append($"constant Textures &textures [[buffer({Defaults.TexturesIndex})]]").ToArray(); } var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs index 6d211b7e8b..e131a645e9 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs @@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl public string DeclareLocal(AstOperand operand) { - string name = $"{DefaultNames.LocalNamePrefix}_{_locals.Count}"; + string name = $"{Defaults.LocalNamePrefix}_{_locals.Count}"; _locals.Add(operand, name); @@ -34,14 +34,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl OperandType.Argument => GetArgumentName(operand.Value), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.LocalVariable => _locals[operand], - OperandType.Undefined => DefaultNames.UndefinedName, + OperandType.Undefined => Defaults.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), }; } public static string GetArgumentName(int argIndex) { - return $"{DefaultNames.ArgumentNamePrefix}{argIndex}"; + return $"{Defaults.ArgumentNamePrefix}{argIndex}"; } public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node)