aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2023-03-19 20:56:48 +0000
committerGitHub <noreply@github.com>2023-03-19 17:56:48 -0300
commit9f1cf6458c78a42256b1f390f5b3b9159b00a7cb (patch)
tree21e250d28c0fd611de0515fc19a381d4bebc6136 /Ryujinx.Graphics.Vulkan
parent67b4e63cff0d6ce9629c3032f2b0d6414cee1220 (diff)
Vulkan: Migrate buffers between memory types to improve GPU performance (#4540)
* Initial implementation of migration between memory heaps - Missing OOM handling - Missing `_map` data safety when remapping - Copy may not have completed yet (needs some kind of fence) - Map may be unmapped before it is done being used. (needs scoped access) - SSBO accesses are all "writes" - maybe pass info in another way. - Missing keeping map type when resizing buffers (should this be done?) * Ensure migrated data is in place before flushing. * Fix issue where old waitable would be signalled. - There is a real issue where existing Auto<> references need to be replaced. * Swap bound Auto<> instances when swapping buffer backing * Fix conversion buffers * Don't try move buffers if the host has shared memory. * Make GPU methods return PinnedSpan with scope * Storage Hint * Fix stupidity * Fix rebase * Tweak rules Attempt to sidestep BOTW slowdown * Remove line * Migrate only when command buffers flush * Change backing swap log to debug * Address some feedback * Disallow backing swap when the flush lock is held by the current thread * Make PinnedSpan from ReadOnlySpan explicitly unsafe * Fix some small issues - Index buffer swap fixed - Allocate DeviceLocal buffers using a separate block list to images. * Remove alternative flags * Address feedback
Diffstat (limited to 'Ryujinx.Graphics.Vulkan')
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferAllocationType.cs12
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferHolder.cs302
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferManager.cs122
-rw-r--r--Ryujinx.Graphics.Vulkan/BufferState.cs15
-rw-r--r--Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs21
-rw-r--r--Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs4
-rw-r--r--Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs2
-rw-r--r--Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs2
-rw-r--r--Ryujinx.Graphics.Vulkan/HelperShader.cs18
-rw-r--r--Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs2
-rw-r--r--Ryujinx.Graphics.Vulkan/IndexBufferState.cs11
-rw-r--r--Ryujinx.Graphics.Vulkan/MemoryAllocator.cs49
-rw-r--r--Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs4
-rw-r--r--Ryujinx.Graphics.Vulkan/PipelineBase.cs19
-rw-r--r--Ryujinx.Graphics.Vulkan/PipelineFull.cs19
-rw-r--r--Ryujinx.Graphics.Vulkan/TextureBuffer.cs8
-rw-r--r--Ryujinx.Graphics.Vulkan/TextureView.cs12
-rw-r--r--Ryujinx.Graphics.Vulkan/VertexBufferState.cs11
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs9
19 files changed, 545 insertions, 97 deletions
diff --git a/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs b/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
new file mode 100644
index 00000000..81489041
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Vulkan
+{
+ internal enum BufferAllocationType
+ {
+ Auto = 0,
+
+ HostMappedNoCache,
+ HostMapped,
+ DeviceLocal,
+ DeviceLocalMapped
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index 055d6a59..21b81bdd 100644
--- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -1,7 +1,10 @@
-using Ryujinx.Graphics.GAL;
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Threading;
using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format;
@@ -11,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan
{
private const int MaxUpdateBufferSize = 0x10000;
+ private const int SetCountThreshold = 100;
+ private const int WriteCountThreshold = 50;
+ private const int FlushCountThreshold = 5;
+
+ public const int DeviceLocalSizeThreshold = 256 * 1024; // 256kb
+
public const AccessFlags DefaultAccessFlags =
AccessFlags.IndirectCommandReadBit |
AccessFlags.ShaderReadBit |
@@ -21,10 +30,10 @@ namespace Ryujinx.Graphics.Vulkan
private readonly VulkanRenderer _gd;
private readonly Device _device;
- private readonly MemoryAllocation _allocation;
- private readonly Auto<DisposableBuffer> _buffer;
- private readonly Auto<MemoryAllocation> _allocationAuto;
- private readonly ulong _bufferHandle;
+ private MemoryAllocation _allocation;
+ private Auto<DisposableBuffer> _buffer;
+ private Auto<MemoryAllocation> _allocationAuto;
+ private ulong _bufferHandle;
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
@@ -32,11 +41,28 @@ namespace Ryujinx.Graphics.Vulkan
private IntPtr _map;
- private readonly MultiFenceHolder _waitable;
+ private MultiFenceHolder _waitable;
private bool _lastAccessIsWrite;
- public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size)
+ private BufferAllocationType _baseType;
+ private BufferAllocationType _currentType;
+ private bool _swapQueued;
+
+ public BufferAllocationType DesiredType { get; private set; }
+
+ private int _setCount;
+ private int _writeCount;
+ private int _flushCount;
+ private int _flushTemp;
+
+ private ReaderWriterLock _flushLock;
+ private FenceHolder _flushFence;
+ private int _flushWaiting;
+
+ private List<Action> _swapActions;
+
+ public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
{
_gd = gd;
_device = device;
@@ -47,9 +73,153 @@ namespace Ryujinx.Graphics.Vulkan
_bufferHandle = buffer.Handle;
Size = size;
_map = allocation.HostPointer;
+
+ _baseType = type;
+ _currentType = currentType;
+ DesiredType = currentType;
+
+ _flushLock = new ReaderWriterLock();
+ }
+
+ public bool TryBackingSwap(ref CommandBufferScoped? cbs)
+ {
+ if (_swapQueued && DesiredType != _currentType)
+ {
+ // Only swap if the buffer is not used in any queued command buffer.
+ bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);
+
+ if (!isRented && _gd.CommandBufferPool.OwnedByCurrentThread && !_flushLock.IsReaderLockHeld)
+ {
+ var currentAllocation = _allocationAuto;
+ var currentBuffer = _buffer;
+ IntPtr currentMap = _map;
+
+ (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType);
+
+ if (buffer.Handle != 0)
+ {
+ _flushLock.AcquireWriterLock(Timeout.Infinite);
+
+ ClearFlushFence();
+
+ _waitable = new MultiFenceHolder(Size);
+
+ _allocation = allocation;
+ _allocationAuto = new Auto<MemoryAllocation>(allocation);
+ _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(_gd.Api, _device, buffer), _waitable, _allocationAuto);
+ _bufferHandle = buffer.Handle;
+ _map = allocation.HostPointer;
+
+ if (_map != IntPtr.Zero && currentMap != IntPtr.Zero)
+ {
+ // Copy data directly. Readbacks don't have to wait if this is done.
+
+ unsafe
+ {
+ new Span<byte>((void*)currentMap, Size).CopyTo(new Span<byte>((void*)_map, Size));
+ }
+ }
+ else
+ {
+ if (cbs == null)
+ {
+ cbs = _gd.CommandBufferPool.Rent();
+ }
+
+ CommandBufferScoped cbsV = cbs.Value;
+
+ Copy(_gd, cbsV, currentBuffer, _buffer, 0, 0, Size);
+
+ // Need to wait for the data to reach the new buffer before data can be flushed.
+
+ _flushFence = _gd.CommandBufferPool.GetFence(cbsV.CommandBufferIndex);
+ _flushFence.Get();
+ }
+
+ Logger.Debug?.PrintMsg(LogClass.Gpu, $"Converted {Size} buffer {_currentType} to {resultType}");
+
+ _currentType = resultType;
+
+ if (_swapActions != null)
+ {
+ foreach (var action in _swapActions)
+ {
+ action();
+ }
+
+ _swapActions.Clear();
+ }
+
+ currentBuffer.Dispose();
+ currentAllocation.Dispose();
+
+ _gd.PipelineInternal.SwapBuffer(currentBuffer, _buffer);
+
+ _flushLock.ReleaseWriterLock();
+ }
+
+ _swapQueued = false;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ _swapQueued = false;
+
+ return true;
+ }
+ }
+
+ private void ConsiderBackingSwap()
+ {
+ if (_baseType == BufferAllocationType.Auto)
+ {
+ if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold)
+ {
+ if (_flushCount > 0 || _flushTemp-- > 0)
+ {
+ // Buffers that flush should ideally be mapped in host address space for easy copies.
+ // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages).
+ // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached.
+ DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
+
+ // It's harder for a buffer that is flushed to revert to another type of mapping.
+ if (_flushCount > 0)
+ {
+ _flushTemp = 1000;
+ }
+ }
+ else if (_writeCount >= WriteCountThreshold)
+ {
+ // Buffers that are written often should ideally be in the device local heap. (Storage buffers)
+ DesiredType = BufferAllocationType.DeviceLocal;
+ }
+ else if (_setCount > SetCountThreshold)
+ {
+ // Buffers that have their data set often should ideally be host mapped. (Constant buffers)
+ DesiredType = BufferAllocationType.HostMapped;
+ }
+
+ _flushCount = 0;
+ _writeCount = 0;
+ _setCount = 0;
+ }
+
+ if (!_swapQueued && DesiredType != _currentType)
+ {
+ _swapQueued = true;
+
+ _gd.PipelineInternal.AddBackingSwap(this);
+ }
+ }
}
- public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size)
+ public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView)
{
var bufferViewCreateInfo = new BufferViewCreateInfo()
{
@@ -62,9 +232,19 @@ namespace Ryujinx.Graphics.Vulkan
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
+ (_swapActions ??= new List<Action>()).Add(invalidateView);
+
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
}
+ public void InheritMetrics(BufferHolder other)
+ {
+ _setCount = other._setCount;
+ _writeCount = other._writeCount;
+ _flushCount = other._flushCount;
+ _flushTemp = other._flushTemp;
+ }
+
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
{
// If the last access is write, we always need a barrier to be sure we will read or modify
@@ -104,12 +284,22 @@ namespace Ryujinx.Graphics.Vulkan
return _buffer;
}
- public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false)
+ public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false, bool isSSBO = false)
{
if (isWrite)
{
+ _writeCount++;
+
SignalWrite(0, Size);
}
+ else if (isSSBO)
+ {
+ // Always consider SSBO access for swapping to device local memory.
+
+ _writeCount++;
+
+ ConsiderBackingSwap();
+ }
return _buffer;
}
@@ -118,6 +308,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (isWrite)
{
+ _writeCount++;
+
SignalWrite(offset, size);
}
@@ -126,6 +318,8 @@ namespace Ryujinx.Graphics.Vulkan
public void SignalWrite(int offset, int size)
{
+ ConsiderBackingSwap();
+
if (offset == 0 && size == Size)
{
_cachedConvertedBuffers.Clear();
@@ -147,11 +341,76 @@ namespace Ryujinx.Graphics.Vulkan
return _map;
}
- public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
+ private void ClearFlushFence()
+ {
+ // Asusmes _flushLock is held as writer.
+
+ if (_flushFence != null)
+ {
+ if (_flushWaiting == 0)
+ {
+ _flushFence.Put();
+ }
+
+ _flushFence = null;
+ }
+ }
+
+ private void WaitForFlushFence()
+ {
+ // Assumes the _flushLock is held as reader, returns in same state.
+
+ if (_flushFence != null)
+ {
+ // If storage has changed, make sure the fence has been reached so that the data is in place.
+
+ var cookie = _flushLock.UpgradeToWriterLock(Timeout.Infinite);
+
+ if (_flushFence != null)
+ {
+ var fence = _flushFence;
+ Interlocked.Increment(ref _flushWaiting);
+
+ // Don't wait in the lock.
+
+ var restoreCookie = _flushLock.ReleaseLock();
+
+ fence.Wait();
+
+ _flushLock.RestoreLock(ref restoreCookie);
+
+ if (Interlocked.Decrement(ref _flushWaiting) == 0)
+ {
+ fence.Put();
+ }
+
+ _flushFence = null;
+ }
+
+ _flushLock.DowngradeFromWriterLock(ref cookie);
+ }
+ }
+
+ public unsafe PinnedSpan<byte> GetData(int offset, int size)
{
+ _flushLock.AcquireReaderLock(Timeout.Infinite);
+
+ WaitForFlushFence();
+
+ _flushCount++;
+
+ Span<byte> result;
+
if (_map != IntPtr.Zero)
{
- return GetDataStorage(offset, size);
+ result = GetDataStorage(offset, size);
+
+ // Need to be careful here, the buffer can't be unmapped while the data is being used.
+ _buffer.IncrementReferenceCount();
+
+ _flushLock.ReleaseReaderLock();
+
+ return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
}
else
{
@@ -161,12 +420,17 @@ namespace Ryujinx.Graphics.Vulkan
{
_gd.FlushAllCommands();
- return resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
+ result = resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
}
else
{
- return resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
+ result = resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
}
+
+ _flushLock.ReleaseReaderLock();
+
+ // Flush buffer is pinned until the next GetBufferData on the thread, which is fine for current uses.
+ return PinnedSpan<byte>.UnsafeFromSpan(result);
}
}
@@ -190,6 +454,8 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
+ _setCount++;
+
if (_map != IntPtr.Zero)
{
// If persistently mapped, set the data directly if the buffer is not currently in use.
@@ -268,6 +534,8 @@ namespace Ryujinx.Graphics.Vulkan
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
+ _writeCount--;
+
InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
@@ -502,11 +770,19 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose()
{
+ _swapQueued = false;
+
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
_buffer.Dispose();
_allocationAuto.Dispose();
_cachedConvertedBuffers.Dispose();
+
+ _flushLock.AcquireWriterLock(Timeout.Infinite);
+
+ ClearFlushFence();
+
+ _flushLock.ReleaseWriterLock();
}
}
}
diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 49fdd75d..f8f41e5b 100644
--- a/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -4,6 +4,7 @@ using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using VkFormat = Silk.NET.Vulkan.Format;
+using VkBuffer = Silk.NET.Vulkan.Buffer;
namespace Ryujinx.Graphics.Vulkan
{
@@ -16,17 +17,17 @@ namespace Ryujinx.Graphics.Vulkan
// Some drivers don't expose a "HostCached" memory type,
// so we need those alternative flags for the allocation to succeed there.
- private const MemoryPropertyFlags DefaultBufferMemoryAltFlags =
+ private const MemoryPropertyFlags DefaultBufferMemoryNoCacheFlags =
MemoryPropertyFlags.HostVisibleBit |
MemoryPropertyFlags.HostCoherentBit;
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit;
- private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
+ private const MemoryPropertyFlags DeviceLocalMappedBufferMemoryFlags =
+ MemoryPropertyFlags.DeviceLocalBit |
MemoryPropertyFlags.HostVisibleBit |
- MemoryPropertyFlags.HostCoherentBit |
- MemoryPropertyFlags.DeviceLocalBit;
+ MemoryPropertyFlags.HostCoherentBit;
private const BufferUsageFlags DefaultBufferUsageFlags =
BufferUsageFlags.TransferSrcBit |
@@ -54,14 +55,14 @@ namespace Ryujinx.Graphics.Vulkan
StagingBuffer = new StagingBuffer(gd, this);
}
- public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal)
+ public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
{
- return CreateWithHandle(gd, size, deviceLocal, out _);
+ return CreateWithHandle(gd, size, out _, baseType, storageHint);
}
- public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder)
+ public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
{
- holder = Create(gd, size, deviceLocal: deviceLocal);
+ holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
if (holder == null)
{
return BufferHandle.Null;
@@ -74,7 +75,12 @@ namespace Ryujinx.Graphics.Vulkan
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
- public unsafe BufferHolder Create(VulkanRenderer gd, int size, bool forConditionalRendering = false, bool deviceLocal = false)
+ public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking(
+ VulkanRenderer gd,
+ int size,
+ BufferAllocationType type,
+ bool forConditionalRendering = false,
+ BufferAllocationType fallbackType = BufferAllocationType.Auto)
{
var usage = DefaultBufferUsageFlags;
@@ -98,48 +104,106 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
- MemoryPropertyFlags allocateFlags;
- MemoryPropertyFlags allocateFlagsAlt;
+ MemoryAllocation allocation;
- if (deviceLocal)
- {
- allocateFlags = DeviceLocalBufferMemoryFlags;
- allocateFlagsAlt = DeviceLocalBufferMemoryFlags;
- }
- else
+ do
{
- allocateFlags = DefaultBufferMemoryFlags;
- allocateFlagsAlt = DefaultBufferMemoryAltFlags;
+ var allocateFlags = type switch
+ {
+ BufferAllocationType.HostMappedNoCache => DefaultBufferMemoryNoCacheFlags,
+ BufferAllocationType.HostMapped => DefaultBufferMemoryFlags,
+ BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags,
+ BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags,
+ _ => DefaultBufferMemoryFlags
+ };
+
+ // If an allocation with this memory type fails, fall back to the previous one.
+ try
+ {
+ allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, true);
+ }
+ catch (VulkanException)
+ {
+ allocation = default;
+ }
}
-
- var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
+ while (allocation.Memory.Handle == 0 && (--type != fallbackType));
if (allocation.Memory.Handle == 0UL)
{
gd.Api.DestroyBuffer(_device, buffer, null);
- return null;
+ return default;
}
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
- return new BufferHolder(gd, _device, buffer, allocation, size);
+ return (buffer, allocation, type);
}
- public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size)
+ public unsafe BufferHolder Create(
+ VulkanRenderer gd,
+ int size,
+ bool forConditionalRendering = false,
+ BufferAllocationType baseType = BufferAllocationType.HostMapped,
+ BufferHandle storageHint = default)
+ {
+ BufferAllocationType type = baseType;
+ BufferHolder storageHintHolder = null;
+
+ if (baseType == BufferAllocationType.Auto)
+ {
+ if (gd.IsSharedMemory)
+ {
+ baseType = BufferAllocationType.HostMapped;
+ type = baseType;
+ }
+ else
+ {
+ type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped;
+ }
+
+ if (storageHint != BufferHandle.Null)
+ {
+ if (TryGetBuffer(storageHint, out storageHintHolder))
+ {
+ type = storageHintHolder.DesiredType;
+ }
+ }
+ }
+
+ (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
+ CreateBacking(gd, size, type, forConditionalRendering);
+
+ if (buffer.Handle != 0)
+ {
+ var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType);
+
+ if (storageHintHolder != null)
+ {
+ holder.InheritMetrics(storageHintHolder);
+ }
+
+ return holder;
+ }
+
+ return null;
+ }
+
+ public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size, Action invalidateView)
{
if (TryGetBuffer(handle, out var holder))
{
- return holder.CreateView(format, offset, size);
+ return holder.CreateView(format, offset, size, invalidateView);
}
return null;
}
- public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite)
+ public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, bool isSSBO = false)
{
if (TryGetBuffer(handle, out var holder))
{
- return holder.GetBuffer(commandBuffer, isWrite);
+ return holder.GetBuffer(commandBuffer, isWrite, isSSBO);
}
return null;
@@ -332,14 +396,14 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
- public ReadOnlySpan<byte> GetData(BufferHandle handle, int offset, int size)
+ public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetData(offset, size);
}
- return ReadOnlySpan<byte>.Empty;
+ return new PinnedSpan<byte>();
}
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/Ryujinx.Graphics.Vulkan/BufferState.cs
index f3a58469..6829f833 100644
--- a/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -2,14 +2,14 @@
namespace Ryujinx.Graphics.Vulkan
{
- readonly struct BufferState : IDisposable
+ struct BufferState : IDisposable
{
public static BufferState Null => new BufferState(null, 0, 0);
private readonly int _offset;
private readonly int _size;
- private readonly Auto<DisposableBuffer> _buffer;
+ private Auto<DisposableBuffer> _buffer;
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{
@@ -29,6 +29,17 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ if (_buffer == from)
+ {
+ _buffer.DecrementReferenceCount();
+ to.IncrementReferenceCount();
+
+ _buffer = to;
+ }
+ }
+
public void Dispose()
{
_buffer?.DecrementReferenceCount();
diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 19a08502..7e126e04 100644
--- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan
else
{
// If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings.
- _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, deviceLocal: true);
+ _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, baseType: BufferAllocationType.DeviceLocal);
}
_dummyTexture = gd.CreateTextureView(new TextureCreateInfo(
@@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan
var buffer = assignment.Range;
int index = assignment.Binding;
- Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
+ Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false, isSSBO: true);
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
DescriptorBufferInfo info = new DescriptorBufferInfo()
@@ -640,6 +640,23 @@ namespace Ryujinx.Graphics.Vulkan
Array.Clear(_storageSet);
}
+ private void SwapBuffer(Auto<DisposableBuffer>[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ for (int i = 0; i < list.Length; i++)
+ {
+ if (list[i] == from)
+ {
+ list[i] = to;
+ }
+ }
+ }
+
+ public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ SwapBuffer(_uniformBufferRefs, from, to);
+ SwapBuffer(_storageBufferRefs, from, to);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
index a1207059..a871679b 100644
--- a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
+++ b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
@@ -156,11 +156,11 @@ namespace Ryujinx.Graphics.Vulkan.Effects
};
int rangeSize = dimensionsBuffer.Length * sizeof(float);
- var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
+ var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
- var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false);
+ var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float));
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
int threadGroupWorkRegionDim = 16;
diff --git a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
index 0f6a0a7b..9e73e1b8 100644
--- a/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
+++ b/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
@@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
int rangeSize = resolutionBuffer.Length * sizeof(float);
- var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
+ var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
diff --git a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index 4dcdaa64..bf698ade 100644
--- a/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
+++ b/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
@@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
int rangeSize = resolutionBuffer.Length * sizeof(float);
- var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
+ var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize);
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index c67389aa..8eb3088e 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -394,7 +394,7 @@ namespace Ryujinx.Graphics.Vulkan
(region[2], region[3]) = (region[3], region[2]);
}
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
@@ -495,7 +495,7 @@ namespace Ryujinx.Graphics.Vulkan
(region[2], region[3]) = (region[3], region[2]);
}
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
@@ -649,7 +649,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs);
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize);
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
@@ -726,7 +726,7 @@ namespace Ryujinx.Graphics.Vulkan
(region[2], region[3]) = (region[3], region[2]);
}
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
@@ -802,7 +802,7 @@ namespace Ryujinx.Graphics.Vulkan
shaderParams[2] = size;
shaderParams[3] = srcOffset;
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
@@ -958,7 +958,7 @@ namespace Ryujinx.Graphics.Vulkan
shaderParams[0] = BitOperations.Log2((uint)ratio);
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
@@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Vulkan
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
@@ -1133,7 +1133,7 @@ namespace Ryujinx.Graphics.Vulkan
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
- var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
@@ -1407,7 +1407,7 @@ namespace Ryujinx.Graphics.Vulkan
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
- var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer);
+ var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer);
var patternBufferAuto = patternBuffer.GetBuffer();
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
index 90774293..11f4ec33 100644
--- a/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
@@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.Vulkan
}
// Expand the repeating pattern to the number of requested primitives.
- BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));
+ BufferHandle newBuffer = _gd.BufferManager.CreateWithHandle(_gd, expectedSize * sizeof(int));
// Copy the old data to the new one.
if (_repeatingBuffer != BufferHandle.Null)
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
index 64b95f60..75b18456 100644
--- a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
@@ -146,5 +146,16 @@ namespace Ryujinx.Graphics.Vulkan
{
return _buffer == buffer;
}
+
+ public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ if (_buffer == from)
+ {
+ _buffer.DecrementReferenceCount();
+ to.IncrementReferenceCount();
+
+ _buffer = to;
+ }
+ }
}
}
diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
index e4dcd916..6a786a96 100644
--- a/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
+++ b/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
@@ -28,32 +28,25 @@ namespace Ryujinx.Graphics.Vulkan
public MemoryAllocation AllocateDeviceMemory(
MemoryRequirements requirements,
- MemoryPropertyFlags flags = 0)
+ MemoryPropertyFlags flags = 0,
+ bool isBuffer = false)
{
- return AllocateDeviceMemory(requirements, flags, flags);
- }
-
- public MemoryAllocation AllocateDeviceMemory(
- MemoryRequirements requirements,
- MemoryPropertyFlags flags,
- MemoryPropertyFlags alternativeFlags)
- {
- int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
+ int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags);
if (memoryTypeIndex < 0)
{
return default;
}
bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
- return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map);
+ return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer);
}
- private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map)
+ private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
{
for (int i = 0; i < _blockLists.Count; i++)
{
var bl = _blockLists[i];
- if (bl.MemoryTypeIndex == memoryTypeIndex)
+ if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
{
lock (bl)
{
@@ -62,18 +55,15 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment);
+ var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
_blockLists.Add(newBl);
return newBl.Allocate(size, alignment, map);
}
private int FindSuitableMemoryTypeIndex(
uint memoryTypeBits,
- MemoryPropertyFlags flags,
- MemoryPropertyFlags alternativeFlags)
+ MemoryPropertyFlags flags)
{
- int bestCandidateIndex = -1;
-
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
{
var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
@@ -84,14 +74,27 @@ namespace Ryujinx.Graphics.Vulkan
{
return i;
}
- else if (type.PropertyFlags.HasFlag(alternativeFlags))
- {
- bestCandidateIndex = i;
- }
}
}
- return bestCandidateIndex;
+ return -1;
+ }
+
+ public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice)
+ {
+ // The device is regarded as having shared memory if all heaps have the device local bit.
+
+ api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
+
+ for (int i = 0; i < properties.MemoryHeapCount; i++)
+ {
+ if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
+ {
+ return false;
+ }
+ }
+
+ return true;
}
public void Dispose()
diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
index dc3eb598..e564cb26 100644
--- a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
+++ b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
@@ -162,15 +162,17 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device;
public int MemoryTypeIndex { get; }
+ public bool ForBuffer { get; }
private readonly int _blockAlignment;
- public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment)
+ public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
{
_blocks = new List<Block>();
_api = api;
_device = device;
MemoryTypeIndex = memoryTypeIndex;
+ ForBuffer = forBuffer;
_blockAlignment = blockAlignment;
}
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 3abab065..6c2f1684 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -1297,6 +1297,25 @@ namespace Ryujinx.Graphics.Vulkan
SignalStateChange();
}
+ public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ _indexBuffer.Swap(from, to);
+
+ for (int i = 0; i < _vertexBuffers.Length; i++)
+ {
+ _vertexBuffers[i].Swap(from, to);
+ }
+
+ for (int i = 0; i < _transformFeedbackBuffers.Length; i++)
+ {
+ _transformFeedbackBuffers[i].Swap(from, to);
+ }
+
+ _descriptorSetUpdater.SwapBuffer(from, to);
+
+ SignalCommandBufferChange();
+ }
+
public unsafe void TextureBarrier()
{
MemoryBarrier memoryBarrier = new MemoryBarrier()
diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index 6c026a07..8026103e 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -17,10 +17,13 @@ namespace Ryujinx.Graphics.Vulkan
private ulong _byteWeight;
+ private List<BufferHolder> _backingSwaps;
+
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
{
_activeQueries = new List<(QueryPool, bool)>();
_pendingQueryCopies = new();
+ _backingSwaps = new();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
}
@@ -185,6 +188,20 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ private void TryBackingSwaps()
+ {
+ CommandBufferScoped? cbs = null;
+
+ _backingSwaps.RemoveAll((holder) => holder.TryBackingSwap(ref cbs));
+
+ cbs?.Dispose();
+ }
+
+ public void AddBackingSwap(BufferHolder holder)
+ {
+ _backingSwaps.Add(holder);
+ }
+
public void Restore()
{
if (Pipeline != null)
@@ -230,6 +247,8 @@ namespace Ryujinx.Graphics.Vulkan
Gd.ResetCounterPool();
+ TryBackingSwaps();
+
Restore();
}
diff --git a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs
index bf9a6ead..738bf57d 100644
--- a/Ryujinx.Graphics.Vulkan/TextureBuffer.cs
+++ b/Ryujinx.Graphics.Vulkan/TextureBuffer.cs
@@ -57,12 +57,12 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotSupportedException();
}
- public ReadOnlySpan<byte> GetData()
+ public PinnedSpan<byte> GetData()
{
return _gd.GetBufferData(_bufferHandle, _offset, _size);
}
- public ReadOnlySpan<byte> GetData(int layer, int level)
+ public PinnedSpan<byte> GetData(int layer, int level)
{
return GetData();
}
@@ -128,7 +128,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_bufferView == null)
{
- _bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size);
+ _bufferView = _gd.BufferManager.CreateView(_bufferHandle, VkFormat, _offset, _size, ReleaseImpl);
}
return _bufferView?.Get(cbs, _offset, _size).Value ?? default;
@@ -147,7 +147,7 @@ namespace Ryujinx.Graphics.Vulkan
return bufferView.Get(cbs, _offset, _size).Value;
}
- bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size);
+ bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl);
if (bufferView != null)
{
diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs
index 264ecf5d..cd280d5f 100644
--- a/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -531,7 +531,7 @@ namespace Ryujinx.Graphics.Vulkan
return bitmap;
}
- public ReadOnlySpan<byte> GetData()
+ public PinnedSpan<byte> GetData()
{
BackgroundResource resources = _gd.BackgroundResources.Get();
@@ -539,15 +539,15 @@ namespace Ryujinx.Graphics.Vulkan
{
_gd.FlushAllCommands();
- return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer());
+ return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer()));
}
else
{
- return GetData(resources.GetPool(), resources.GetFlushBuffer());
+ return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
}
}
- public ReadOnlySpan<byte> GetData(int layer, int level)
+ public PinnedSpan<byte> GetData(int layer, int level)
{
BackgroundResource resources = _gd.BackgroundResources.Get();
@@ -555,11 +555,11 @@ namespace Ryujinx.Graphics.Vulkan
{
_gd.FlushAllCommands();
- return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level);
+ return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
}
else
{
- return GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level);
+ return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
}
}
diff --git a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
index 7a022010..c4856019 100644
--- a/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
@@ -129,6 +129,17 @@ namespace Ryujinx.Graphics.Vulkan
return _buffer == buffer;
}
+ public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
+ {
+ if (_buffer == from)
+ {
+ _buffer.DecrementReferenceCount();
+ to.IncrementReferenceCount();
+
+ _buffer = to;
+ }
+ }
+
public void Dispose()
{
// Only dispose if this buffer is not refetched on each bind.
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 8d4e54c4..7e7d3036 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsAmdGcn { get; private set; }
internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; }
+ internal bool IsSharedMemory { get; private set; }
public string GpuVendor { get; private set; }
public string GpuRenderer { get; private set; }
public string GpuVersion { get; private set; }
@@ -313,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan
portabilityFlags,
vertexBufferAlignment);
+ IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice);
+
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -373,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
_initialized = true;
}
- public BufferHandle CreateBuffer(int size)
+ public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
{
- return BufferManager.CreateWithHandle(this, size, false);
+ return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint);
}
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
@@ -439,7 +442,7 @@ namespace Ryujinx.Graphics.Vulkan
_syncManager.RegisterFlush();
}
- public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
+ public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
{
return BufferManager.GetData(buffer, offset, size);
}