diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index 05f9a7911e..434a737471 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Foundation; using SharpMetal.Metal; +using SharpMetal.QuartzCore; using System; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -14,25 +15,43 @@ namespace Ryujinx.Graphics.Metal public sealed class MetalRenderer : IRenderer { private readonly MTLDevice _device; - private readonly Window _window; - private readonly Pipeline _pipeline; private readonly MTLCommandQueue _queue; + private readonly Func _getMetalLayer; + + private Pipeline _pipeline; + private Window _window; public event EventHandler ScreenCaptured; public bool PreferThreading => true; public IPipeline Pipeline => _pipeline; public IWindow Window => _window; - public MetalRenderer() + public MetalRenderer(Func metalLayer) { _device = MTLDevice.CreateSystemDefaultDevice(); _queue = _device.NewCommandQueue(); - _pipeline = new Pipeline(_device, _queue); - _window = new Window(this); + _getMetalLayer = metalLayer; } public void Initialize(GraphicsDebugLevel logLevel) { + var layer = _getMetalLayer(); + layer.Device = _device; + + var captureDescriptor = new MTLCaptureDescriptor(); + captureDescriptor.CaptureObject = _queue; + captureDescriptor.Destination = MTLCaptureDestination.GPUTraceDocument; + captureDescriptor.OutputURL = NSURL.FileURLWithPath(StringHelper.NSString("/Users/isaacmarovitz/Desktop/Trace.gputrace")); + var captureError = new NSError(IntPtr.Zero); + MTLCaptureManager.SharedCaptureManager().StartCapture(captureDescriptor, ref captureError); + if (captureError != IntPtr.Zero) + { + Console.Write($"Failed to start capture! {StringHelper.String(captureError.LocalizedDescription)}"); + + } + + _window = new Window(this, layer); + _pipeline = new Pipeline(_device, _queue, layer); } public void BackgroundContextAction(Action action, bool alwaysBackground = false) @@ -96,7 +115,10 @@ namespace Ryujinx.Graphics.Metal public ITexture CreateTexture(TextureCreateInfo info) { - return new Texture(_device, _pipeline, info); + var texture = new Texture(_device, _pipeline, info); + Logger.Warning?.Print(LogClass.Gpu, "Texture created!"); + + return texture; } public bool PrepareHostMapping(IntPtr address, ulong size) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 77a0ee1a65..4c2f5e3000 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using SharpMetal.Foundation; using SharpMetal.Metal; +using SharpMetal.QuartzCore; using System; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -28,8 +29,10 @@ namespace Ryujinx.Graphics.Metal private MTLBuffer _indexBuffer; private MTLIndexType _indexType; private ulong _indexBufferOffset; + private MTLClearColor _clearColor = new() { alpha = 1.0f }; + private int frameCount = 0; - public Pipeline(MTLDevice device, MTLCommandQueue commandQueue) + public Pipeline(MTLDevice device, MTLCommandQueue commandQueue, CAMetalLayer metalLayer) { _device = device; _mtlCommandQueue = commandQueue; @@ -49,13 +52,17 @@ namespace Ryujinx.Graphics.Metal // TODO: Recreate descriptor and encoder state as needed var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); renderPipelineDescriptor.VertexFunction = vertexFunction; - renderPipelineDescriptor.FragmentFunction = fragmentFunction; + // renderPipelineDescriptor.FragmentFunction = fragmentFunction; + // TODO: This should not be hardcoded, but a bug in SharpMetal prevents me from doing this correctly + renderPipelineDescriptor.ColorAttachments.Object(0).PixelFormat = MTLPixelFormat.BGRA8Unorm; - _renderEncoderState = new(_device.NewRenderPipelineState(renderPipelineDescriptor, ref error), _device); + var renderPipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); } + + _renderEncoderState = new RenderEncoderState(renderPipelineState, _device); // _commandBuffer = _mtlCommandQueue.CommandBuffer(); @@ -68,6 +75,7 @@ namespace Ryujinx.Graphics.Metal _currentEncoder.EndEncoding(); _currentEncoder = null; } + Logger.Warning?.Print(LogClass.Gpu, "Current pass ended"); } public MTLRenderCommandEncoder BeginRenderPass() @@ -77,6 +85,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLRenderPassDescriptor(); var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); _renderEncoderState.SetEncoderState(renderCommandEncoder); + Logger.Warning?.Print(LogClass.Gpu, "Began render pass"); _currentEncoder = renderCommandEncoder; return renderCommandEncoder; @@ -88,6 +97,7 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLBlitPassDescriptor(); var blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began blit pass"); _currentEncoder = blitCommandEncoder; return blitCommandEncoder; @@ -99,18 +109,43 @@ namespace Ryujinx.Graphics.Metal var descriptor = new MTLComputePassDescriptor(); var computeCommandEncoder = _commandBuffer.ComputeCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began compute pass"); _currentEncoder = computeCommandEncoder; return computeCommandEncoder; } - public void Present() + public void Present(CAMetalDrawable drawable) { EndCurrentPass(); - // TODO: Give command buffer a valid MTLDrawable - // _commandBuffer.PresentDrawable(); - // _commandBuffer.Commit(); + var descriptor = new MTLRenderPassDescriptor(); + descriptor.ColorAttachments.Object(0).Texture = drawable.Texture; + descriptor.ColorAttachments.Object(0).LoadAction = MTLLoadAction.Clear; + descriptor.ColorAttachments.Object(0).ClearColor = _clearColor; + + var renderCommandEncoder = _commandBuffer.RenderCommandEncoder(descriptor); + Logger.Warning?.Print(LogClass.Gpu, "Began present"); + _renderEncoderState.SetEncoderState(renderCommandEncoder); + + // Barry goes here + // renderCommandEncoder.SetFragmentTexture(_renderTarget, 0); + + renderCommandEncoder.DrawPrimitives(MTLPrimitiveType.Triangle, 0, 6); + renderCommandEncoder.EndEncoding(); + + _commandBuffer.PresentDrawable(drawable); + _commandBuffer.Commit(); + + Logger.Warning?.Print(LogClass.Gpu, "Presented"); + + frameCount++; + + if (frameCount >= 5) + { + MTLCaptureManager.SharedCaptureManager().StopCapture(); + Logger.Warning?.Print(LogClass.Gpu, "Trace ended!"); + } _commandBuffer = _mtlCommandQueue.CommandBuffer(); } @@ -147,7 +182,7 @@ namespace Ryujinx.Graphics.Metal public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + _clearColor = new MTLClearColor { red = color.Red, green = color.Green, blue = color.Blue, alpha = color.Alpha}; } public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, diff --git a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs index a8c719fe91..db946d8b8e 100644 --- a/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/RenderEncoderState.cs @@ -17,23 +17,23 @@ namespace Ryujinx.Graphics.Metal private MTLStencilDescriptor _backFaceStencil = null; private MTLStencilDescriptor _frontFaceStencil = null; - public MTLRenderPipelineState RenderPipelineState; + public MTLRenderPipelineState CopyPipeline; public PrimitiveTopology Topology = PrimitiveTopology.Triangles; public MTLCullMode CullMode = MTLCullMode.None; public MTLWinding Winding = MTLWinding.Clockwise; - public RenderEncoderState(MTLRenderPipelineState renderPipelineState, MTLDevice device) + public RenderEncoderState(MTLRenderPipelineState copyPipeline, MTLDevice device) { _device = device; - RenderPipelineState = renderPipelineState; + CopyPipeline = copyPipeline; } public void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder) { - renderCommandEncoder.SetRenderPipelineState(RenderPipelineState); + renderCommandEncoder.SetRenderPipelineState(CopyPipeline); renderCommandEncoder.SetCullMode(CullMode); renderCommandEncoder.SetFrontFacingWinding(Winding); - renderCommandEncoder.SetDepthStencilState(_depthStencilState); + // renderCommandEncoder.SetDepthStencilState(_depthStencilState); } public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace) diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal index 3c2c8aa5f2..f21f219f02 100644 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal +++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorBlitShaderSource.metal @@ -2,29 +2,35 @@ using namespace metal; -struct TexCoordIn { - float4 tex_coord_in_data; +constant float2 quadVertices[] = { + float2(-1, -1), + float2(-1, 1), + float2( 1, 1), + float2(-1, -1), + float2( 1, 1), + float2( 1, -1) }; -vertex float4 vertexMain(uint vertexID [[vertex_id]], - constant TexCoordIn& tex_coord_in [[buffer(1)]]) { - int low = vertexID & 1; - int high = vertexID >> 1; - float2 tex_coord; - tex_coord.x = tex_coord_in.tex_coord_in_data[low]; - tex_coord.y = tex_coord_in.tex_coord_in_data[2 + high]; +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; - float4 position; - position.x = (float(low) - 0.5) * 2.0; - position.y = (float(high) - 0.5) * 2.0; - position.z = 0.0; - position.w = 1.0; +vertex CopyVertexOut vertexMain(unsigned short vid [[vertex_id]]) { + float2 position = quadVertices[vid]; - return position; + CopyVertexOut out; + + out.position = float4(position, 0, 1); + out.uv = position * 0.5f + 0.5f; + + return out; } -fragment float4 fragmentMain(float2 tex_coord [[stage_in]], - texture2d tex [[texture(0)]]) { - float4 color = tex.sample(metal::address::clamp_to_edge, tex_coord); - return color; +fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], + texture2d tex) { + constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); + + float3 color = tex.sample(sam, in.uv).xyz; + return float4(color, 1.0f); } diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 2a1e0a48c9..d8607a6183 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -9,7 +9,7 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class Texture : ITexture, IDisposable + public class Texture : ITexture, IDisposable { private readonly TextureCreateInfo _info; private readonly Pipeline _pipeline; @@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.Metal public Texture(MTLDevice device, Pipeline pipeline, TextureCreateInfo info) { + Logger.Warning?.Print(LogClass.Gpu, "Texture init"); _device = device; _pipeline = pipeline; _info = info; @@ -50,6 +51,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -109,6 +112,7 @@ namespace Ryujinx.Graphics.Metal public void CopyTo(BufferRange range, int layer, int level, int stride) { + Logger.Warning?.Print(LogClass.Gpu, "Copy to"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -160,6 +164,7 @@ namespace Ryujinx.Graphics.Metal // TODO: Handle array formats public unsafe void SetData(SpanOrArray data) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -216,6 +221,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder) @@ -257,6 +263,7 @@ namespace Ryujinx.Graphics.Metal public void SetData(SpanOrArray data, int layer, int level, Rectangle region) { + Logger.Warning?.Print(LogClass.Gpu, "Set data"); MTLBlitCommandEncoder blitCommandEncoder; if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder)