diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index dd1a5e071..b0be094d8 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -15,14 +15,11 @@ namespace Ryujinx.Graphics.Metal private readonly MTLCommandQueue _mtlCommandQueue; private MTLCommandBuffer _commandBuffer; - private MTLRenderCommandEncoder _renderCommandEncoder; - private MTLRenderPipelineState _renderPipelineState; - private MTLBlitCommandEncoder _blitCommandEncoder; + private MTLCommandEncoder _currentEncoder; - public MTLRenderCommandEncoder RenderCommandEncoder => _renderCommandEncoder; - public MTLBlitCommandEncoder BlitCommandEncoder => _blitCommandEncoder; + public MTLCommandEncoder CurrentEncoder; - private PrimitiveTopology _topology; + private RenderEncoderState _renderEncoderState; private MTLBuffer _indexBuffer; private MTLIndexType _indexType; @@ -35,35 +32,57 @@ namespace Ryujinx.Graphics.Metal var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var error = new NSError(IntPtr.Zero); - _renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error)); if (error != IntPtr.Zero) { // throw new Exception($"Failed to create render pipeline state! {StringHelp}"); throw new Exception($"Failed to create render pipeline state!"); } - CreateCommandBuffer(); + _commandBuffer = _mtlCommandQueue.CommandBuffer(); + } + + public void EndCurrentPass() + { + if (_currentEncoder != null) + { + _currentEncoder.EndEncoding(); + _currentEncoder = null; + } + } + + public MTLRenderCommandEncoder BeginRenderPass() + { + EndCurrentPass(); + + var descriptor = new MTLRenderPassDescriptor { }; + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); + _renderEncoderState.SetEncoderState(renderCommandEncoder); + + _currentEncoder = renderCommandEncoder; + return renderCommandEncoder; + } + + public MTLBlitCommandEncoder BeginBlitPass() + { + EndCurrentPass(); + + var descriptor = new MTLBlitPassDescriptor { }; + var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + + _currentEncoder = blitCommandEncoder; + return blitCommandEncoder; } public void Present() { - _renderCommandEncoder.EndEncoding(); - _blitCommandEncoder.EndEncoding(); + EndCurrentPass(); + // TODO: Give command buffer a valid MTLDrawable // _commandBuffer.PresentDrawable(); _commandBuffer.Commit(); - CreateCommandBuffer(); - } - - public void CreateCommandBuffer() - { _commandBuffer = _mtlCommandQueue.CommandBuffer(); - - _renderCommandEncoder = _commandBuffer.RenderCommandEncoder(new MTLRenderPassDescriptor()); - _renderCommandEncoder.SetRenderPipelineState(_renderPipelineState); - - _blitCommandEncoder = _commandBuffer.BlitCommandEncoder(new MTLBlitPassDescriptor()); } public void Barrier() @@ -104,18 +123,40 @@ namespace Ryujinx.Graphics.Metal public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - // TODO: Support topology re-indexing to provide support for TriangleFans - var _primitiveType = _topology.Convert(); + MTLRenderCommandEncoder renderCommandEncoder; - _renderCommandEncoder.DrawPrimitives(_primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + if (_currentEncoder is MTLRenderCommandEncoder encoder) + { + renderCommandEncoder = encoder; + } + else + { + renderCommandEncoder = BeginRenderPass(); + } + + // TODO: Support topology re-indexing to provide support for TriangleFans + var primitiveType = _renderEncoderState.Topology.Convert(); + + renderCommandEncoder.DrawPrimitives(primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); } public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - // TODO: Support topology re-indexing to provide support for TriangleFans - var _primitiveType = _topology.Convert(); + MTLRenderCommandEncoder renderCommandEncoder; - _renderCommandEncoder.DrawIndexedPrimitives(_primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); + if (_currentEncoder is MTLRenderCommandEncoder encoder) + { + renderCommandEncoder = encoder; + } + else + { + renderCommandEncoder = BeginRenderPass(); + } + + // TODO: Support topology re-indexing to provide support for TriangleFans + var primitiveType = _renderEncoderState.Topology.Convert(); + + renderCommandEncoder.DrawIndexedPrimitives(primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); } public void DrawIndexedIndirect(BufferRange indirectBuffer) @@ -180,12 +221,26 @@ namespace Ryujinx.Graphics.Metal public void SetFaceCulling(bool enable, Face face) { - _renderCommandEncoder.SetCullMode(enable ? face.Convert() : MTLCullMode.None); + var cullMode = enable ? face.Convert() : MTLCullMode.None; + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetCullMode(cullMode); + } + + _renderEncoderState.CullMode = cullMode; } public void SetFrontFace(FrontFace frontFace) { - _renderCommandEncoder.SetFrontFacingWinding(frontFace.Convert()); + var winding = frontFace.Convert(); + + if (_currentEncoder is MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetFrontFacingWinding(winding); + } + + _renderEncoderState.Winding = winding; } public void SetIndexBuffer(BufferRange buffer, IndexType type) @@ -244,7 +299,7 @@ namespace Ryujinx.Graphics.Metal public void SetPrimitiveTopology(PrimitiveTopology topology) { - _topology = topology; + _renderEncoderState.Topology = topology; } public void SetProgram(IProgram program) diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs new file mode 100644 index 000000000..5f078ded6 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -0,0 +1,27 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + struct RenderEncoderState + { + public MTLRenderPipelineState RenderPipelineState; + public PrimitiveTopology Topology = PrimitiveTopology.Triangles; + public MTLCullMode CullMode = MTLCullMode.None; + public MTLWinding Winding = MTLWinding.Clockwise; + + public RenderEncoderState(MTLRenderPipelineState renderPipelineState) + { + RenderPipelineState = renderPipelineState; + } + + public void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) + { + renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); + renderCommandEncoder.SetCullMode(CullMode); + renderCommandEncoder.SetFrontFacingWinding(Winding); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 7dd73ae5b..177582a2b 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -46,9 +46,20 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + if (destination is Texture destinationTexture) { - _pipeline.BlitCommandEncoder.CopyFromTexture( + blitCommandEncoder.CopyFromTexture( MTLTexture, (ulong)firstLayer, (ulong)firstLevel, @@ -62,9 +73,20 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { + MTLBlitCommandEncoder blitCommandEncoder; + + if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) + { + blitCommandEncoder = encoder; + } + else + { + blitCommandEncoder = _pipeline.BeginBlitPass(); + } + if (destination is Texture destinationTexture) { - _pipeline.BlitCommandEncoder.CopyFromTexture( + blitCommandEncoder.CopyFromTexture( MTLTexture, (ulong)srcLayer, (ulong)srcLevel,