Make Vulkan memory allocator actually thread safe (#5575)
* Make Vulkan memory allocator actually thread safe * Make free thread safe too * PR feedback
This commit is contained in:
parent
ddc9ae2a83
commit
4a835bb2b9
2 changed files with 74 additions and 24 deletions
|
@ -1,6 +1,7 @@
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
||||||
{
|
{
|
||||||
|
@ -21,6 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_device = device;
|
_device = device;
|
||||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
|
@ -40,21 +43,37 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blockLists.Count; i++)
|
_lock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var bl = _blockLists[i];
|
for (int i = 0; i < _blockLists.Count; i++)
|
||||||
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
|
||||||
{
|
{
|
||||||
lock (bl)
|
var bl = _blockLists[i];
|
||||||
|
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||||
{
|
{
|
||||||
return bl.Allocate(size, alignment, map);
|
return bl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
_lock.EnterWriteLock();
|
||||||
_blockLists.Add(newBl);
|
|
||||||
return newBl.Allocate(size, alignment, map);
|
try
|
||||||
|
{
|
||||||
|
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||||
|
_blockLists.Add(newBl);
|
||||||
|
|
||||||
|
return newBl.Allocate(size, alignment, map);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int FindSuitableMemoryTypeIndex(
|
internal int FindSuitableMemoryTypeIndex(
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -166,6 +167,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||||
{
|
{
|
||||||
_blocks = new List<Block>();
|
_blocks = new List<Block>();
|
||||||
|
@ -174,6 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
MemoryTypeIndex = memoryTypeIndex;
|
MemoryTypeIndex = memoryTypeIndex;
|
||||||
ForBuffer = forBuffer;
|
ForBuffer = forBuffer;
|
||||||
_blockAlignment = blockAlignment;
|
_blockAlignment = blockAlignment;
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
||||||
|
@ -184,19 +188,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _blocks.Count; i++)
|
_lock.EnterReadLock();
|
||||||
{
|
|
||||||
var block = _blocks[i];
|
|
||||||
|
|
||||||
if (block.Mapped == map && block.Size >= size)
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _blocks.Count; i++)
|
||||||
{
|
{
|
||||||
ulong offset = block.Allocate(size, alignment);
|
var block = _blocks[i];
|
||||||
if (offset != InvalidOffset)
|
|
||||||
|
if (block.Mapped == map && block.Size >= size)
|
||||||
{
|
{
|
||||||
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
ulong offset = block.Allocate(size, alignment);
|
||||||
|
if (offset != InvalidOffset)
|
||||||
|
{
|
||||||
|
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
||||||
|
|
||||||
|
@ -244,14 +257,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
if (block.IsTotallyFree())
|
if (block.IsTotallyFree())
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blocks.Count; i++)
|
_lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_blocks[i] == block)
|
for (int i = 0; i < _blocks.Count; i++)
|
||||||
{
|
{
|
||||||
_blocks.RemoveAt(i);
|
if (_blocks[i] == block)
|
||||||
break;
|
{
|
||||||
|
_blocks.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
block.Destroy(_api, _device);
|
block.Destroy(_api, _device);
|
||||||
}
|
}
|
||||||
|
@ -259,13 +281,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private void InsertBlock(Block block)
|
private void InsertBlock(Block block)
|
||||||
{
|
{
|
||||||
int index = _blocks.BinarySearch(block);
|
_lock.EnterWriteLock();
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index;
|
|
||||||
}
|
|
||||||
|
|
||||||
_blocks.Insert(index, block);
|
try
|
||||||
|
{
|
||||||
|
int index = _blocks.BinarySearch(block);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index = ~index;
|
||||||
|
}
|
||||||
|
|
||||||
|
_blocks.Insert(index, block);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
Loading…
Reference in a new issue