mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-22 09:03:36 +00:00
Device local mapping for all buffers
May avoid issues with drivers with NVIDIA on linux/older gpus on windows when using large buffers (?) Also some performance things and fixes issues with opengl games loading textures weird.
This commit is contained in:
parent
74f8ef93c7
commit
4994e50d1c
15 changed files with 539 additions and 99 deletions
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -66,6 +67,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasCommandBufferDependency(CommandBufferScoped cbs)
|
||||||
|
{
|
||||||
|
return _cbOwnership.IsSet(cbs.CommandBufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasRentedCommandBufferDependency(CommandBufferPool cbp)
|
||||||
|
{
|
||||||
|
return _cbOwnership.AnySet();
|
||||||
|
}
|
||||||
|
|
||||||
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
|
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
|
||||||
{
|
{
|
||||||
// We don't want to add a reference to this object to the command buffer
|
// We don't want to add a reference to this object to the command buffer
|
||||||
|
@ -94,11 +105,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void IncrementReferenceCount()
|
public void IncrementReferenceCount()
|
||||||
{
|
{
|
||||||
if (_referenceCount == 0)
|
if (Interlocked.Increment(ref _referenceCount) == 1)
|
||||||
{
|
{
|
||||||
|
Interlocked.Decrement(ref _referenceCount);
|
||||||
throw new Exception("Attempted to inc ref of dead object.");
|
throw new Exception("Attempted to inc ref of dead object.");
|
||||||
}
|
}
|
||||||
_referenceCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DecrementReferenceCount(int cbIndex)
|
public void DecrementReferenceCount(int cbIndex)
|
||||||
|
@ -109,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void DecrementReferenceCount()
|
public void DecrementReferenceCount()
|
||||||
{
|
{
|
||||||
if (--_referenceCount == 0)
|
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||||
{
|
{
|
||||||
_value.Dispose();
|
_value.Dispose();
|
||||||
_value = default;
|
_value = default;
|
||||||
|
|
107
Ryujinx.Graphics.Vulkan/BackgroundResources.cs
Normal file
107
Ryujinx.Graphics.Vulkan/BackgroundResources.cs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
class BackgroundResource : IDisposable
|
||||||
|
{
|
||||||
|
private VulkanGraphicsDevice _gd;
|
||||||
|
private Device _device;
|
||||||
|
|
||||||
|
private CommandBufferPool _pool;
|
||||||
|
private PersistentFlushBuffer _flushBuffer;
|
||||||
|
|
||||||
|
public BackgroundResource(VulkanGraphicsDevice gd, Device device)
|
||||||
|
{
|
||||||
|
_gd = gd;
|
||||||
|
_device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandBufferPool GetPool()
|
||||||
|
{
|
||||||
|
if (_pool == null)
|
||||||
|
{
|
||||||
|
_pool = new CommandBufferPool(_gd.Api, _device, _gd.BackgroundQueue, _gd.QueueFamilyIndex, isLight: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PersistentFlushBuffer GetFlushBuffer()
|
||||||
|
{
|
||||||
|
if (_flushBuffer == null)
|
||||||
|
{
|
||||||
|
_flushBuffer = new PersistentFlushBuffer(_gd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _flushBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_pool?.Dispose();
|
||||||
|
_flushBuffer?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackgroundResources : IDisposable
|
||||||
|
{
|
||||||
|
private VulkanGraphicsDevice _gd;
|
||||||
|
private Device _device;
|
||||||
|
|
||||||
|
private Dictionary<Thread, BackgroundResource> _resources;
|
||||||
|
|
||||||
|
public BackgroundResources(VulkanGraphicsDevice gd, Device device)
|
||||||
|
{
|
||||||
|
_gd = gd;
|
||||||
|
_device = device;
|
||||||
|
|
||||||
|
_resources = new Dictionary<Thread, BackgroundResource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cleanup()
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<Thread, BackgroundResource> tuple in _resources)
|
||||||
|
{
|
||||||
|
if (!tuple.Key.IsAlive)
|
||||||
|
{
|
||||||
|
tuple.Value.Dispose();
|
||||||
|
_resources.Remove(tuple.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackgroundResource Get()
|
||||||
|
{
|
||||||
|
Thread thread = Thread.CurrentThread;
|
||||||
|
|
||||||
|
lock (_resources)
|
||||||
|
{
|
||||||
|
BackgroundResource resource;
|
||||||
|
if (!_resources.TryGetValue(thread, out resource))
|
||||||
|
{
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
|
resource = new BackgroundResource(_gd, _device);
|
||||||
|
|
||||||
|
_resources[thread] = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (_resources)
|
||||||
|
{
|
||||||
|
foreach (var resource in _resources.Values)
|
||||||
|
{
|
||||||
|
resource.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,29 @@
|
||||||
_masks = new long[(count + IntMask) / IntSize];
|
_masks = new long[(count + IntMask) / IntSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AnySet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _masks.Length; i++)
|
||||||
|
{
|
||||||
|
if (_masks[i] != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSet(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit / IntSize;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
return (_masks[wordIndex] & wordMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Set(int bit)
|
public bool Set(int bit)
|
||||||
{
|
{
|
||||||
int wordIndex = bit / IntSize;
|
int wordIndex = bit / IntSize;
|
||||||
|
|
|
@ -129,7 +129,32 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
|
public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
|
||||||
{
|
{
|
||||||
return GetDataStorage(offset, size);
|
if (_map != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return GetDataStorage(offset, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BackgroundResource resource = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
|
if (_gd.CommandBufferPool.OwnedByCurrentThread)
|
||||||
|
{
|
||||||
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
|
return resource.GetFlushBuffer().GetBufferData(_gd.CommandBufferPool, this, offset, size);
|
||||||
|
}
|
||||||
|
else if (_gd.BackgroundQueue.Handle != 0)
|
||||||
|
{
|
||||||
|
lock (_gd.BackgroundQueueLock)
|
||||||
|
{
|
||||||
|
return resource.GetFlushBuffer().GetBufferData(resource.GetPool(), this, offset, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new byte[size];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Span<byte> GetDataStorage(int offset, int size)
|
public unsafe Span<byte> GetDataStorage(int offset, int size)
|
||||||
|
@ -152,28 +177,37 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsFlush = _gd.CommandBufferPool.HasWaitableOnRentedCommandBuffer(_waitable, offset, dataSize);
|
if (_map != IntPtr.Zero)
|
||||||
bool needsWait = needsFlush || MayWait(offset, dataSize);
|
{
|
||||||
|
// If persistently mapped, set the data directly if the buffer is not currently in use.
|
||||||
|
//bool needsFlush = _gd.CommandBufferPool.HasWaitableOnRentedCommandBuffer(_waitable, offset, dataSize);
|
||||||
|
bool needsFlush = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool);// (_waitable, offset, dataSize);
|
||||||
|
|
||||||
|
if (!needsFlush)
|
||||||
|
{
|
||||||
|
WaitForFences(offset, dataSize);
|
||||||
|
|
||||||
|
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbs != null && !_buffer.HasCommandBufferDependency(cbs.Value))
|
||||||
|
{
|
||||||
|
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
||||||
|
// This avoids ending and beginning render passes on each buffer data upload.
|
||||||
|
|
||||||
|
cbs = _gd.PipelineInternal.GetPreloadCommandBuffer();
|
||||||
|
endRenderPass = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (cbs == null ||
|
if (cbs == null ||
|
||||||
!needsWait ||
|
|
||||||
!VulkanConfiguration.UseFastBufferUpdates ||
|
!VulkanConfiguration.UseFastBufferUpdates ||
|
||||||
|
data.Length > MaxUpdateBufferSize ||
|
||||||
!TryPushData(cbs.Value, endRenderPass, offset, data))
|
!TryPushData(cbs.Value, endRenderPass, offset, data))
|
||||||
{
|
{
|
||||||
// Some pending command might access the buffer,
|
_gd.BufferManager.StagingBuffer.PushData(_gd.CommandBufferPool, cbs, endRenderPass, this, offset, data);
|
||||||
// so we need to ensure they are all submited to the GPU before waiting.
|
|
||||||
// The flush below forces the submission of all pending commands.
|
|
||||||
if (needsFlush)
|
|
||||||
{
|
|
||||||
_gd.FlushAllCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitForFences(offset, dataSize);
|
|
||||||
|
|
||||||
if (_map != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +223,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_gd.BufferManager.StagingBuffer.PushData(_gd.CommandBufferPool, null, null, this, offset, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data)
|
public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
|
@ -206,7 +244,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
endRenderPass();
|
endRenderPass?.Invoke();
|
||||||
|
|
||||||
var dstBuffer = GetBuffer(cbs.CommandBuffer, true).Get(cbs, dstOffset, data.Length).Value;
|
var dstBuffer = GetBuffer(cbs.CommandBuffer, true).Get(cbs, dstOffset, data.Length).Value;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
||||||
MemoryPropertyFlags.MemoryPropertyHostCachedBit;
|
MemoryPropertyFlags.MemoryPropertyHostCachedBit;
|
||||||
|
|
||||||
|
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
|
||||||
|
MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
|
||||||
|
|
||||||
|
private const MemoryPropertyFlags FlushableDeviceLocalBufferMemoryFlags =
|
||||||
|
MemoryPropertyFlags.MemoryPropertyHostVisibleBit |
|
||||||
|
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
||||||
|
MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
|
||||||
|
|
||||||
private const BufferUsageFlags DefaultBufferUsageFlags =
|
private const BufferUsageFlags DefaultBufferUsageFlags =
|
||||||
BufferUsageFlags.BufferUsageTransferSrcBit |
|
BufferUsageFlags.BufferUsageTransferSrcBit |
|
||||||
BufferUsageFlags.BufferUsageTransferDstBit |
|
BufferUsageFlags.BufferUsageTransferDstBit |
|
||||||
|
@ -41,9 +49,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
StagingBuffer = new StagingBuffer(gd, this);
|
StagingBuffer = new StagingBuffer(gd, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateWithHandle(VulkanGraphicsDevice gd, int size)
|
public BufferHandle CreateWithHandle(VulkanGraphicsDevice gd, int size, bool deviceLocal)
|
||||||
{
|
{
|
||||||
var holder = Create(gd, size);
|
var holder = Create(gd, size, deviceLocal: deviceLocal);
|
||||||
if (holder == null)
|
if (holder == null)
|
||||||
{
|
{
|
||||||
return BufferHandle.Null;
|
return BufferHandle.Null;
|
||||||
|
@ -58,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe BufferHolder Create(VulkanGraphicsDevice gd, int size, bool forConditionalRendering = false)
|
public unsafe BufferHolder Create(VulkanGraphicsDevice gd, int size, bool forConditionalRendering = false, bool deviceLocal = false)
|
||||||
{
|
{
|
||||||
var usage = DefaultBufferUsageFlags;
|
var usage = DefaultBufferUsageFlags;
|
||||||
|
|
||||||
|
@ -81,7 +89,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
||||||
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, DefaultBufferMemoryFlags);
|
|
||||||
|
var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags;
|
||||||
|
|
||||||
|
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags);
|
||||||
|
|
||||||
if (allocation.Memory.Handle == 0UL)
|
if (allocation.Memory.Handle == 0UL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
class CommandBufferPool : IDisposable
|
class CommandBufferPool : IDisposable
|
||||||
{
|
{
|
||||||
public const int MaxCommandBuffers = 8;
|
public const int MaxCommandBuffers = 16;
|
||||||
|
|
||||||
private int _totalCommandBuffers;
|
private int _totalCommandBuffers;
|
||||||
private int _totalCommandBuffersMask;
|
private int _totalCommandBuffersMask;
|
||||||
|
@ -19,12 +19,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly CommandPool _pool;
|
private readonly CommandPool _pool;
|
||||||
private readonly Thread _owner;
|
private readonly Thread _owner;
|
||||||
|
|
||||||
|
public int PerFrame = 0;
|
||||||
|
|
||||||
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
|
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
|
||||||
|
|
||||||
private struct ReservedCommandBuffer
|
private struct ReservedCommandBuffer
|
||||||
{
|
{
|
||||||
public bool InUse;
|
public bool InUse;
|
||||||
public bool InConsumption;
|
public bool InConsumption;
|
||||||
|
public bool Active;
|
||||||
public CommandBuffer CommandBuffer;
|
public CommandBuffer CommandBuffer;
|
||||||
public FenceHolder Fence;
|
public FenceHolder Fence;
|
||||||
public SemaphoreHolder Semaphore;
|
public SemaphoreHolder Semaphore;
|
||||||
|
@ -48,6 +51,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Dependants = new List<IAuto>();
|
Dependants = new List<IAuto>();
|
||||||
Waitables = new HashSet<MultiFenceHolder>();
|
Waitables = new HashSet<MultiFenceHolder>();
|
||||||
Dependencies = new HashSet<SemaphoreHolder>();
|
Dependencies = new HashSet<SemaphoreHolder>();
|
||||||
|
|
||||||
|
Active = true; // Fence should be refreshed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +125,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
entry.Waitables.Add(waitable);
|
entry.Waitables.Add(waitable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasWaitable(int cbIndex, MultiFenceHolder waitable)
|
||||||
|
{
|
||||||
|
ref var entry = ref _commandBuffers[cbIndex];
|
||||||
|
return entry.Waitables.Contains(waitable);
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
|
public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
|
||||||
{
|
{
|
||||||
lock (_commandBuffers)
|
lock (_commandBuffers)
|
||||||
|
@ -140,11 +151,47 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsFenceOnRentedCommandBuffer(FenceHolder fence)
|
||||||
|
{
|
||||||
|
lock (_commandBuffers)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||||
|
{
|
||||||
|
ref var entry = ref _commandBuffers[i];
|
||||||
|
|
||||||
|
if (entry.InUse && entry.Fence == fence)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public FenceHolder GetFence(int cbIndex)
|
public FenceHolder GetFence(int cbIndex)
|
||||||
{
|
{
|
||||||
return _commandBuffers[cbIndex].Fence;
|
return _commandBuffers[cbIndex].Fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckConsumption(int startAt)
|
||||||
|
{
|
||||||
|
int wrapBefore = _totalCommandBuffers + 1;
|
||||||
|
int index = (startAt + wrapBefore) % _totalCommandBuffers;
|
||||||
|
|
||||||
|
for (int i = 0; i < _totalCommandBuffers - 1; i++)
|
||||||
|
{
|
||||||
|
ref var entry = ref _commandBuffers[index];
|
||||||
|
|
||||||
|
if (!entry.InUse && entry.InConsumption && entry.Fence.IsSignaled())
|
||||||
|
{
|
||||||
|
WaitAndDecrementRef(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = (index + wrapBefore) % _totalCommandBuffers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
|
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
|
||||||
{
|
{
|
||||||
Return(cbs);
|
Return(cbs);
|
||||||
|
@ -153,8 +200,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public CommandBufferScoped Rent()
|
public CommandBufferScoped Rent()
|
||||||
{
|
{
|
||||||
|
PerFrame++;
|
||||||
lock (_commandBuffers)
|
lock (_commandBuffers)
|
||||||
{
|
{
|
||||||
|
CheckConsumption(_cursor);
|
||||||
|
|
||||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||||
{
|
{
|
||||||
int currentIndex = _cursor;
|
int currentIndex = _cursor;
|
||||||
|
@ -165,6 +215,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
WaitAndDecrementRef(currentIndex);
|
WaitAndDecrementRef(currentIndex);
|
||||||
entry.InUse = true;
|
entry.InUse = true;
|
||||||
|
entry.Active = true;
|
||||||
|
|
||||||
var commandBufferBeginInfo = new CommandBufferBeginInfo()
|
var commandBufferBeginInfo = new CommandBufferBeginInfo()
|
||||||
{
|
{
|
||||||
|
@ -241,6 +292,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
ref var entry = ref _commandBuffers[cbIndex];
|
ref var entry = ref _commandBuffers[cbIndex];
|
||||||
|
|
||||||
|
if (!entry.Active)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Active = false;
|
||||||
|
|
||||||
if (entry.InConsumption)
|
if (entry.InConsumption)
|
||||||
{
|
{
|
||||||
entry.Fence.Wait();
|
entry.Fence.Wait();
|
||||||
|
|
|
@ -31,6 +31,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_pool.AddDependency(CommandBufferIndex, dependencyCbs);
|
_pool.AddDependency(CommandBufferIndex, dependencyCbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasWaitable(MultiFenceHolder waitable)
|
||||||
|
{
|
||||||
|
return _pool.HasWaitable(CommandBufferIndex, waitable);
|
||||||
|
}
|
||||||
|
|
||||||
public FenceHolder GetFence()
|
public FenceHolder GetFence()
|
||||||
{
|
{
|
||||||
return _pool.GetFence(CommandBufferIndex);
|
return _pool.GetFence(CommandBufferIndex);
|
||||||
|
|
|
@ -169,7 +169,7 @@ void main()
|
||||||
region[3] = temp;
|
region[3] = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ void main()
|
||||||
region[3] = temp;
|
region[3] = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
|
|
73
Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs
Normal file
73
Ryujinx.Graphics.Vulkan/PersistentFlushBuffer.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
internal class PersistentFlushBuffer : IDisposable
|
||||||
|
{
|
||||||
|
private VulkanGraphicsDevice _gd;
|
||||||
|
|
||||||
|
private BufferHolder _flushStorage;
|
||||||
|
|
||||||
|
public PersistentFlushBuffer(VulkanGraphicsDevice gd)
|
||||||
|
{
|
||||||
|
_gd = gd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferHolder ResizeIfNeeded(int size)
|
||||||
|
{
|
||||||
|
var flushStorage = _flushStorage;
|
||||||
|
|
||||||
|
if (flushStorage == null || size > _flushStorage.Size)
|
||||||
|
{
|
||||||
|
if (flushStorage != null)
|
||||||
|
{
|
||||||
|
flushStorage.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
flushStorage = _gd.BufferManager.Create(_gd, size);
|
||||||
|
_flushStorage = flushStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flushStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
|
||||||
|
{
|
||||||
|
var flushStorage = ResizeIfNeeded(size);
|
||||||
|
|
||||||
|
using (var cbs = cbp.Rent())
|
||||||
|
{
|
||||||
|
var srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
|
||||||
|
var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer);
|
||||||
|
|
||||||
|
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
flushStorage.WaitForFences();
|
||||||
|
return flushStorage.GetDataStorage(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> GetTextureData(CommandBufferPool cbp, TextureView view, int size)
|
||||||
|
{
|
||||||
|
GAL.TextureCreateInfo info = view.Info;
|
||||||
|
|
||||||
|
var flushStorage = ResizeIfNeeded(size);
|
||||||
|
|
||||||
|
using (var cbs = cbp.Rent())
|
||||||
|
{
|
||||||
|
var buffer = flushStorage.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
|
||||||
|
var image = view.GetImage().Get(cbs).Value;
|
||||||
|
|
||||||
|
view.CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
flushStorage.WaitForFences();
|
||||||
|
return flushStorage.GetDataStorage(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_flushStorage.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private PipelineCache _pipelineCache;
|
private PipelineCache _pipelineCache;
|
||||||
|
|
||||||
protected CommandBufferScoped Cbs;
|
protected CommandBufferScoped Cbs;
|
||||||
|
protected CommandBufferScoped? PreloadCbs;
|
||||||
protected CommandBuffer CommandBuffer;
|
protected CommandBuffer CommandBuffer;
|
||||||
|
|
||||||
public CommandBufferScoped CurrentCommandBuffer => Cbs;
|
public CommandBufferScoped CurrentCommandBuffer => Cbs;
|
||||||
|
|
|
@ -225,6 +225,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandBufferScoped GetPreloadCommandBuffer()
|
||||||
|
{
|
||||||
|
if (PreloadCbs == null)
|
||||||
|
{
|
||||||
|
PreloadCbs = Gd.CommandBufferPool.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PreloadCbs.Value;
|
||||||
|
}
|
||||||
|
|
||||||
public void FlushCommandsImpl([System.Runtime.CompilerServices.CallerMemberName] string caller = "")
|
public void FlushCommandsImpl([System.Runtime.CompilerServices.CallerMemberName] string caller = "")
|
||||||
{
|
{
|
||||||
// System.Console.WriteLine("flush by " + caller);
|
// System.Console.WriteLine("flush by " + caller);
|
||||||
|
@ -237,6 +247,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PreloadCbs != null)
|
||||||
|
{
|
||||||
|
PreloadCbs.Value.Dispose();
|
||||||
|
PreloadCbs = null;
|
||||||
|
}
|
||||||
|
|
||||||
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||||
|
|
||||||
// Restore per-command buffer state.
|
// Restore per-command buffer state.
|
||||||
|
|
|
@ -37,25 +37,55 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_freeSize = BufferSize;
|
_freeSize = BufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
public unsafe void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
if (data.Length > BufferSize)
|
bool isRender = cbs != null;
|
||||||
{
|
CommandBufferScoped scoped = cbs ?? cbp.Rent();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_freeSize < data.Length)
|
// Must push all data to the buffer. If it can't fit, split it up.
|
||||||
{
|
|
||||||
FreeCompleted();
|
|
||||||
|
|
||||||
|
endRenderPass?.Invoke();
|
||||||
|
|
||||||
|
while (data.Length > 0)
|
||||||
|
{
|
||||||
if (_freeSize < data.Length)
|
if (_freeSize < data.Length)
|
||||||
{
|
{
|
||||||
return false;
|
FreeCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (_freeSize == 0)
|
||||||
|
{
|
||||||
|
if (!WaitFreeCompleted(cbp))
|
||||||
|
{
|
||||||
|
if (isRender)
|
||||||
|
{
|
||||||
|
_gd.FlushAllCommands();
|
||||||
|
scoped = cbp.Rent();
|
||||||
|
isRender = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scoped = cbp.ReturnAndRent(scoped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunkSize = Math.Min(_freeSize, data.Length);
|
||||||
|
|
||||||
|
PushDataImpl(scoped, dst, dstOffset, data.Slice(0, chunkSize));
|
||||||
|
|
||||||
|
dstOffset += chunkSize;
|
||||||
|
data = data.Slice(chunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
endRenderPass();
|
if (!isRender)
|
||||||
|
{
|
||||||
|
scoped.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
var srcBuffer = _buffer.GetBuffer();
|
var srcBuffer = _buffer.GetBuffer();
|
||||||
var dstBuffer = dst.GetBuffer();
|
var dstBuffer = dst.GetBuffer();
|
||||||
|
|
||||||
|
@ -81,14 +111,61 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Debug.Assert(_freeSize >= 0);
|
Debug.Assert(_freeSize >= 0);
|
||||||
|
|
||||||
_pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
|
_pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Length > BufferSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_freeSize < data.Length)
|
||||||
|
{
|
||||||
|
FreeCompleted();
|
||||||
|
|
||||||
|
if (_freeSize < data.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endRenderPass();
|
||||||
|
|
||||||
|
PushDataImpl(cbs, dst, dstOffset, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool WaitFreeCompleted(CommandBufferPool cbp)
|
||||||
|
{
|
||||||
|
if (_pendingCopies.TryPeek(out var pc))
|
||||||
|
{
|
||||||
|
if (!pc.Fence.IsSignaled())
|
||||||
|
{
|
||||||
|
if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc.Fence.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dequeued = _pendingCopies.Dequeue();
|
||||||
|
Debug.Assert(dequeued.Fence == pc.Fence);
|
||||||
|
_freeSize += pc.Size;
|
||||||
|
pc.Fence.Put();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FreeCompleted()
|
private void FreeCompleted()
|
||||||
{
|
{
|
||||||
while (_pendingCopies.TryPeek(out var pc) && pc.Fence.IsSignaled())
|
FenceHolder signalledFence = null;
|
||||||
|
while (_pendingCopies.TryPeek(out var pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled()))
|
||||||
{
|
{
|
||||||
|
signalledFence = pc.Fence; // Already checked - don't need to do it again.
|
||||||
var dequeued = _pendingCopies.Dequeue();
|
var dequeued = _pendingCopies.Dequeue();
|
||||||
Debug.Assert(dequeued.Fence == pc.Fence);
|
Debug.Assert(dequeued.Fence == pc.Fence);
|
||||||
_freeSize += pc.Size;
|
_freeSize += pc.Size;
|
||||||
|
|
|
@ -228,9 +228,41 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return _image;
|
return _image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasCommandBufferDependency(CommandBufferScoped cbs)
|
||||||
|
{
|
||||||
|
if (_foreignAllocationAuto != null)
|
||||||
|
{
|
||||||
|
return _foreignAllocationAuto.HasCommandBufferDependency(cbs);
|
||||||
|
}
|
||||||
|
else if (_allocationAuto != null)
|
||||||
|
{
|
||||||
|
return _allocationAuto.HasCommandBufferDependency(cbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe void InitialTransition(ImageLayout srcLayout, ImageLayout dstLayout)
|
private unsafe void InitialTransition(ImageLayout srcLayout, ImageLayout dstLayout)
|
||||||
{
|
{
|
||||||
using var cbs = _gd.CommandBufferPool.Rent();
|
CommandBufferScoped cbs;
|
||||||
|
bool useTempCbs = !_gd.CommandBufferPool.OwnedByCurrentThread;
|
||||||
|
|
||||||
|
if (useTempCbs)
|
||||||
|
{
|
||||||
|
cbs = _gd.BackgroundResources.Get().GetPool().Rent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_gd.PipelineInternal != null)
|
||||||
|
{
|
||||||
|
cbs = _gd.PipelineInternal.GetPreloadCommandBuffer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
|
useTempCbs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var aspectFlags = _info.Format.ConvertAspectFlags();
|
var aspectFlags = _info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
|
@ -260,6 +292,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
null,
|
null,
|
||||||
1,
|
1,
|
||||||
barrier);
|
barrier);
|
||||||
|
|
||||||
|
if (useTempCbs)
|
||||||
|
{
|
||||||
|
cbs.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SampleCountFlags ConvertToSampleCountFlags(uint samples)
|
private static SampleCountFlags ConvertToSampleCountFlags(uint samples)
|
||||||
|
|
|
@ -237,12 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
lock (_gd.BackgroundQueueLock)
|
lock (_gd.BackgroundQueueLock)
|
||||||
{
|
{
|
||||||
using var cbp = new CommandBufferPool(
|
var cbp = _gd.BackgroundResources.Get().GetPool();
|
||||||
_gd.Api,
|
|
||||||
_device,
|
|
||||||
_gd.BackgroundQueue,
|
|
||||||
_gd.QueueFamilyIndex,
|
|
||||||
isLight: true);
|
|
||||||
|
|
||||||
using var cbs = cbp.Rent();
|
using var cbs = cbp.Rent();
|
||||||
|
|
||||||
|
@ -426,8 +421,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
dstSize += dstTemp.Info.GetMipSize2D(l);
|
dstSize += dstTemp.Info.GetMipSize2D(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize);
|
using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize, deviceLocal: true);
|
||||||
using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize);
|
using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize, deviceLocal: true);
|
||||||
|
|
||||||
src.Storage.CopyFromOrToBuffer(
|
src.Storage.CopyFromOrToBuffer(
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
|
@ -669,30 +664,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
bufferHolder.WaitForFences();
|
bufferHolder.WaitForFences();
|
||||||
byte[] bitmap = new byte[size];
|
byte[] bitmap = new byte[size];
|
||||||
GetDataFromBuffer(bufferHolder.GetDataStorage(0, size)).CopyTo(bitmap);
|
GetDataFromBuffer(bufferHolder.GetDataStorage(0, size), size, Span<byte>.Empty).CopyTo(bitmap);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> GetData()
|
public ReadOnlySpan<byte> GetData()
|
||||||
{
|
{
|
||||||
|
BackgroundResource resources = _gd.BackgroundResources.Get();
|
||||||
|
|
||||||
if (_gd.CommandBufferPool.OwnedByCurrentThread)
|
if (_gd.CommandBufferPool.OwnedByCurrentThread)
|
||||||
{
|
{
|
||||||
_gd.FlushAllCommands();
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
return GetData(_gd.CommandBufferPool);
|
return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer());
|
||||||
}
|
}
|
||||||
else if (_gd.BackgroundQueue.Handle != 0)
|
else if (_gd.BackgroundQueue.Handle != 0)
|
||||||
{
|
{
|
||||||
lock (_gd.BackgroundQueueLock)
|
lock (_gd.BackgroundQueueLock)
|
||||||
{
|
{
|
||||||
using var cbp = new CommandBufferPool(
|
return GetData(resources.GetPool(), resources.GetFlushBuffer());
|
||||||
_gd.Api,
|
|
||||||
_device,
|
|
||||||
_gd.BackgroundQueue,
|
|
||||||
_gd.QueueFamilyIndex,
|
|
||||||
isLight: true);
|
|
||||||
|
|
||||||
return GetData(cbp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -714,46 +704,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp)
|
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
|
||||||
{
|
{
|
||||||
int size;
|
int size = 0;
|
||||||
var bufferHolder = _flushStorage;
|
|
||||||
|
|
||||||
if (bufferHolder == null)
|
for (int level = 0; level < Info.Levels; level++)
|
||||||
{
|
{
|
||||||
size = 0;
|
size += Info.GetMipSize(level);
|
||||||
|
|
||||||
for (int level = 0; level < Info.Levels; level++)
|
|
||||||
{
|
|
||||||
size += Info.GetMipSize(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
size = GetBufferDataLength(size);
|
|
||||||
|
|
||||||
bufferHolder = _gd.BufferManager.Create(_gd, size);
|
|
||||||
|
|
||||||
var existingStorage = Interlocked.CompareExchange(ref _flushStorage, bufferHolder, null);
|
|
||||||
if (existingStorage != null)
|
|
||||||
{
|
|
||||||
bufferHolder.Dispose();
|
|
||||||
bufferHolder = existingStorage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size = bufferHolder.Size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var cbs = cbp.Rent())
|
size = GetBufferDataLength(size);
|
||||||
{
|
|
||||||
var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
|
|
||||||
var image = GetImage().Get(cbs).Value;
|
|
||||||
|
|
||||||
CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, size, true, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
Span<byte> result = flushBuffer.GetTextureData(cbp, this, size);
|
||||||
}
|
return GetDataFromBuffer(result, size, result);
|
||||||
|
|
||||||
bufferHolder.WaitForFences();
|
|
||||||
return GetDataFromBuffer(bufferHolder.GetDataStorage(0, size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(ReadOnlySpan<byte> data)
|
||||||
|
@ -772,12 +735,24 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
using var bufferHolder = _gd.BufferManager.Create(_gd, bufferDataLength);
|
using var bufferHolder = _gd.BufferManager.Create(_gd, bufferDataLength);
|
||||||
|
|
||||||
using var cbs = _gd.CommandBufferPool.Rent();
|
Auto<DisposableImage> imageAuto = GetImage();
|
||||||
|
|
||||||
|
// Load texture data inline if the texture has been used on the current command buffer.
|
||||||
|
|
||||||
|
bool loadInline = Storage.HasCommandBufferDependency(_gd.PipelineInternal.CurrentCommandBuffer);
|
||||||
|
|
||||||
|
var cbs = loadInline ? _gd.PipelineInternal.CurrentCommandBuffer : _gd.PipelineInternal.GetPreloadCommandBuffer();
|
||||||
|
|
||||||
|
if (loadInline)
|
||||||
|
{
|
||||||
|
_gd.PipelineInternal.EndRenderPass();
|
||||||
|
Common.Logging.Logger.Error?.PrintMsg(Common.Logging.LogClass.Gpu, "Loaded inline!");
|
||||||
|
}
|
||||||
|
|
||||||
CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);
|
CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);
|
||||||
|
|
||||||
var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
|
var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
|
||||||
var image = GetImage().Get(cbs).Value;
|
var image = imageAuto.Get(cbs).Value;
|
||||||
|
|
||||||
CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice);
|
CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice);
|
||||||
}
|
}
|
||||||
|
@ -813,11 +788,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
input.CopyTo(storage);
|
input.CopyTo(storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage)
|
private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
|
||||||
{
|
{
|
||||||
if (NeedsD24S8Conversion())
|
if (NeedsD24S8Conversion())
|
||||||
{
|
{
|
||||||
byte[] output = new byte[GetBufferDataLength(storage.Length)];
|
if (output.IsEmpty)
|
||||||
|
{
|
||||||
|
output = new byte[GetBufferDataLength(size)];
|
||||||
|
}
|
||||||
|
|
||||||
FormatConverter.ConvertD32FS8ToD24S8(output, storage);
|
FormatConverter.ConvertD32FS8ToD24S8(output, storage);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -830,7 +809,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return Info.Format == GAL.Format.D24UnormS8Uint && VkFormat == VkFormat.D32SfloatS8Uint;
|
return Info.Format == GAL.Format.D24UnormS8Uint && VkFormat == VkFormat.D32SfloatS8Uint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyFromOrToBuffer(
|
public void CopyFromOrToBuffer(
|
||||||
CommandBuffer commandBuffer,
|
CommandBuffer commandBuffer,
|
||||||
VkBuffer buffer,
|
VkBuffer buffer,
|
||||||
Image image,
|
Image image,
|
||||||
|
|
|
@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
internal CommandBufferPool CommandBufferPool { get; private set; }
|
internal CommandBufferPool CommandBufferPool { get; private set; }
|
||||||
internal DescriptorSetManager DescriptorSetManager { get; private set; }
|
internal DescriptorSetManager DescriptorSetManager { get; private set; }
|
||||||
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
|
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
|
||||||
|
internal BackgroundResources BackgroundResources { get; private set; }
|
||||||
|
|
||||||
internal BufferManager BufferManager { get; private set; }
|
internal BufferManager BufferManager { get; private set; }
|
||||||
|
|
||||||
|
@ -170,6 +171,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
PipelineLayoutCache = new PipelineLayoutCache();
|
PipelineLayoutCache = new PipelineLayoutCache();
|
||||||
|
|
||||||
|
BackgroundResources = new BackgroundResources(this, _device);
|
||||||
|
|
||||||
BufferManager = new BufferManager(this, _physicalDevice, _device);
|
BufferManager = new BufferManager(this, _physicalDevice, _device);
|
||||||
|
|
||||||
_syncManager = new SyncManager(this, _device);
|
_syncManager = new SyncManager(this, _device);
|
||||||
|
@ -194,7 +197,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size)
|
||||||
{
|
{
|
||||||
return BufferManager.CreateWithHandle(this, size);
|
return BufferManager.CreateWithHandle(this, size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProgram CreateProgram(IShader[] shaders, ShaderInfo info)
|
public IProgram CreateProgram(IShader[] shaders, ShaderInfo info)
|
||||||
|
@ -386,6 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
public unsafe void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
CommandBufferPool.Dispose();
|
CommandBufferPool.Dispose();
|
||||||
|
BackgroundResources.Dispose();
|
||||||
_counters.Dispose();
|
_counters.Dispose();
|
||||||
_window.Dispose();
|
_window.Dispose();
|
||||||
HelperShader.Dispose();
|
HelperShader.Dispose();
|
||||||
|
|
Loading…
Reference in a new issue