Texture, Pipeline, Sample, Renderer Improvements

This commit is contained in:
Isaac Marovitz 2023-07-27 21:51:20 -04:00 committed by Isaac Marovitz
parent ebaf1d8258
commit 1e36815713
5 changed files with 181 additions and 54 deletions

View file

@ -182,6 +182,20 @@ namespace Ryujinx.Graphics.Metal
}; };
} }
public static MTLTextureSwizzle Convert(this SwizzleComponent swizzleComponent)
{
return swizzleComponent switch
{
SwizzleComponent.Zero => MTLTextureSwizzle.Zero,
SwizzleComponent.One => MTLTextureSwizzle.One,
SwizzleComponent.Red => MTLTextureSwizzle.Red,
SwizzleComponent.Green => MTLTextureSwizzle.Green,
SwizzleComponent.Blue => MTLTextureSwizzle.Blue,
SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha,
_ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero),
};
}
private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default) private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default)
{ {
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}."); Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}.");

View file

@ -1,8 +1,8 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using SharpMetal.Metal;
using System; using System;
using SharpMetal;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@ -28,10 +28,10 @@ namespace Ryujinx.Graphics.Metal
public void Initialize(GraphicsDebugLevel logLevel) public void Initialize(GraphicsDebugLevel logLevel)
{ {
_device = MTLDevice.MTLCreateSystemDefaultDevice(); _device = MTLDevice.CreateSystemDefaultDevice();
Queue = _device.NewCommandQueueWithMaxCommandBufferCount(Constants.MaxCommandBuffersPerQueue); Queue = _device.NewCommandQueue();
var commandBuffer = Queue.CommandBufferWithDescriptor(new MTLCommandBufferDescriptor { RetainedReferences = true }); var commandBuffer = Queue.CommandBuffer();
_pipeline = new Pipeline(_device, commandBuffer); _pipeline = new Pipeline(_device, commandBuffer);
} }
@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Metal
{ {
BufferCount++; BufferCount++;
var buffer = _device.NewBufferWithBytesLengthOptions(pointer, (ulong)size, MTLResourceOptions.StorageModeShared); var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared);
var bufferPtr = buffer.NativePtr; var bufferPtr = buffer.NativePtr;
return Unsafe.As<IntPtr, BufferHandle>(ref bufferPtr); return Unsafe.As<IntPtr, BufferHandle>(ref bufferPtr);
} }
@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Metal
{ {
BufferCount++; BufferCount++;
var buffer = _device.NewBufferWithLengthOptions((ulong)size, MTLResourceOptions.StorageModeShared); var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared);
var bufferPtr = buffer.NativePtr; var bufferPtr = buffer.NativePtr;
return Unsafe.As<IntPtr, BufferHandle>(ref bufferPtr); return Unsafe.As<IntPtr, BufferHandle>(ref bufferPtr);
} }
@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Metal
{ {
(MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert();
var sampler = _device.CreateSamplerState(new MTLSamplerDescriptor var sampler = _device.NewSamplerState(new MTLSamplerDescriptor
{ {
BorderColor = MTLSamplerBorderColor.TransparentBlack, BorderColor = MTLSamplerBorderColor.TransparentBlack,
MinFilter = minFilter, MinFilter = minFilter,
@ -90,10 +90,10 @@ namespace Ryujinx.Graphics.Metal
RAddressMode = info.AddressP.Convert() RAddressMode = info.AddressP.Convert()
}); });
throw new NotImplementedException(); return new Sampler(sampler);
} }
public ITexture CreateTexture(TextureCreateInfo info, float scale) public ITexture CreateTexture(TextureCreateInfo info)
{ {
MTLTextureDescriptor descriptor = new() MTLTextureDescriptor descriptor = new()
{ {
@ -105,10 +105,10 @@ namespace Ryujinx.Graphics.Metal
SampleCount = (ulong)info.Samples, SampleCount = (ulong)info.Samples,
}; };
return CreateTextureView(info, scale); return CreateTextureView(info);
} }
internal TextureView CreateTextureView(TextureCreateInfo info, float scale) internal Texture CreateTextureView(TextureCreateInfo info)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -160,17 +160,21 @@ namespace Ryujinx.Graphics.Metal
supportsFragmentShaderOrderingIntel: false, supportsFragmentShaderOrderingIntel: false,
supportsGeometryShader: false, supportsGeometryShader: false,
supportsGeometryShaderPassthrough: false, supportsGeometryShaderPassthrough: false,
supportsTransformFeedback: false,
supportsImageLoadFormatted: false, supportsImageLoadFormatted: false,
supportsLayerVertexTessellation: false, supportsLayerVertexTessellation: false,
supportsMismatchingViewFormat: true, supportsMismatchingViewFormat: true,
supportsCubemapView: true, supportsCubemapView: true,
supportsNonConstantTextureOffset: false, supportsNonConstantTextureOffset: false,
supportsShaderBallot: false, supportsShaderBallot: false,
supportsShaderBarrierDivergence: false,
supportsShaderFloat64: false,
supportsTextureShadowLod: false, supportsTextureShadowLod: false,
supportsViewportIndexVertexTessellation: false, supportsViewportIndexVertexTessellation: false,
supportsViewportMask: false, supportsViewportMask: false,
supportsViewportSwizzle: false, supportsViewportSwizzle: false,
supportsIndirectParameters: true, supportsIndirectParameters: true,
supportsDepthClipControl: false,
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
maximumTexturesPerStage: Constants.MaxTexturesPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage,
@ -212,7 +216,7 @@ namespace Ryujinx.Graphics.Metal
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -243,4 +247,4 @@ namespace Ryujinx.Graphics.Metal
_pipeline.Dispose(); _pipeline.Dispose();
} }
} }
} }

