Fix Geometry/TFB on compute, Buffer Textures, add Window Resizing (#28)

This commit is contained in:
riperiperi
2024-06-29 19:07:07 +01:00
committed by Evan Husted
parent bbbc9e529d
commit 4b53d18bef
28 changed files with 280 additions and 92 deletions

View File

@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Metal
public readonly PrimitiveTopology Topology => _currentState.Topology;
public readonly Texture[] RenderTargets => _currentState.RenderTargets;
public readonly Texture DepthStencil => _currentState.DepthStencil;
public readonly ComputeSize ComputeLocalSize => _currentState.ComputeProgram.ComputeLocalSize;
// RGBA32F is the biggest format
private const int ZeroBufferSize = 4 * 4;
@@ -811,6 +812,7 @@ namespace Ryujinx.Graphics.Metal
Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}");
return;
}
switch (stage)
{
case ShaderStage.Fragment:
@@ -852,10 +854,14 @@ namespace Ryujinx.Graphics.Metal
}
}
public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler)
public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler)
{
UpdateTexture(stage, binding, texture);
UpdateSampler(stage, binding, sampler);
if (sampler != null)
{
UpdateSampler(stage, binding, sampler.GetSampler());
}
}
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)

View File

@@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Metal
_programStrideChange = new Program(
[
new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl)
], device);
], device, new ComputeSize(64, 1, 1));
}
private static string ReadMsl(string fileName)
@@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Metal
_pipeline.SetStorageBuffers(1, sbRanges);
_pipeline.SetProgram(_programStrideChange);
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1);
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1);
// Restore previous state
_pipeline.SwapState(null);

View File

@@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Metal
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{
return new Program(shaders, _device);
return new Program(shaders, _device, info.ComputeLocalSize);
}
public ISampler CreateSampler(SamplerCreateInfo info)
@@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Metal
{
if (info.Target == Target.TextureBuffer)
{
return new TextureBuffer(this, info);
return new TextureBuffer(_device, this, _pipeline, info);
}
return new Texture(_device, this, _pipeline, info);

View File

@@ -347,13 +347,15 @@ namespace Ryujinx.Graphics.Metal
BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ)
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
var computeCommandEncoder = GetOrCreateComputeEncoder(true);
ComputeSize localSize = _encoderStateManager.ComputeLocalSize;
computeCommandEncoder.DispatchThreadgroups(
new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ },
new MTLSize { width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ });
new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z });
}
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
@@ -658,12 +660,11 @@ namespace Ryujinx.Graphics.Metal
{
if (texture is TextureBase tex)
{
if (sampler is Sampler samp)
if (sampler == null || sampler is Sampler)
{
var mtlSampler = samp.GetSampler();
var index = (ulong)binding;
_encoderStateManager.UpdateTextureAndSampler(stage, index, tex, mtlSampler);
_encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler);
}
}
}

View File

@@ -15,13 +15,16 @@ namespace Ryujinx.Graphics.Metal
public MTLFunction VertexFunction;
public MTLFunction FragmentFunction;
public MTLFunction ComputeFunction;
public ComputeSize ComputeLocalSize { get; }
private HashTableSlim<PipelineUid, MTLRenderPipelineState> _graphicsPipelineCache;
private MTLComputePipelineState? _computePipelineCache;
private bool _firstBackgroundUse;
public Program(ShaderSource[] shaders, MTLDevice device)
public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default)
{
ComputeLocalSize = computeLocalSize;
for (int index = 0; index < shaders.Length; index++)
{
ShaderSource shader = shaders[index];

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Foundation;
using SharpMetal.Metal;
using System;
@@ -249,6 +250,27 @@ namespace Ryujinx.Graphics.Metal
return pipelineState;
}
public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program)
{
ComputeSize localSize = program.ComputeLocalSize;
uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z);
if (maxThreads == 0)
{
throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension.");
}
var descriptor = new MTLComputePipelineDescriptor
{
ComputeFunction = program.ComputeFunction,
MaxTotalThreadsPerThreadgroup = maxThreads,
ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true,
};
return descriptor;
}
public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program)
{
if (program.TryGetComputePipeline(out var pipelineState))
@@ -256,8 +278,10 @@ namespace Ryujinx.Graphics.Metal
return pipelineState;
}
using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program);
var error = new NSError(IntPtr.Zero);
pipelineState = device.NewComputePipelineState(program.ComputeFunction, ref error);
pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error);
if (error != IntPtr.Zero)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}");

View File

@@ -37,7 +37,9 @@ namespace Ryujinx.Graphics.Metal
descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat);
_mtlTexture = _device.NewTexture(descriptor);
MtlFormat = pixelFormat;
descriptor.Dispose();
}
public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info)

View File

@@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Metal
return _mtlTexture;
}
public void Release()
public virtual void Release()
{
Dispose();
}

View File

@@ -7,27 +7,54 @@ using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class TextureBuffer : ITexture
class TextureBuffer : TextureBase, ITexture
{
private readonly MetalRenderer _renderer;
private MTLTextureDescriptor _descriptor;
private BufferHandle _bufferHandle;
private int _offset;
private int _size;
private int _bufferCount;
public int Width { get; }
public int Height { get; }
public MTLPixelFormat MtlFormat { get; }
public TextureBuffer(MetalRenderer renderer, TextureCreateInfo info)
public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info)
{
_renderer = renderer;
Width = info.Width;
Height = info.Height;
MtlFormat = FormatTable.GetFormat(info.Format);
MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format);
_descriptor = new MTLTextureDescriptor
{
PixelFormat = pixelFormat,
Usage = MTLTextureUsage.Unknown,
TextureType = MTLTextureType.TextureBuffer,
Width = (ulong)Info.Width,
Height = (ulong)Info.Height,
};
MtlFormat = pixelFormat;
}
private void RebuildStorage()
{
// Find the parent buffer, and try to build a texture from it.
// TODO: texture uses should register read/write usage on the assigned buffer.
Auto<DisposableBuffer> bufferAuto = _renderer.BufferManager.GetBuffer(_bufferHandle, false);
if (_mtlTexture.NativePtr != 0)
{
_mtlTexture.Dispose();
}
if (bufferAuto == null)
{
_mtlTexture = default;
}
else
{
DisposableBuffer buffer = bufferAuto.Get(_pipeline.Cbs, _offset, _size);
_descriptor.Width = (uint)(_size / Info.BytesPerPixel);
_mtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size);
}
}
public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
@@ -65,11 +92,6 @@ namespace Ryujinx.Graphics.Metal
throw new NotImplementedException();
}
public void Release()
{
}
public void SetData(IMemoryOwner<byte> data)
{
_renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span);
@@ -101,7 +123,14 @@ namespace Ryujinx.Graphics.Metal
_size = buffer.Size;
_bufferCount = _renderer.BufferManager.BufferCount;
Release();
RebuildStorage();
}
public override void Release()
{
_descriptor.Dispose();
base.Release();
}
}
}

View File

@@ -18,6 +18,10 @@ namespace Ryujinx.Graphics.Metal
private int _width;
private int _height;
private int _requestedWidth;
private int _requestedHeight;
// private bool _vsyncEnabled;
private AntiAliasing _currentAntiAliasing;
private bool _updateEffect;
@@ -35,10 +39,26 @@ namespace Ryujinx.Graphics.Metal
_metalLayer = metalLayer;
}
public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
private unsafe void ResizeIfNeeded()
{
if (_requestedWidth != 0 && _requestedHeight != 0)
{
// TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size.
var rect = new NSRect(_requestedWidth, _requestedHeight, 0, 0);
ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect);
_requestedWidth = 0;
_requestedHeight = 0;
}
}
public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{
if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex)
{
ResizeIfNeeded();
var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable"));
_width = (int)drawable.Texture.Width;
@@ -114,7 +134,8 @@ namespace Ryujinx.Graphics.Metal
public void SetSize(int width, int height)
{
// Ignore
_requestedWidth = width;
_requestedHeight = height;
}
public void ChangeVSyncMode(bool vsyncEnabled)