0
0
Fork 0
mirror of https://github.com/GreemDev/Ryujinx.git synced 2025-01-24 21:42:00 +00:00
Ryujinx/src/Ryujinx.Graphics.Metal/BufferManager.cs
2024-09-28 19:03:01 -04:00

227 lines
6.7 KiB
C#

using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct ScopedTemporaryBuffer : IDisposable
{
private readonly BufferManager _bufferManager;
private readonly bool _isReserved;
public readonly BufferRange Range;
public readonly BufferHolder Holder;
public BufferHandle Handle => Range.Handle;
public int Offset => Range.Offset;
public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved)
{
_bufferManager = bufferManager;
Range = new BufferRange(handle, offset, size);
Holder = holder;
_isReserved = isReserved;
}
public void Dispose()
{
if (!_isReserved)
{
_bufferManager.Delete(Range.Handle);
}
}
}
[SupportedOSPlatform("macos")]
class BufferManager : IDisposable
{
private readonly IdList<BufferHolder> _buffers;
private readonly MTLDevice _device;
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
public int BufferCount { get; private set; }
public StagingBuffer StagingBuffer { get; }
public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
{
_device = device;
_renderer = renderer;
_pipeline = pipeline;
_buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(_renderer, this);
}
public BufferHandle Create(nint pointer, int size)
{
// TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us.
var buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer == IntPtr.Zero)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}.");
return BufferHandle.Null;
}
var holder = new BufferHolder(_renderer, _pipeline, buffer, size);
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public BufferHandle CreateWithHandle(int size)
{
return CreateWithHandle(size, out _);
}
public BufferHandle CreateWithHandle(int size, out BufferHolder holder)
{
holder = Create(size);
if (holder == null)
{
return BufferHandle.Null;
}
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size)
{
StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size);
if (result.HasValue)
{
return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true);
}
else
{
// Create a temporary buffer.
BufferHandle handle = CreateWithHandle(size, out BufferHolder holder);
return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false);
}
}
public BufferHolder Create(int size)
{
var buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer != IntPtr.Zero)
{
return new BufferHolder(_renderer, _pipeline, buffer, size);
}
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}.");
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out var holder))
{
size = holder.Size;
return holder.GetBuffer(isWrite);
}
size = 0;
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, int offset, int size, bool isWrite)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBuffer(offset, size, isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBuffer(isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBufferI8ToI16(cbs, offset, size);
}
return null;
}
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetData(offset, size);
}
return new PinnedSpan<byte>();
}
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null);
}
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs)
{
if (TryGetBuffer(handle, out var holder))
{
holder.SetData(offset, data, cbs);
}
}
public void Delete(BufferHandle handle)
{
if (TryGetBuffer(handle, out var holder))
{
holder.Dispose();
_buffers.Remove((int)Unsafe.As<BufferHandle, ulong>(ref handle));
}
}
private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
{
return _buffers.TryGetValue((int)Unsafe.As<BufferHandle, ulong>(ref handle), out holder);
}
public void Dispose()
{
StagingBuffer.Dispose();
foreach (var buffer in _buffers)
{
buffer.Dispose();
}
}
}
}