mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2026-02-17 22:36:51 -05:00
Fix Geometry/TFB on compute, Buffer Textures, add Window Resizing (#28)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)}");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Metal
|
||||
return _mtlTexture;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
public virtual void Release()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user