/ src / Ryujinx.Graphics.Vulkan / MemoryAllocator.cs
MemoryAllocator.cs
  1  using Silk.NET.Vulkan;
  2  using System;
  3  using System.Collections.Generic;
  4  using System.Threading;
  5  
  6  namespace Ryujinx.Graphics.Vulkan
  7  {
  8      class MemoryAllocator : IDisposable
  9      {
 10          private const ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
 11  
 12          private readonly Vk _api;
 13          private readonly VulkanPhysicalDevice _physicalDevice;
 14          private readonly Device _device;
 15          private readonly List<MemoryAllocatorBlockList> _blockLists;
 16          private readonly int _blockAlignment;
 17          private readonly ReaderWriterLockSlim _lock;
 18  
 19          public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
 20          {
 21              _api = api;
 22              _physicalDevice = physicalDevice;
 23              _device = device;
 24              _blockLists = new List<MemoryAllocatorBlockList>();
 25              _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
 26              _lock = new(LockRecursionPolicy.NoRecursion);
 27          }
 28  
 29          public MemoryAllocation AllocateDeviceMemory(
 30              MemoryRequirements requirements,
 31              MemoryPropertyFlags flags = 0,
 32              bool isBuffer = false)
 33          {
 34              int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags);
 35              if (memoryTypeIndex < 0)
 36              {
 37                  return default;
 38              }
 39  
 40              bool map = flags.HasFlag(MemoryPropertyFlags.HostVisibleBit);
 41              return Allocate(memoryTypeIndex, requirements.Size, requirements.Alignment, map, isBuffer);
 42          }
 43  
 44          private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
 45          {
 46              _lock.EnterReadLock();
 47  
 48              try
 49              {
 50                  for (int i = 0; i < _blockLists.Count; i++)
 51                  {
 52                      var bl = _blockLists[i];
 53                      if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
 54                      {
 55                          return bl.Allocate(size, alignment, map);
 56                      }
 57                  }
 58              }
 59              finally
 60              {
 61                  _lock.ExitReadLock();
 62              }
 63  
 64              _lock.EnterWriteLock();
 65  
 66              try
 67              {
 68                  var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
 69                  _blockLists.Add(newBl);
 70  
 71                  return newBl.Allocate(size, alignment, map);
 72              }
 73              finally
 74              {
 75                  _lock.ExitWriteLock();
 76              }
 77          }
 78  
 79          internal int FindSuitableMemoryTypeIndex(
 80              uint memoryTypeBits,
 81              MemoryPropertyFlags flags)
 82          {
 83              for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++)
 84              {
 85                  var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i];
 86  
 87                  if ((memoryTypeBits & (1 << i)) != 0)
 88                  {
 89                      if (type.PropertyFlags.HasFlag(flags))
 90                      {
 91                          return i;
 92                      }
 93                  }
 94              }
 95  
 96              return -1;
 97          }
 98  
 99          public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice)
100          {
101              for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++)
102              {
103                  if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
104                  {
105                      return false;
106                  }
107              }
108  
109              return true;
110          }
111  
112          public void Dispose()
113          {
114              for (int i = 0; i < _blockLists.Count; i++)
115              {
116                  _blockLists[i].Dispose();
117              }
118          }
119      }
120  }