View file

@ -1,7 +1,9 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using SharpMetal.Foundation;
using SharpMetal.Metal;
using System; using System;
using SharpMetal; using System.Runtime.CompilerServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal namespace Ryujinx.Graphics.Metal
@ -9,16 +11,30 @@ namespace Ryujinx.Graphics.Metal
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
public class Pipeline : IPipeline, IDisposable public class Pipeline : IPipeline, IDisposable
{ {
private MTLDevice _device;
private MTLCommandBuffer _commandBuffer; private MTLCommandBuffer _commandBuffer;
private MTLRenderCommandEncoder _renderCommandEncoder; private MTLRenderCommandEncoder _renderCommandEncoder;
private PrimitiveTopology _topology;
private MTLBuffer _indexBuffer;
private MTLIndexType _indexType;
private ulong _indexBufferOffset;
public Pipeline(MTLDevice device, MTLCommandBuffer commandBuffer) public Pipeline(MTLDevice device, MTLCommandBuffer commandBuffer)
{ {
var renderPipelineDescriptor = new MTLRenderPipelineDescriptor(); var renderPipelineDescriptor = new MTLRenderPipelineDescriptor();
var renderPipelineState = device.CreateRenderPipelineState(renderPipelineDescriptor, out NSError _); var error = new NSError(IntPtr.Zero);
_device = device;
var renderPipelineState = _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!");
}
_commandBuffer = commandBuffer; _commandBuffer = commandBuffer;
_renderCommandEncoder = _commandBuffer.CreateRenderCommandEncoder(new MTLRenderPassDescriptor()); _renderCommandEncoder = _commandBuffer.RenderCommandEncoder(new MTLRenderPassDescriptor());
_renderCommandEncoder.SetRenderPipelineState(renderPipelineState); _renderCommandEncoder.SetRenderPipelineState(renderPipelineState);
} }
@ -45,7 +61,7 @@ namespace Ryujinx.Graphics.Metal
public void CommandBufferBarrier() public void CommandBufferBarrier()
{ {
throw new NotImplementedException(); // TODO: Only required for MTLHeap or untracked resources
} }
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@ -60,12 +76,18 @@ namespace Ryujinx.Graphics.Metal
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{ {
throw new NotImplementedException(); // TODO: Support topology re-indexing to provide support for TriangleFans
var _primitiveType = _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) public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{ {
throw new NotImplementedException(); // TODO: Support topology re-indexing to provide support for TriangleFans
var _primitiveType = _topology.Convert();
_renderCommandEncoder.DrawIndexedPrimitives(_primitiveType, (ulong)indexCount, _indexType, _indexBuffer, _indexBufferOffset, (ulong)instanceCount, firstVertex, (ulong)firstInstance);
} }
public void DrawIndexedIndirect(BufferRange indirectBuffer) public void DrawIndexedIndirect(BufferRange indirectBuffer)
@ -95,7 +117,7 @@ namespace Ryujinx.Graphics.Metal
public void SetAlphaTest(bool enable, float reference, CompareOp op) public void SetAlphaTest(bool enable, float reference, CompareOp op)
{ {
throw new NotImplementedException(); // Metal does not support alpha test.
} }
public void SetBlendState(AdvancedBlendDescriptor blend) public void SetBlendState(AdvancedBlendDescriptor blend)
@ -130,17 +152,23 @@ namespace Ryujinx.Graphics.Metal
public void SetFaceCulling(bool enable, Face face) public void SetFaceCulling(bool enable, Face face)
{ {
throw new NotImplementedException(); _renderCommandEncoder.SetCullMode(enable ? face.Convert() : MTLCullMode.None);
} }
public void SetFrontFace(FrontFace frontFace) public void SetFrontFace(FrontFace frontFace)
{ {
throw new NotImplementedException(); _renderCommandEncoder.SetFrontFacingWinding(frontFace.Convert());
} }
public void SetIndexBuffer(BufferRange buffer, IndexType type) public void SetIndexBuffer(BufferRange buffer, IndexType type)
{ {
throw new NotImplementedException(); if (buffer.Handle != BufferHandle.Null)
{
_indexType = type.Convert();
_indexBufferOffset = (ulong)buffer.Offset;
var handle = buffer.Handle;
_indexBuffer = new(Unsafe.As<BufferHandle, IntPtr>(ref handle));
}
} }
public void SetImage(int binding, ITexture texture, Format imageFormat) public void SetImage(int binding, ITexture texture, Format imageFormat)
@ -150,12 +178,12 @@ namespace Ryujinx.Graphics.Metal
public void SetLineParameters(float width, bool smooth) public void SetLineParameters(float width, bool smooth)
{ {
throw new NotImplementedException(); // Not supported in Metal
} }
public void SetLogicOpState(bool enable, LogicalOp op) public void SetLogicOpState(bool enable, LogicalOp op)
{ {
throw new NotImplementedException(); // Not supported in Metal
} }
public void SetMultisampleState(MultisampleDescriptor multisample) public void SetMultisampleState(MultisampleDescriptor multisample)
@ -175,17 +203,20 @@ namespace Ryujinx.Graphics.Metal
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
{ {
throw new NotImplementedException(); // Not supported in Metal
} }
public void SetPrimitiveRestart(bool enable, int index) public void SetPrimitiveRestart(bool enable, int index)
{ {
throw new NotImplementedException(); // TODO: Supported for LineStrip and TriangleStrip
// https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263
// https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives
// https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal
} }
public void SetPrimitiveTopology(PrimitiveTopology topology) public void SetPrimitiveTopology(PrimitiveTopology topology)
{ {
throw new NotImplementedException(); _topology = topology;
} }
public void SetProgram(IProgram program) public void SetProgram(IProgram program)
@ -198,11 +229,6 @@ namespace Ryujinx.Graphics.Metal
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SetRenderTargetScale(float scale)
{
throw new NotImplementedException();
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -213,14 +239,33 @@ namespace Ryujinx.Graphics.Metal
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions) public unsafe void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
{ {
throw new NotImplementedException(); // TODO: Test max allowed scissor rects on device
var mtlScissorRects = new MTLScissorRect[regions.Length];
for (int i = 0; i < regions.Length; i++)
{
var region = regions[i];
mtlScissorRects[i] = new MTLScissorRect
{
height = (ulong)region.Height,
width = (ulong)region.Width,
x = (ulong)region.X,
y = (ulong)region.Y
};
}
fixed (MTLScissorRect* pMtlScissorRects = mtlScissorRects)
{
// TODO: Fix this function which currently wont accept pointer as intended
// _renderCommandEncoder.SetScissorRects(pMtlScissorRects, regions.Length);
}
} }
public void SetStencilTest(StencilTestDescriptor stencilTest) public void SetStencilTest(StencilTestDescriptor stencilTest)
{ {
throw new NotImplementedException(); // TODO
} }
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
@ -253,9 +298,30 @@ namespace Ryujinx.Graphics.Metal
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool disableTransform) public unsafe void SetViewports(ReadOnlySpan<Viewport> viewports)
{ {
throw new NotImplementedException(); // TODO: Test max allowed viewports on device
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 = viewport.DepthNear,
zfar = viewport.DepthFar
};
}
fixed (MTLViewport* pMtlViewports = mtlViewports)
{
// TODO: Fix this function which currently wont accept pointer as intended
// _renderCommandEncoder.SetViewports(pMtlViewports, viewports.Length);
}
} }
public void TextureBarrier() public void TextureBarrier()
@ -270,40 +336,34 @@ namespace Ryujinx.Graphics.Metal
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
{ {
throw new NotImplementedException(); // TODO: Implementable via indirect draw commands
return false;
} }
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
{ {
throw new NotImplementedException(); // TODO: Implementable via indirect draw commands
return false;
} }
public void EndHostConditionalRendering() public void EndHostConditionalRendering()
{ {
throw new NotImplementedException(); // TODO: Implementable via indirect draw commands
}
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
{
throw new NotImplementedException();
} }
public void BeginTransformFeedback(PrimitiveTopology topology) public void BeginTransformFeedback(PrimitiveTopology topology)
{ {
// Metal does not support Transform Feedback // Metal does not support Transform Feedback
throw new NotSupportedException();
} }
public void EndTransformFeedback() public void EndTransformFeedback()
{ {
// Metal does not support Transform Feedback // Metal does not support Transform Feedback
throw new NotSupportedException();
} }
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
// Metal does not support Transform Feedback // Metal does not support Transform Feedback
throw new NotSupportedException();
} }
public void Dispose() public void Dispose()
@ -311,4 +371,4 @@ namespace Ryujinx.Graphics.Metal
} }
} }
} }

