Skip to content

Commit

Permalink
Make Vulkan memory allocator actually thread safe (#5575)
Browse files Browse the repository at this point in the history
* Make Vulkan memory allocator actually thread safe

* Make free thread safe too

* PR feedback
  • Loading branch information
gdkchan authored Sep 25, 2023
1 parent ddc9ae2 commit 4a835bb
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 23 deletions.
33 changes: 26 additions & 7 deletions src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Threading;

namespace Ryujinx.Graphics.Vulkan
{
Expand All @@ -13,6 +14,7 @@ class MemoryAllocator : IDisposable
private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment;
private readonly ReaderWriterLockSlim _lock;

public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
{
Expand All @@ -21,6 +23,7 @@ public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device devic
_device = device;
_blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
_lock = new(LockRecursionPolicy.NoRecursion);
}

public MemoryAllocation AllocateDeviceMemory(
Expand All @@ -40,21 +43,37 @@ public MemoryAllocation AllocateDeviceMemory(

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];
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
for (int i = 0; i < _blockLists.Count; i++)
{
lock (bl)
var bl = _blockLists[i];
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
{
return bl.Allocate(size, alignment, map);
}
}
}
finally
{
_lock.ExitReadLock();
}

_lock.EnterWriteLock();

var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
_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(
Expand Down
63 changes: 47 additions & 16 deletions src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace Ryujinx.Graphics.Vulkan
{
Expand Down Expand Up @@ -166,6 +167,8 @@ public unsafe void Destroy(Vk api, Device device)

private readonly int _blockAlignment;

private readonly ReaderWriterLockSlim _lock;

public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
{
_blocks = new List<Block>();
Expand All @@ -174,6 +177,7 @@ public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int
MemoryTypeIndex = memoryTypeIndex;
ForBuffer = forBuffer;
_blockAlignment = blockAlignment;
_lock = new(LockRecursionPolicy.NoRecursion);
}

public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
Expand All @@ -184,19 +188,28 @@ public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
}

for (int i = 0; i < _blocks.Count; i++)
{
var block = _blocks[i];
_lock.EnterReadLock();

if (block.Mapped == map && block.Size >= size)
try
{
for (int i = 0; i < _blocks.Count; i++)
{
ulong offset = block.Allocate(size, alignment);
if (offset != InvalidOffset)
var block = _blocks[i];

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);

Expand Down Expand Up @@ -244,28 +257,46 @@ public void Free(Block block, ulong offset, ulong size)

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);
break;
if (_blocks[i] == block)
{
_blocks.RemoveAt(i);
break;
}
}
}
finally
{
_lock.ExitWriteLock();
}

block.Destroy(_api, _device);
}
}

private void InsertBlock(Block block)
{
int index = _blocks.BinarySearch(block);
if (index < 0)
_lock.EnterWriteLock();

try
{
index = ~index;
}
int index = _blocks.BinarySearch(block);
if (index < 0)
{
index = ~index;
}

_blocks.Insert(index, block);
_blocks.Insert(index, block);
}
finally
{
_lock.ExitWriteLock();
}
}

public void Dispose()
Expand Down

0 comments on commit 4a835bb

Please sign in to comment.