Seizure my beloved is working

This commit is contained in:
Isaac Marovitz 2023-08-02 19:56:59 -04:00 committed by Isaac Marovitz
parent 6d722d83ba
commit aaa140e510
5 changed files with 109 additions and 39 deletions

View file

@ -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<CAMetalLayer> _getMetalLayer;
private Pipeline _pipeline;
private Window _window;
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
public bool PreferThreading => true;
public IPipeline Pipeline => _pipeline;
public IWindow Window => _window;
public MetalRenderer()
public MetalRenderer(Func<CAMetalLayer> 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)

View file

@ -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,

View file

@ -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)

View file

@ -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<float> tex [[texture(0)]]) {
float4 color = tex.sample(metal::address::clamp_to_edge, tex_coord);
return color;
fragment float4 fragmentMain(CopyVertexOut in [[stage_in]],
texture2d<float> 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);
}

View file

@ -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<byte> 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<byte> 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<byte> data, int layer, int level, Rectangle<int> region)
{
Logger.Warning?.Print(LogClass.Gpu, "Set data");
MTLBlitCommandEncoder blitCommandEncoder;
if (_pipeline.CurrentEncoder is MTLBlitCommandEncoder encoder)