View file

@ -0,0 +1,19 @@
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
namespace Ryujinx.Graphics.Metal
{
public class Sampler : ISampler
{
private MTLSamplerState _mtlSamplerState;
public Sampler(MTLSamplerState mtlSamplerState)
{
_mtlSamplerState = mtlSamplerState;
}
public void Dispose()
{
}
}
}

View file

@ -1,14 +1,44 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System; using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal namespace Ryujinx.Graphics.Metal
{ {
class TextureView : ITexture, IDisposable [SupportedOSPlatform("macos")]
class Texture : ITexture, IDisposable
{ {
public int Width { get; } private readonly TextureCreateInfo _info;
public int Height { get; }
public float ScaleFactor { get; } public MTLTexture MTLTexture;
public TextureCreateInfo Info => Info;
public int Width => Info.Width;
public int Height => Info.Height;
public Texture(MTLDevice device, TextureCreateInfo info, int firstLayer, int firstLevel)
{
_info = info;
var descriptor = new MTLTextureDescriptor();
descriptor.PixelFormat = FormatTable.GetFormat(Info.Format);
// descriptor.Usage =
descriptor.Width = (ulong)Width;
descriptor.Height = (ulong)Height;
descriptor.Depth = (ulong)Info.Depth;
descriptor.SampleCount = (ulong)Info.Samples;
descriptor.TextureType = Info.Target.Convert();
descriptor.Swizzle = new MTLTextureSwizzleChannels
{
red = Info.SwizzleR.Convert(),
green = Info.SwizzleG.Convert(),
blue = Info.SwizzleB.Convert(),
alpha = Info.SwizzleA.Convert()
};
MTLTexture = device.NewTexture(descriptor);
}
public void CopyTo(ITexture destination, int firstLayer, int firstLevel) public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
{ {
throw new NotImplementedException(); throw new NotImplementedException();