diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs new file mode 100644 index 000000000..e4be1fb7c --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -0,0 +1,50 @@ +using Ryujinx.Graphics.GAL; +using SharpMetal.Metal; +using System.Collections.Generic; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + public struct EncoderState + { + public MTLFunction? VertexFunction = null; + public MTLFunction? FragmentFunction = null; + + public Dictionary FragmentTextures = new(); + public Dictionary FragmentSamplers = new(); + + public Dictionary VertexTextures = new(); + public Dictionary VertexSamplers = new(); + + public List VertexBuffers = []; + public List UniformBuffers = []; + public List StorageBuffers = []; + + public MTLBuffer IndexBuffer = default; + public MTLIndexType IndexType = MTLIndexType.UInt16; + public ulong IndexBufferOffset = 0; + + public MTLDepthStencilState? DepthStencilState = null; + + public MTLCompareFunction DepthCompareFunction = MTLCompareFunction.Always; + public bool DepthWriteEnabled = false; + + public MTLStencilDescriptor BackFaceStencil = new(); + public MTLStencilDescriptor FrontFaceStencil = new(); + + public PrimitiveTopology Topology = PrimitiveTopology.Triangles; + public MTLCullMode CullMode = MTLCullMode.None; + public MTLWinding Winding = MTLWinding.Clockwise; + + public MTLViewport[] Viewports = []; + public MTLScissorRect[] Scissors = []; + + // Changes to attachments take recreation! + public MTLTexture DepthStencil = default; + public MTLTexture[] RenderTargets = []; + public MTLVertexDescriptor VertexDescriptor = new(); + + public EncoderState() { } + } +} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs new file mode 100644 index 000000000..a7e7de281 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -0,0 +1,558 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using SharpMetal.Foundation; +using SharpMetal.Metal; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + struct EncoderStateManager + { + private readonly MTLDevice _device; + private Pipeline _pipeline; + + private EncoderState _currentState = new(); + private EncoderState _backState = new(); + + // Public accessors + public MTLBuffer IndexBuffer => _currentState.IndexBuffer; + public MTLIndexType IndexType => _currentState.IndexType; + public ulong IndexBufferOffset => _currentState.IndexBufferOffset; + public PrimitiveTopology Topology => _currentState.Topology; + + public EncoderStateManager(MTLDevice device, Pipeline pipeline) + { + _device = device; + _pipeline = pipeline; + } + + public void SwapStates() + { + (_currentState, _backState) = (_backState, _currentState); + + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public MTLRenderCommandEncoder CreateRenderCommandEncoder() + { + // Initialise Pass & State + + var renderPassDescriptor = new MTLRenderPassDescriptor(); + var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); + + const int MaxColorAttachments = 8; + for (int i = 0; i < MaxColorAttachments; i++) + { + if (_currentState.RenderTargets[i] != IntPtr.Zero) + { + var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); + passAttachment.Texture = _currentState.RenderTargets[i]; + passAttachment.LoadAction = MTLLoadAction.Load; + + var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); + pipelineAttachment.SetBlendingEnabled(true); + pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat; + pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; + pipelineAttachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; + } + } + + var depthAttachment = renderPassDescriptor.DepthAttachment; + depthAttachment.Texture = _currentState.DepthStencil; + depthAttachment.LoadAction = MTLLoadAction.Load; + + // var stencilAttachment = renderPassDescriptor.StencilAttachment; + // stencilAttachment.Texture = + // stencilAttachment.LoadAction = MTLLoadAction.Load; + + renderPipelineDescriptor.DepthAttachmentPixelFormat = _currentState.DepthStencil.PixelFormat; + // renderPipelineDescriptor.StencilAttachmentPixelFormat = + + renderPipelineDescriptor.VertexDescriptor = _currentState.VertexDescriptor; + + if (_currentState.VertexFunction != null) + { + renderPipelineDescriptor.VertexFunction = _currentState.VertexFunction.Value; + } + else + { + return new (IntPtr.Zero); + } + + if (_currentState.FragmentFunction != null) + { + renderPipelineDescriptor.FragmentFunction = _currentState.FragmentFunction.Value; + } + + var error = new NSError(IntPtr.Zero); + var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); + if (error != IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); + } + + // Initialise Encoder + + var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); + + renderCommandEncoder.SetRenderPipelineState(pipelineState); + + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + SetScissors(renderCommandEncoder, _currentState.Scissors); + SetViewports(renderCommandEncoder, _currentState.Viewports); + SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + SetCullMode(renderCommandEncoder, _currentState.CullMode); + SetFrontFace(renderCommandEncoder, _currentState.Winding); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + + return renderCommandEncoder; + } + + public void UpdateIndexBuffer(BufferRange buffer, IndexType type) + { + if (buffer.Handle != BufferHandle.Null) + { + _currentState.IndexType = type.Convert(); + _currentState.IndexBufferOffset = (ulong)buffer.Offset; + var handle = buffer.Handle; + _currentState.IndexBuffer = new(Unsafe.As(ref handle)); + } + } + + public void UpdatePrimitiveTopology(PrimitiveTopology topology) + { + _currentState.Topology = topology; + } + + public void UpdateProgram(IProgram program) + { + Program prg = (Program)program; + + if (prg.VertexFunction == IntPtr.Zero) + { + Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); + return; + } + + _currentState.VertexFunction = prg.VertexFunction; + _currentState.FragmentFunction = prg.FragmentFunction; + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) + { + _currentState.RenderTargets = new MTLTexture[colors.Length]; + + for (int i = 0; i < colors.Length; i++) + { + if (colors[i] is not Texture tex) + { + continue; + } + + _currentState.RenderTargets[i] = tex.MTLTexture; + } + + if (depthStencil is Texture depthTexture) + { + _currentState.DepthStencil = depthTexture.MTLTexture; + } + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + public void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) + { + for (int i = 0; i < vertexAttribs.Length; i++) + { + if (!vertexAttribs[i].IsZero) + { + // TODO: Format should not be hardcoded + var attrib = _currentState.VertexDescriptor.Attributes.Object((ulong)i); + attrib.Format = MTLVertexFormat.Float4; + attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; + attrib.Offset = (ulong)vertexAttribs[i].Offset; + + var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); + layout.Stride = 1; + } + } + + // Requires recreating pipeline + if (_pipeline.CurrentEncoderType == EncoderType.Render) + { + _pipeline.EndCurrentPass(); + } + } + + // Inlineable + public void UpdateStencilState(StencilTestDescriptor stencilTest) + { + _currentState.BackFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.BackSFail.Convert(), + DepthFailureOperation = stencilTest.BackDpFail.Convert(), + DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), + StencilCompareFunction = stencilTest.BackFunc.Convert(), + ReadMask = (uint)stencilTest.BackFuncMask, + WriteMask = (uint)stencilTest.BackMask + }; + + _currentState.FrontFaceStencil = new MTLStencilDescriptor + { + StencilFailureOperation = stencilTest.FrontSFail.Convert(), + DepthFailureOperation = stencilTest.FrontDpFail.Convert(), + DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), + StencilCompareFunction = stencilTest.FrontFunc.Convert(), + ReadMask = (uint)stencilTest.FrontFuncMask, + WriteMask = (uint)stencilTest.FrontMask + }; + + _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _currentState.DepthCompareFunction, + DepthWriteEnabled = _currentState.DepthWriteEnabled, + BackFaceStencil = stencilTest.TestEnable ? _currentState.BackFaceStencil : new MTLStencilDescriptor(IntPtr.Zero), + FrontFaceStencil = stencilTest.TestEnable ? _currentState.FrontFaceStencil : new MTLStencilDescriptor(IntPtr.Zero) + }); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + } + } + + // Inlineable + public void UpdateDepthState(DepthTestDescriptor depthTest) + { + _currentState.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; + _currentState.DepthWriteEnabled = depthTest.WriteEnable; + + _currentState.DepthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor + { + DepthCompareFunction = _currentState.DepthCompareFunction, + DepthWriteEnabled = _currentState.DepthWriteEnabled, + BackFaceStencil = _currentState.BackFaceStencil, + FrontFaceStencil = _currentState.FrontFaceStencil + }); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetDepthStencilState(renderCommandEncoder, _currentState.DepthStencilState); + } + } + + // Inlineable + public void UpdateScissors(ReadOnlySpan> regions) + { + int maxScissors = Math.Min(regions.Length, _currentState.Viewports.Length); + + if (maxScissors == 0) + { + return; + } + + _currentState.Scissors = new MTLScissorRect[maxScissors]; + + for (int i = 0; i < maxScissors; i++) + { + var region = regions[i]; + + _currentState.Scissors[i] = new MTLScissorRect + { + height = Math.Clamp((ulong)region.Height, 0, (ulong)_currentState.Viewports[i].height), + width = Math.Clamp((ulong)region.Width, 0, (ulong)_currentState.Viewports[i].width), + x = (ulong)region.X, + y = (ulong)region.Y + }; + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetScissors(renderCommandEncoder, _currentState.Scissors); + } + } + + // Inlineable + public void UpdateViewports(ReadOnlySpan viewports) + { + static float Clamp(float value) + { + return Math.Clamp(value, 0f, 1f); + } + + _currentState.Viewports = new MTLViewport[viewports.Length]; + + for (int i = 0; i < viewports.Length; i++) + { + var viewport = viewports[i]; + _currentState.Viewports[i] = new MTLViewport + { + originX = viewport.Region.X, + originY = viewport.Region.Y, + width = viewport.Region.Width, + height = viewport.Region.Height, + znear = Clamp(viewport.DepthNear), + zfar = Clamp(viewport.DepthFar) + }; + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetViewports(renderCommandEncoder, _currentState.Viewports); + } + } + + // Inlineable + public void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) + { + _currentState.VertexBuffers = []; + + for (int i = 0; i < vertexBuffers.Length; i++) + { + if (vertexBuffers[i].Stride != 0) + { + var layout = _currentState.VertexDescriptor.Layouts.Object((ulong)i); + layout.Stride = (ulong)vertexBuffers[i].Stride; + + _currentState.VertexBuffers.Add(new BufferInfo + { + Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), + Offset = vertexBuffers[i].Buffer.Offset, + Index = i + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.VertexBuffers); + } + } + + // Inlineable + public void UpdateUniformBuffers(ReadOnlySpan buffers) + { + _currentState.UniformBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + _currentState.UniformBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.UniformBuffers, true); + } + } + + // Inlineable + public void UpdateStorageBuffers(ReadOnlySpan buffers) + { + _currentState.StorageBuffers = []; + + foreach (BufferAssignment buffer in buffers) + { + if (buffer.Range.Size != 0) + { + // TODO: DONT offset the binding by 15 + _currentState.StorageBuffers.Add(new BufferInfo + { + Handle = buffer.Range.Handle.ToIntPtr(), + Offset = buffer.Range.Offset, + Index = buffer.Binding + 15 + }); + } + } + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetBuffers(renderCommandEncoder, _currentState.StorageBuffers, true); + } + } + + // Inlineable + public void UpdateCullMode(bool enable, Face face) + { + _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetCullMode(renderCommandEncoder, _currentState.CullMode); + } + } + + // Inlineable + public void UpdateFrontFace(FrontFace frontFace) + { + _currentState.Winding = frontFace.Convert(); + + // Inline Update + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetFrontFace(renderCommandEncoder, _currentState.Winding); + } + } + + // Inlineable + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, MTLTexture texture, MTLSamplerState sampler) + { + switch (stage) + { + case ShaderStage.Fragment: + _currentState.FragmentTextures[binding] = texture; + _currentState.FragmentSamplers[binding] = sampler; + break; + case ShaderStage.Vertex: + _currentState.VertexTextures[binding] = texture; + _currentState.VertexSamplers[binding] = sampler; + break; + } + + if (_pipeline.CurrentEncoderType == EncoderType.Render && _pipeline.CurrentEncoder != null) + { + var renderCommandEncoder = new MTLRenderCommandEncoder(_pipeline.CurrentEncoder.Value); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers); + SetTextureAndSampler(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers); + } + } + + private static void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder, MTLDepthStencilState? depthStencilState) + { + if (depthStencilState != null) + { + renderCommandEncoder.SetDepthStencilState(depthStencilState.Value); + } + } + + private unsafe static void SetScissors(MTLRenderCommandEncoder renderCommandEncoder, MTLScissorRect[] scissors) + { + if (scissors.Length > 0) + { + fixed (MTLScissorRect* pMtlScissors = scissors) + { + renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)scissors.Length); + } + } + } + + private unsafe static void SetViewports(MTLRenderCommandEncoder renderCommandEncoder, MTLViewport[] viewports) + { + if (viewports.Length > 0) + { + fixed (MTLViewport* pMtlViewports = viewports) + { + renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); + } + } + } + + private static void SetBuffers(MTLRenderCommandEncoder renderCommandEncoder, List buffers, bool fragment = false) + { + foreach (var buffer in buffers) + { + renderCommandEncoder.SetVertexBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + + if (fragment) + { + renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(buffer.Handle), (ulong)buffer.Offset, (ulong)buffer.Index); + } + } + } + + private static void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder, MTLCullMode cullMode) + { + renderCommandEncoder.SetCullMode(cullMode); + } + + private static void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder, MTLWinding winding) + { + renderCommandEncoder.SetFrontFacingWinding(winding); + } + + private static void SetTextureAndSampler(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, Dictionary textures, Dictionary samplers) + { + foreach (var texture in textures) + { + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexTexture(texture.Value, texture.Key); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentTexture(texture.Value, texture.Key); + break; + } + } + + foreach (var sampler in samplers) + { + switch (stage) + { + case ShaderStage.Vertex: + renderCommandEncoder.SetVertexSamplerState(sampler.Value, sampler.Key); + break; + case ShaderStage.Fragment: + renderCommandEncoder.SetFragmentSamplerState(sampler.Value, sampler.Key); + break; + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs new file mode 100644 index 000000000..a700cd68a --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -0,0 +1,206 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using SharpMetal.Metal; +using System; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + enum ComponentType + { + Float, + SignedInteger, + UnsignedInteger, + } + + [SupportedOSPlatform("macos")] + class HelperShader : IDisposable + { + private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; + private MTLDevice _device; + private Pipeline _pipeline; + + private readonly IProgram _programColorBlit; + private readonly IProgram _programColorClearF; + private readonly IProgram _programColorClearSI; + private readonly IProgram _programColorClearUI; + private readonly IProgram _programDepthStencilClear; + + public HelperShader(MTLDevice device, Pipeline pipeline) + { + _device = device; + _pipeline = pipeline; + + var blitSource = ReadMsl("Blit.metal"); + _programColorBlit = new Program( + [ + new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl), + new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl) + ], device); + + // var colorClearFSource = ReadMsl("ColorClearF.metal"); + // _programColorClearF = new Program( + // [ + // new ShaderSource(colorClearFSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearFSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var colorClearSiSource = ReadMsl("ColorClearSI.metal"); + // _programColorClearSI = new Program( + // [ + // new ShaderSource(colorClearSiSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearSiSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var colorClearUiSource = ReadMsl("ColorClearUI.metal"); + // _programColorClearUI = new Program( + // [ + // new ShaderSource(colorClearUiSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(colorClearUiSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + // + // var depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); + // _programDepthStencilClear = new Program( + // [ + // new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), + // new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) + // ], device); + } + + private static string ReadMsl(string fileName) + { + return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); + } + + public void BlitColor( + ITexture source, + ITexture destination) + { + var sampler = _device.NewSamplerState(new MTLSamplerDescriptor + { + MinFilter = MTLSamplerMinMagFilter.Nearest, + MagFilter = MTLSamplerMinMagFilter.Nearest, + MipFilter = MTLSamplerMipFilter.NotMipmapped + }); + + _pipeline.SetProgram(_programColorBlit); + _pipeline.SetRenderTargets([destination], null); + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, source, new Sampler(sampler)); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.Triangles); + _pipeline.Draw(6, 1, 0, 0); + _pipeline.Finish(); + } + + public void ClearColor( + Texture dst, + uint componentMask, + int dstWidth, + int dstHeight, + ComponentType type, + Rectangle scissor) + { + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + IProgram program; + + if (type == ComponentType.SignedInteger) + { + program = _programColorClearSI; + } + else if (type == ComponentType.UnsignedInteger) + { + program = _programColorClearUI; + } + else + { + program = _programColorClearF; + } + + _pipeline.SetProgram(program); + // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); + _pipeline.SetRenderTargetColorMasks([componentMask]); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors([scissor]); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + } + + public void ClearDepthStencil( + Texture dst, + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask, + int dstWidth, + int dstHeight, + Rectangle scissor) + { + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + _pipeline.SetProgram(_programDepthStencilClear); + // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors([scissor]); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + } + + private static StencilTestDescriptor CreateStencilTestDescriptor( + bool enabled, + int refValue = 0, + int compareMask = 0xff, + int writeMask = 0xff) + { + return new StencilTestDescriptor( + enabled, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, + refValue, + compareMask, + writeMask, + CompareOp.Always, + StencilOp.Replace, + StencilOp.Replace, + StencilOp.Replace, + refValue, + compareMask, + writeMask); + } + + public void Dispose() + { + _programColorBlit.Dispose(); + _programColorClearF.Dispose(); + _programColorClearSI.Dispose(); + _programColorClearUI.Dispose(); + _programDepthStencilClear.Dispose(); + _pipeline.Dispose(); + } + } +} diff --git a/src/Ryujinx.Graphics.Metal/HelperShaders.cs b/src/Ryujinx.Graphics.Metal/HelperShaders.cs deleted file mode 100644 index 8ca7adb6f..000000000 --- a/src/Ryujinx.Graphics.Metal/HelperShaders.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - public class HelperShaders - { - private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/HelperShadersSource.metal"; - - public HelperShader BlitShader; - - public HelperShaders(MTLDevice device) - { - var error = new NSError(IntPtr.Zero); - - var shaderSource = EmbeddedResources.ReadAllText(ShadersSourcePath); - var library = device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Library: {StringHelper.String(error.LocalizedDescription)}"); - } - - BlitShader = new HelperShader(library, "vertexBlit", "fragmentBlit"); - } - } - - [SupportedOSPlatform("macos")] - public readonly struct HelperShader - { - public readonly MTLFunction VertexFunction; - public readonly MTLFunction FragmentFunction; - - public HelperShader(MTLLibrary library, string vertex, string fragment) - { - VertexFunction = library.NewFunction(StringHelper.NSString(vertex)); - FragmentFunction = library.NewFunction(StringHelper.NSString(fragment)); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index fbb3e31b6..1b29a8562 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -5,7 +5,6 @@ using SharpMetal.Foundation; using SharpMetal.Metal; using SharpMetal.QuartzCore; using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -24,37 +23,27 @@ namespace Ryujinx.Graphics.Metal { private readonly MTLDevice _device; private readonly MTLCommandQueue _commandQueue; - private readonly HelperShaders _helperShaders; + private readonly HelperShader _helperShader; private MTLCommandBuffer _commandBuffer; + public MTLCommandBuffer CommandBuffer => _commandBuffer; + private MTLCommandEncoder? _currentEncoder; + public MTLCommandEncoder? CurrentEncoder => _currentEncoder; + private EncoderType _currentEncoderType = EncoderType.None; - private MTLTexture[] _renderTargets = []; - private MTLTexture _depthTarget; + public EncoderType CurrentEncoderType => _currentEncoderType; - private RenderEncoderState _renderEncoderState; - private readonly MTLVertexDescriptor _vertexDescriptor = new(); - private List _vertexBuffers = []; - private List _uniformBuffers = []; - private List _storageBuffers = []; - - private MTLBuffer _indexBuffer; - private MTLIndexType _indexType; - private ulong _indexBufferOffset; - private MTLClearColor _clearColor; + private EncoderStateManager _encoderStateManager; public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) { _device = device; _commandQueue = commandQueue; - _helperShaders = new HelperShaders(_device); - - _renderEncoderState = new RenderEncoderState( - _helperShaders.BlitShader.VertexFunction, - _helperShaders.BlitShader.FragmentFunction, - _device); + _helperShader = new HelperShader(_device, this); _commandBuffer = _commandQueue.CommandBuffer(); + _encoderStateManager = new EncoderStateManager(_device, this); } public MTLRenderCommandEncoder GetOrCreateRenderEncoder() @@ -126,28 +115,11 @@ namespace Ryujinx.Graphics.Metal { EndCurrentPass(); - var descriptor = new MTLRenderPassDescriptor(); - for (int i = 0; i < _renderTargets.Length; i++) - { - if (_renderTargets[i] != null) - { - var attachment = descriptor.ColorAttachments.Object((ulong)i); - attachment.Texture = _renderTargets[i]; - attachment.LoadAction = MTLLoadAction.Load; - } - } - - var depthAttachment = descriptor.DepthAttachment; - depthAttachment.Texture = _depthTarget; - depthAttachment.LoadAction = MTLLoadAction.Load; - - var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); - - RebindBuffers(renderCommandEncoder); + var renderCommandEncoder = _encoderStateManager.CreateRenderCommandEncoder(); _currentEncoder = renderCommandEncoder; _currentEncoderType = EncoderType.Render; + return renderCommandEncoder; } @@ -184,34 +156,9 @@ namespace Ryujinx.Graphics.Metal EndCurrentPass(); - var descriptor = new MTLRenderPassDescriptor(); - var colorAttachment = descriptor.ColorAttachments.Object(0); + _encoderStateManager.SwapStates(); - colorAttachment.Texture = drawable.Texture; - colorAttachment.LoadAction = MTLLoadAction.Clear; - colorAttachment.ClearColor = _clearColor; - - descriptor.ColorAttachments.SetObject(colorAttachment, 0); - - var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); - _renderEncoderState = new RenderEncoderState( - _helperShaders.BlitShader.VertexFunction, - _helperShaders.BlitShader.FragmentFunction, - _device); - _renderEncoderState.SetEncoderState(renderCommandEncoder, descriptor, _vertexDescriptor); - - var sampler = _device.NewSamplerState(new MTLSamplerDescriptor - { - MinFilter = MTLSamplerMinMagFilter.Nearest, - MagFilter = MTLSamplerMinMagFilter.Nearest, - MipFilter = MTLSamplerMipFilter.NotMipmapped - }); - - renderCommandEncoder.SetFragmentTexture(tex.MTLTexture, 0); - renderCommandEncoder.SetFragmentSamplerState(sampler, 0); - - renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); - renderCommandEncoder.EndEncoding(); + // _helperShader.BlitColor(tex, drawable.Texture); _commandBuffer.PresentDrawable(drawable); _commandBuffer.Commit(); @@ -219,31 +166,16 @@ namespace Ryujinx.Graphics.Metal _commandBuffer = _commandQueue.CommandBuffer(); } + public void Finish() + { + _encoderStateManager.SwapStates(); + } + public void Barrier() { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void RebindBuffers(MTLRenderCommandEncoder renderCommandEncoder) - { - foreach (var vertexBuffer in _vertexBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(vertexBuffer.Handle), (ulong)vertexBuffer.Offset, (ulong)vertexBuffer.Index); - } - - foreach (var uniformBuffer in _uniformBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); - renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(uniformBuffer.Handle), (ulong)uniformBuffer.Offset, (ulong)uniformBuffer.Index); - } - - foreach (var storageBuffer in _storageBuffers) - { - renderCommandEncoder.SetVertexBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); - renderCommandEncoder.SetFragmentBuffer(new MTLBuffer(storageBuffer.Handle), (ulong)storageBuffer.Offset, (ulong)storageBuffer.Index); - } - } - public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { var blitCommandEncoder = GetOrCreateBlitEncoder(); @@ -262,11 +194,10 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha }; + Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, - int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } @@ -301,9 +232,14 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = GetOrCreateRenderEncoder(); // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _renderEncoderState.Topology.Convert(); + var primitiveType = _encoderStateManager.Topology.Convert(); - renderCommandEncoder.DrawPrimitives(primitiveType, (ulong)firstVertex, (ulong)vertexCount, (ulong)instanceCount, (ulong)firstInstance); + renderCommandEncoder.DrawPrimitives( + primitiveType, + (ulong)firstVertex, + (ulong)vertexCount, + (ulong)instanceCount, + (ulong)firstInstance); } public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) @@ -311,9 +247,17 @@ namespace Ryujinx.Graphics.Metal var renderCommandEncoder = GetOrCreateRenderEncoder(); // TODO: Support topology re-indexing to provide support for TriangleFans - var primitiveType = _renderEncoderState.Topology.Convert(); + var primitiveType = _encoderStateManager.Topology.Convert(); - renderCommandEncoder.DrawIndexedPrimitives(primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance); + renderCommandEncoder.DrawIndexedPrimitives( + primitiveType, + (ulong)indexCount, + _encoderStateManager.IndexType, + _encoderStateManager.IndexBuffer, + _encoderStateManager.IndexBufferOffset, + (ulong)instanceCount, + firstVertex, + (ulong)firstInstance); } public void DrawIndexedIndirect(BufferRange indirectBuffer) @@ -383,49 +327,22 @@ namespace Ryujinx.Graphics.Metal public void SetDepthTest(DepthTestDescriptor depthTest) { - var depthStencilState = _renderEncoderState.UpdateDepthState( - depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always, - depthTest.WriteEnable); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); - } + _encoderStateManager.UpdateDepthState(depthTest); } public void SetFaceCulling(bool enable, Face face) { - var cullMode = enable ? face.Convert() : MTLCullMode.None; - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetCullMode(cullMode); - } - - _renderEncoderState.CullMode = cullMode; + _encoderStateManager.UpdateCullMode(enable, face); } public void SetFrontFace(FrontFace frontFace) { - var winding = frontFace.Convert(); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetFrontFacingWinding(winding); - } - - _renderEncoderState.Winding = winding; + _encoderStateManager.UpdateFrontFace(frontFace); } public void SetIndexBuffer(BufferRange buffer, IndexType type) { - if (buffer.Handle != BufferHandle.Null) - { - _indexType = type.Convert(); - _indexBufferOffset = (ulong)buffer.Offset; - var handle = buffer.Handle; - _indexBuffer = new(Unsafe.As(ref handle)); - } + _encoderStateManager.UpdateIndexBuffer(buffer, type); } public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) @@ -482,23 +399,12 @@ namespace Ryujinx.Graphics.Metal public void SetPrimitiveTopology(PrimitiveTopology topology) { - _renderEncoderState.Topology = topology; + _encoderStateManager.UpdatePrimitiveTopology(topology); } public void SetProgram(IProgram program) { - Program prg = (Program)program; - - if (prg.VertexFunction == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, "Invalid Vertex Function!"); - return; - } - - _renderEncoderState = new RenderEncoderState( - prg.VertexFunction, - prg.FragmentFunction, - _device); + _encoderStateManager.UpdateProgram(program); } public void SetRasterizerDiscard(bool discard) @@ -513,118 +419,27 @@ namespace Ryujinx.Graphics.Metal public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - _renderTargets = new MTLTexture[colors.Length]; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] is not Texture tex) - { - continue; - } - - if (tex.MTLTexture != null) - { - _renderTargets[i] = tex.MTLTexture; - } - } - - if (depthStencil is Texture depthTexture) - { - _depthTarget = depthTexture.MTLTexture; - } - - // Recreate Render Command Encoder - BeginRenderPass(); + _encoderStateManager.UpdateRenderTargets(colors, depthStencil); } - public unsafe void SetScissors(ReadOnlySpan> regions) + public void SetScissors(ReadOnlySpan> regions) { - int maxScissors = Math.Min(regions.Length, _renderEncoderState.Viewports.Length); - - if (maxScissors == 0) - { - return; - } - - var mtlScissorRects = new MTLScissorRect[maxScissors]; - - for (int i = 0; i < maxScissors; i++) - { - var region = regions[i]; - - mtlScissorRects[i] = new MTLScissorRect - { - height = Math.Clamp((ulong)region.Height, 0, (ulong)_renderEncoderState.Viewports[i].height), - width = Math.Clamp((ulong)region.Width, 0, (ulong)_renderEncoderState.Viewports[i].width), - x = (ulong)region.X, - y = (ulong)region.Y - }; - } - - _renderEncoderState.UpdateScissors(mtlScissorRects); - if (_currentEncoderType == EncoderType.Render) - { - fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissorRects, (ulong)regions.Length); - } - } + _encoderStateManager.UpdateScissors(regions); } public void SetStencilTest(StencilTestDescriptor stencilTest) { - var backFace = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }; + _encoderStateManager.UpdateStencilState(stencilTest); + } - var frontFace = new MTLStencilDescriptor - { - StencilFailureOperation = stencilTest.FrontSFail.Convert(), - DepthFailureOperation = stencilTest.FrontDpFail.Convert(), - DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), - StencilCompareFunction = stencilTest.FrontFunc.Convert(), - ReadMask = (uint)stencilTest.FrontFuncMask, - WriteMask = (uint)stencilTest.FrontMask - }; - - var depthStencilState = _renderEncoderState.UpdateStencilState(backFace, frontFace); - - if (_currentEncoderType == EncoderType.Render) - { - new MTLRenderCommandEncoder(_currentEncoder.Value).SetDepthStencilState(depthStencilState); - } + public void SetUniformBuffers(ReadOnlySpan buffers) + { + _encoderStateManager.UpdateUniformBuffers(buffers); } public void SetStorageBuffers(ReadOnlySpan buffers) { - _storageBuffers = []; - - foreach (BufferAssignment buffer in buffers) - { - if (buffer.Range.Size != 0) - { - // Offset the binding by 15 - _storageBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding + 15 - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } + _encoderStateManager.UpdateStorageBuffers(buffers); } public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) @@ -633,27 +448,18 @@ namespace Ryujinx.Graphics.Metal { if (sampler is Sampler samp) { - MTLRenderCommandEncoder renderCommandEncoder; - MTLComputeCommandEncoder computeCommandEncoder; - var mtlTexture = tex.MTLTexture; var mtlSampler = samp.GetSampler(); var index = (ulong)binding; switch (stage) { - case ShaderStage.Fragment: - renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetFragmentTexture(mtlTexture, index); - renderCommandEncoder.SetFragmentSamplerState(mtlSampler, index); - break; case ShaderStage.Vertex: - renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetVertexTexture(mtlTexture, index); - renderCommandEncoder.SetVertexSamplerState(mtlSampler, index); + case ShaderStage.Fragment: + _encoderStateManager.UpdateTextureAndSampler(stage, index, mtlTexture, mtlSampler); break; case ShaderStage.Compute: - computeCommandEncoder = GetOrCreateComputeEncoder(); + var computeCommandEncoder = GetOrCreateComputeEncoder(); computeCommandEncoder.SetTexture(mtlTexture, index); computeCommandEncoder.SetSamplerState(mtlSampler, index); break; @@ -669,30 +475,6 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); } - public void SetUniformBuffers(ReadOnlySpan buffers) - { - _uniformBuffers = []; - - foreach (BufferAssignment buffer in buffers) - { - if (buffer.Range.Size != 0) - { - _uniformBuffers.Add(new BufferInfo - { - Handle = buffer.Range.Handle.ToIntPtr(), - Offset = buffer.Range.Offset, - Index = buffer.Binding - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } - } - public void SetUserClipDistance(int index, bool enableClip) { Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); @@ -700,81 +482,17 @@ namespace Ryujinx.Graphics.Metal public void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - for (int i = 0; i < vertexAttribs.Length; i++) - { - if (!vertexAttribs[i].IsZero) - { - // TODO: Format should not be hardcoded - var attrib = _vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = MTLVertexFormat.Float4; - attrib.BufferIndex = (ulong)vertexAttribs[i].BufferIndex; - attrib.Offset = (ulong)vertexAttribs[i].Offset; - - var layout = _vertexDescriptor.Layouts.Object((ulong)vertexAttribs[i].BufferIndex); - layout.Stride = 1; - } - } + _encoderStateManager.UpdateVertexAttribs(vertexAttribs); } public void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _vertexBuffers = []; - - for (int i = 0; i < vertexBuffers.Length; i++) - { - if (vertexBuffers[i].Stride != 0) - { - var layout = _vertexDescriptor.Layouts.Object((ulong)i); - layout.Stride = (ulong)vertexBuffers[i].Stride; - - _vertexBuffers.Add(new BufferInfo - { - Handle = vertexBuffers[i].Buffer.Handle.ToIntPtr(), - Offset = vertexBuffers[i].Buffer.Offset, - Index = i - }); - } - } - - if (_currentEncoderType == EncoderType.Render) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - RebindBuffers(renderCommandEncoder); - } + _encoderStateManager.UpdateVertexBuffers(vertexBuffers); } - public unsafe void SetViewports(ReadOnlySpan viewports) + public void SetViewports(ReadOnlySpan viewports) { - static float Clamp(float value) - { - return Math.Clamp(value, 0f, 1f); - } - - var mtlViewports = new MTLViewport[viewports.Length]; - - for (int i = 0; i < viewports.Length; i++) - { - var viewport = viewports[i]; - mtlViewports[i] = new MTLViewport - { - originX = viewport.Region.X, - originY = viewport.Region.Y, - width = viewport.Region.Width, - height = viewport.Region.Height, - znear = Clamp(viewport.DepthNear), - zfar = Clamp(viewport.DepthFar) - }; - } - - _renderEncoderState.UpdateViewport(mtlViewports); - if (_currentEncoderType == EncoderType.Render) - { - fixed (MTLViewport* pMtlViewports = mtlViewports) - { - var renderCommandEncoder = GetOrCreateRenderEncoder(); - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)viewports.Length); - } - } + _encoderStateManager.UpdateViewports(viewports); } public void TextureBarrier() diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 255a7316b..764bcf126 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -28,8 +28,6 @@ namespace Ryujinx.Graphics.Metal { Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{StringHelper.String(libraryError.LocalizedDescription)}"); _status = ProgramLinkStatus.Failure; - //Console.WriteLine($"SHADER {index}: {shader.Code}"); - //throw new NotImplementedException(); return; } diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs deleted file mode 100644 index c22f98522..000000000 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ /dev/null @@ -1,151 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct RenderEncoderState - { - private readonly MTLDevice _device; - private readonly MTLFunction? _vertexFunction = null; - private readonly MTLFunction? _fragmentFunction = null; - private MTLDepthStencilState? _depthStencilState = null; - - private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always; - private bool _depthWriteEnabled = false; - - private MTLStencilDescriptor _backFaceStencil = new(); - private MTLStencilDescriptor _frontFaceStencil = new(); - - public PrimitiveTopology Topology = PrimitiveTopology.Triangles; - public MTLCullMode CullMode = MTLCullMode.None; - public MTLWinding Winding = MTLWinding.Clockwise; - - private MTLViewport[] _viewports = []; - private MTLScissorRect[] _scissors = []; - public readonly MTLViewport[] Viewports => _viewports; - - public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device) - { - _vertexFunction = vertexFunction; - _fragmentFunction = fragmentFunction; - _device = device; - } - - public unsafe void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLRenderPassDescriptor descriptor, MTLVertexDescriptor vertexDescriptor) - { - var renderPipelineDescriptor = new MTLRenderPipelineDescriptor - { - VertexDescriptor = vertexDescriptor - }; - - if (_vertexFunction != null) - { - renderPipelineDescriptor.VertexFunction = _vertexFunction.Value; - } - - if (_fragmentFunction != null) - { - renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value; - } - - const int MaxColorAttachments = 8; - for (int i = 0; i < MaxColorAttachments; i++) - { - var renderAttachment = descriptor.ColorAttachments.Object((ulong)i); - if (renderAttachment.Texture != IntPtr.Zero) - { - var attachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - attachment.SetBlendingEnabled(true); - attachment.PixelFormat = renderAttachment.Texture.PixelFormat; - attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha; - attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha; - } - } - - renderPipelineDescriptor.DepthAttachmentPixelFormat = descriptor.DepthAttachment.Texture.PixelFormat; - - var error = new NSError(IntPtr.Zero); - var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - renderCommandEncoder.SetCullMode(CullMode); - renderCommandEncoder.SetFrontFacingWinding(Winding); - - if (_depthStencilState != null) - { - renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value); - } - - if (_viewports.Length > 0) - { - fixed (MTLViewport* pMtlViewports = _viewports) - { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_viewports.Length); - } - } - - if (_scissors.Length > 0) - { - fixed (MTLScissorRect* pMtlScissors = _scissors) - { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_scissors.Length); - } - } - } - - public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) - { - _backFaceStencil = backFace; - _frontFaceStencil = frontFace; - - _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor - { - DepthCompareFunction = _depthCompareFunction, - DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil - }); - - return _depthStencilState.Value; - } - - public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled) - { - _depthCompareFunction = depthCompareFunction; - _depthWriteEnabled = depthWriteEnabled; - - var state = _device.NewDepthStencilState(new MTLDepthStencilDescriptor - { - DepthCompareFunction = _depthCompareFunction, - DepthWriteEnabled = _depthWriteEnabled, - BackFaceStencil = _backFaceStencil, - FrontFaceStencil = _frontFaceStencil - }); - - _depthStencilState = state; - - return state; - } - - public void UpdateScissors(MTLScissorRect[] scissors) - { - _scissors = scissors; - } - - public void UpdateViewport(MTLViewport[] viewports) - { - _viewports = viewports; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj index 46f2d070d..d8da12834 100644 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj @@ -15,7 +15,11 @@ - + + + + + diff --git a/src/Ryujinx.Graphics.Metal/Sampler.cs b/src/Ryujinx.Graphics.Metal/Sampler.cs index 0c556eac4..5cb898a9f 100644 --- a/src/Ryujinx.Graphics.Metal/Sampler.cs +++ b/src/Ryujinx.Graphics.Metal/Sampler.cs @@ -33,6 +33,11 @@ namespace Ryujinx.Graphics.Metal _mtlSamplerState = samplerState; } + public Sampler(MTLSamplerState samplerState) + { + _mtlSamplerState = samplerState; + } + public MTLSamplerState GetSampler() { return _mtlSamplerState; diff --git a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal similarity index 85% rename from src/Ryujinx.Graphics.Metal/HelperShadersSource.metal rename to src/Ryujinx.Graphics.Metal/Shaders/Blit.metal index dd39c6a89..b2bec3e8e 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShadersSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal @@ -20,7 +20,7 @@ struct CopyVertexOut { float2 uv; }; -vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { +vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { float2 position = quadVertices[vid]; CopyVertexOut out; @@ -32,7 +32,7 @@ vertex CopyVertexOut vertexBlit(unsigned short vid [[vertex_id]]) { return out; } -fragment float4 fragmentBlit(CopyVertexOut in [[stage_in]], +fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], texture2d texture [[texture(0)]], sampler sampler [[sampler(0)]]) { return texture.sample(sampler, in.uv); diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal new file mode 100644 index 000000000..e69de29bb diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index abfadb53a..5ce1dd2ed 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal;