diff --git a/src/Vulkan/VMASharp_OriginalSources/Allocation.cs b/src/Vulkan/VMASharp_OriginalSources/Allocation.cs
new file mode 100644
index 0000000000..f5171b33f3
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Allocation.cs
@@ -0,0 +1,317 @@
+#pragma warning disable CA1063
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Diagnostics;
+
+using Silk.NET.Vulkan;
+using Buffer = Silk.NET.Vulkan.Buffer;
+using System.Buffers;
+
+namespace VMASharp
+{
+ ///
+ /// The object containing details on a suballocation of Vulkan Memory
+ ///
+ public unsafe abstract class Allocation : IDisposable
+ {
+ internal VulkanMemoryAllocator Allocator { get; }
+
+ protected Vk VkApi => Allocator.VkApi;
+
+ protected long size;
+ protected long alignment;
+ private int lastUseFrameIndex;
+ protected int memoryTypeIndex;
+ protected int mapCount;
+ private bool LostOrDisposed = false;
+
+ ///
+ /// Size of this allocation, in bytes.
+ /// Value never changes, unless allocation is lost.
+ ///
+ public long Size
+ {
+ get
+ {
+ if (LostOrDisposed || lastUseFrameIndex == Helpers.FrameIndexLost)
+ {
+ return 0;
+ }
+
+ return size;
+ }
+ }
+
+ ///
+ /// Memory type index that this allocation is from. Value does not change.
+ ///
+ public int MemoryTypeIndex { get => memoryTypeIndex; }
+
+ ///
+ /// Handle to Vulkan memory object.
+ /// Same memory object can be shared by multiple allocations.
+ /// It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
+ /// If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
+ ///
+ public abstract DeviceMemory DeviceMemory { get; }
+
+ ///
+ /// Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
+ /// It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
+ ///
+ public abstract long Offset { get; internal set; }
+
+ internal abstract bool CanBecomeLost { get; }
+
+
+ internal bool IsPersistantMapped
+ {
+ get => this.mapCount < 0;
+ }
+
+ internal int LastUseFrameIndex
+ {
+ get
+ {
+ return this.lastUseFrameIndex;
+ }
+ }
+
+ internal long Alignment => this.alignment;
+
+ public object? UserData { get; set; }
+
+ internal Allocation(VulkanMemoryAllocator allocator, int currentFrameIndex)
+ {
+ this.Allocator = allocator;
+ this.lastUseFrameIndex = currentFrameIndex;
+ }
+
+ ///
+ /// If this allocation is mapped, returns a pointer to the mapped memory region. Returns Null otherwise.
+ ///
+ public abstract IntPtr MappedData { get; }
+
+ public void Dispose()
+ {
+ if (!this.LostOrDisposed)
+ {
+ this.Allocator.FreeMemory(this);
+ LostOrDisposed = true;
+ }
+ }
+
+ public Result BindBufferMemory(Buffer buffer)
+ {
+ Debug.Assert(this.Offset >= 0);
+
+ return this.Allocator.BindVulkanBuffer(buffer, this.DeviceMemory, this.Offset, null);
+ }
+
+ public unsafe Result BindBufferMemory(Buffer buffer, long allocationLocalOffset, IntPtr pNext)
+ {
+ return this.BindBufferMemory(buffer, allocationLocalOffset, (void*)pNext);
+ }
+
+ public unsafe Result BindBufferMemory(Buffer buffer, long allocationLocalOffset, void* pNext = null)
+ {
+ if ((ulong)allocationLocalOffset >= (ulong)this.Size)
+ {
+ throw new ArgumentOutOfRangeException(nameof(allocationLocalOffset));
+ }
+
+ return this.Allocator.BindVulkanBuffer(buffer, this.DeviceMemory, this.Offset + allocationLocalOffset, pNext);
+ }
+
+ public unsafe Result BindImageMemory(Image image)
+ {
+ return this.Allocator.BindVulkanImage(image, this.DeviceMemory, this.Offset, null);
+ }
+
+ public unsafe Result BindImageMemory(Image image, long allocationLocalOffset, IntPtr pNext)
+ {
+ return this.BindImageMemory(image, allocationLocalOffset, (void*)pNext);
+ }
+
+ public unsafe Result BindImageMemory(Image image, long allocationLocalOffset, void* pNext = null)
+ {
+ if ((ulong)allocationLocalOffset >= (ulong)this.Size)
+ {
+ throw new ArgumentOutOfRangeException(nameof(allocationLocalOffset));
+ }
+
+ return this.Allocator.BindVulkanImage(image, this.DeviceMemory, this.Offset + allocationLocalOffset, pNext);
+ }
+
+ internal bool MakeLost(int currentFrame, int frameInUseCount)
+ {
+ if (!this.CanBecomeLost)
+ {
+ throw new InvalidOperationException("Internal Exception, tried to make an allocation lost that cannot become lost.");
+ }
+
+ int localLastUseFrameIndex = this.lastUseFrameIndex;
+
+ while (true)
+ {
+ if (localLastUseFrameIndex == Helpers.FrameIndexLost)
+ {
+ Debug.Assert(false);
+ return false;
+ }
+ else if (localLastUseFrameIndex + frameInUseCount >= currentFrame)
+ {
+ return false;
+ }
+ else
+ {
+ var tmp = Interlocked.CompareExchange(ref this.lastUseFrameIndex, Helpers.FrameIndexLost, localLastUseFrameIndex);
+
+ if (tmp == localLastUseFrameIndex)
+ {
+ this.LostOrDisposed = true;
+ return true;
+ }
+
+ localLastUseFrameIndex = tmp;
+ }
+ }
+ }
+
+ public bool TouchAllocation()
+ {
+ if (this.LostOrDisposed)
+ {
+ return false;
+ }
+
+ int currFrameIndexLoc = this.Allocator.CurrentFrameIndex;
+ int lastUseFrameIndexLoc = this.lastUseFrameIndex;
+
+ if (this.CanBecomeLost)
+ {
+ while (true)
+ {
+ if (lastUseFrameIndexLoc == Helpers.FrameIndexLost)
+ {
+ return false;
+ }
+ else if (lastUseFrameIndexLoc == currFrameIndexLoc)
+ {
+ return true;
+ }
+
+ lastUseFrameIndexLoc = Interlocked.CompareExchange(ref this.lastUseFrameIndex, currFrameIndexLoc, lastUseFrameIndexLoc);
+ }
+ }
+ else
+ {
+ while (true)
+ {
+ Debug.Assert(lastUseFrameIndexLoc != Helpers.FrameIndexLost);
+
+ if (lastUseFrameIndexLoc == currFrameIndexLoc)
+ break;
+
+ lastUseFrameIndexLoc = Interlocked.CompareExchange(ref this.lastUseFrameIndex, currFrameIndexLoc, lastUseFrameIndexLoc);
+ }
+
+ return true;
+ }
+ }
+
+ ///
+ /// Flushes a specified region of memory
+ ///
+ /// Offset in this allocation
+ /// Size of region to flush
+ /// The result of the operation
+ public Result Flush(long offset, long size)
+ {
+ return Allocator.FlushOrInvalidateAllocation(this, offset, size, CacheOperation.Flush);
+ }
+
+ ///
+ /// Invalidates a specified region of memory
+ ///
+ /// Offset in this allocation
+ /// Size of region to Invalidate
+ /// The result of the operation
+ public Result Invalidate(long offset, long size)
+ {
+ return Allocator.FlushOrInvalidateAllocation(this, offset, size, CacheOperation.Invalidate);
+ }
+
+ public abstract IntPtr Map();
+
+ public abstract void Unmap();
+
+ public bool TryGetMemory(out Memory memory) where T: unmanaged
+ {
+ if (mapCount != 0)
+ {
+ int size = checked((int)this.Size);
+
+ if (size >= sizeof(T))
+ {
+ memory = new UnmanagedMemoryManager((byte*)MappedData, size / sizeof(T)).Memory;
+
+ return true;
+ }
+ }
+
+ memory = Memory.Empty;
+ return false;
+ }
+
+ public bool TryGetSpan(out Span span) where T: unmanaged
+ {
+ if (mapCount != 0)
+ {
+ int size = checked((int)this.Size);
+
+ if (size >= sizeof(T))
+ {
+ span = new Span((void*)MappedData, size / sizeof(T));
+
+ return true;
+ }
+ }
+
+ span = Span.Empty;
+ return false;
+ }
+
+ private unsafe sealed class UnmanagedMemoryManager : MemoryManager where T: unmanaged
+ {
+ private readonly T* Pointer;
+ private readonly int ElementCount;
+
+ public UnmanagedMemoryManager(void* ptr, int elemCount)
+ {
+ Pointer = (T*)ptr;
+ ElementCount = elemCount;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ }
+
+ public override Span GetSpan()
+ {
+ return new Span(Pointer, ElementCount);
+ }
+
+ public override MemoryHandle Pin(int elementIndex = 0)
+ {
+ return new MemoryHandle(Pointer + elementIndex);
+ }
+
+ public override void Unpin()
+ {
+ }
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/AllocatorEnums.cs b/src/Vulkan/VMASharp_OriginalSources/AllocatorEnums.cs
new file mode 100644
index 0000000000..644388f9e9
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/AllocatorEnums.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace VMASharp
+{
+ [Flags]
+ public enum AllocatorCreateFlags
+ {
+ ///
+ /// Tells the allocator to not use any internal locking, not currently respected
+ ///
+ ExternallySyncronized = 0x00000001,
+
+ //KhrDedicatedAllocation = 0x00000002,
+
+ //KhrBindMemory2 = 0x00000004,
+
+ ///
+ /// Enables usage of the VK_EXT_memory_budget extension.
+ ///
+ /// You may set this flag only if you found out that this device extension is supported,
+ /// enabled it on the device passed through ,
+ /// and you want it to be used internally by this library.
+ ///
+ ExtMemoryBudget = 0x00000008,
+
+ AMDDeviceCoherentMemory = 0x00000010,
+
+ BufferDeviceAddress = 0x00000020
+ }
+
+ public enum MemoryUsage
+ {
+ ///
+ /// No Intended memory usage specified
+ ///
+ Unknown = 0,
+
+ ///
+ /// Memory will be used on device only, so fast access from the device is preferred.
+ /// It usually means device-local GPU (video) memory.
+ /// No need to be mappable on host.
+ /// It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
+ ///
+ ///
+ /// Usage:
+ /// - Resources written and read by device, e.g. images used as attachments.
+ /// - Resources transferred from host once (immutable) or infrequently and read by
+ /// device multiple times, e.g. textures to be sampled, vertex buffers, uniform
+ /// (constant) buffers, and majority of other types of resources used on GPU.
+ ///
+ /// Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
+ /// In such case, you are free to map it.
+ /// You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
+ ///
+ GPU_Only,
+
+ ///
+ /// Memory will be mappable on host.
+ /// It usually means CPU (system) memory.
+ /// Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
+ /// CPU access is typically uncached. Writes may be write-combined.
+ /// Resources created in this pool may still be accessible to the device, but access to them can be slow.
+ /// It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
+ ///
+ ///
+ /// Usage: Staging copy of resources used as transfer source.
+ ///
+ CPU_Only,
+
+ ///
+ /// Memory that is mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
+ /// CPU access is typically uncached. Writes may be write-combined.
+ ///
+ ///
+ /// Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
+ ///
+ CPU_To_GPU,
+
+ ///
+ /// Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
+ /// It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
+ ///
+ ///
+ /// Usage:
+ /// - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
+ /// - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
+ ///
+ GPU_To_CPU,
+
+ ///
+ /// CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
+ ///
+ ///
+ /// Usage: Staging copy of resources moved from GPU memory to CPU memory as part
+ /// of custom paging/residency mechanism, to be moved back to GPU memory when needed.
+ ///
+ CPU_Copy,
+
+ ///
+ /// Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
+ /// Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
+ /// Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
+ ///
+ ///
+ /// Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
+ ///
+ GPU_LazilyAllocated
+ }
+
+ [Flags]
+ public enum AllocationCreateFlags
+ {
+ DedicatedMemory = 0x0001,
+ NeverAllocate = 0x0002,
+ Mapped = 0x0004,
+ CanBecomeLost = 0x0008,
+ CanMakeOtherLost = 0x0010,
+ UpperAddress = 0x0040,
+ DontBind = 0x0080,
+ WithinBudget = 0x0100
+ }
+
+ [Flags]
+ public enum AllocationStrategyFlags
+ {
+ BestFit = 0x1,
+ WorstFit = 0x2,
+ FirstFit = 0x4,
+ MinMemory = BestFit,
+ MinTime = FirstFit,
+ MinFragmentation = WorstFit
+ }
+
+ [Flags]
+ public enum PoolCreateFlags
+ {
+ IgnoreBufferImageGranularity = 0x0001,
+ LinearAlgorithm = 0x0010,
+ BuddyAlgorithm = 0x0020
+ }
+
+ public enum DefragmentationFlags
+ {
+ Incremental = 0x1
+ }
+
+ public enum SuballocationType
+ {
+ Free = 0,
+ Unknown,
+ Buffer,
+ Image_Unknown,
+ Image_Linear,
+ Image_Optimal
+ }
+
+ public enum AllocationRequestType
+ {
+ Normal,
+ UpperAddress,
+ EndOfList1,
+ EndOfList2
+ }
+
+ internal enum CacheOperation
+ {
+ Flush,
+ Invalidate
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/AllocatorStructs.cs b/src/Vulkan/VMASharp_OriginalSources/AllocatorStructs.cs
new file mode 100644
index 0000000000..a5a8fb6bca
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/AllocatorStructs.cs
@@ -0,0 +1,168 @@
+using System;
+using Silk.NET.Core;
+using Silk.NET.Vulkan;
+
+#pragma warning disable CA1815
+
+namespace VMASharp
+{
+ public struct VulkanMemoryAllocatorCreateInfo
+ {
+ ///
+ /// Flags for created allocator
+ ///
+ public AllocatorCreateFlags Flags;
+
+ public Version32 VulkanAPIVersion;
+
+ public Vk VulkanAPIObject;
+
+ public Instance Instance;
+
+ public PhysicalDevice PhysicalDevice;
+
+ public Device LogicalDevice;
+
+ public long PreferredLargeHeapBlockSize;
+
+ public long[]? HeapSizeLimits;
+
+ public int FrameInUseCount;
+
+ public VulkanMemoryAllocatorCreateInfo(Version32 vulkanApiVersion,
+ Vk vulkanApiObject,
+ Instance instance, PhysicalDevice physicalDevice, Device logicalDevice,
+ AllocatorCreateFlags flags = default,
+ long preferredLargeHeapBlockSize = 0,
+ long[]? heapSizeLimits = null,
+ int frameInUseCount = 0)
+ {
+ Flags = flags;
+ VulkanAPIVersion = vulkanApiVersion;
+ VulkanAPIObject = vulkanApiObject;
+ Instance = instance;
+ PhysicalDevice = physicalDevice;
+ LogicalDevice = logicalDevice;
+ PreferredLargeHeapBlockSize = preferredLargeHeapBlockSize;
+ HeapSizeLimits = heapSizeLimits;
+ FrameInUseCount = frameInUseCount;
+ }
+ }
+
+ public struct AllocationCreateInfo
+ {
+ public AllocationCreateFlags Flags;
+
+ public AllocationStrategyFlags Strategy;
+
+ public MemoryUsage Usage;
+
+ public MemoryPropertyFlags RequiredFlags;
+
+ public MemoryPropertyFlags PreferredFlags;
+
+ public uint MemoryTypeBits;
+
+ public VulkanMemoryPool? Pool;
+
+ public object? UserData;
+
+ public AllocationCreateInfo(AllocationCreateFlags flags = default,
+ AllocationStrategyFlags strategy = default,
+ MemoryUsage usage = default,
+ MemoryPropertyFlags requiredFlags = default,
+ MemoryPropertyFlags preferredFlags = default,
+ uint memoryTypeBits = 0,
+ VulkanMemoryPool? pool = null,
+ object? userData = null)
+ {
+ Flags = flags;
+ Strategy = strategy;
+ Usage = usage;
+ RequiredFlags = requiredFlags;
+ PreferredFlags = preferredFlags;
+ MemoryTypeBits = memoryTypeBits;
+ Pool = pool;
+ UserData = userData;
+ }
+ }
+
+ public struct AllocationPoolCreateInfo
+ {
+ ///
+ /// Memory type index to allocate from, non-optional
+ ///
+ public int MemoryTypeIndex;
+
+ public PoolCreateFlags Flags;
+
+ public long BlockSize;
+
+ public int MinBlockCount;
+
+ public int MaxBlockCount;
+
+ public int FrameInUseCount;
+
+ public Func? AllocationAlgorithmCreate;
+
+ public AllocationPoolCreateInfo(int memoryTypeIndex,
+ PoolCreateFlags flags = 0,
+ long blockSize = 0,
+ int minBlockCount = 0,
+ int maxBlockCount = 0,
+ int frameInUseCount = 0,
+ Func? allocationAlgorithemCreate = null)
+ {
+ MemoryTypeIndex = memoryTypeIndex;
+ Flags = flags;
+ BlockSize = blockSize;
+ MinBlockCount = minBlockCount;
+ MaxBlockCount = maxBlockCount;
+ FrameInUseCount = frameInUseCount;
+ AllocationAlgorithmCreate = allocationAlgorithemCreate;
+ }
+ }
+
+ public struct AllocationContext
+ {
+ public int CurrentFrame, FrameInUseCount;
+ public long BufferImageGranularity;
+ public long AllocationSize;
+ public long AllocationAlignment;
+ public AllocationStrategyFlags Strategy;
+ public SuballocationType SuballocationType;
+ public bool CanMakeOtherLost;
+
+ public AllocationContext(int currentFrame, int framesInUse, long bufferImageGranularity, long allocationSize, long allocationAlignment, AllocationStrategyFlags strategy, SuballocationType suballocType, bool canMakeOtherLost)
+ {
+ CurrentFrame = currentFrame;
+ FrameInUseCount = framesInUse;
+ BufferImageGranularity = bufferImageGranularity;
+ AllocationSize = allocationSize;
+ AllocationAlignment = allocationAlignment;
+ Strategy = strategy;
+ SuballocationType = suballocType;
+ CanMakeOtherLost = canMakeOtherLost;
+ }
+ }
+
+ public struct AllocationRequest
+ {
+ public const long LostAllocationCost = 1048576;
+
+ public long Offset, SumFreeSize, SumItemSize;
+ public long ItemsToMakeLostCount;
+
+ public object Item;
+
+ public object CustomData;
+
+ public AllocationRequestType Type;
+
+ public readonly long CalcCost()
+ {
+ return SumItemSize + ItemsToMakeLostCount * LostAllocationCost;
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/BlockAllocation.cs b/src/Vulkan/VMASharp_OriginalSources/BlockAllocation.cs
new file mode 100644
index 0000000000..576cb06a33
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/BlockAllocation.cs
@@ -0,0 +1,133 @@
+using Silk.NET.Vulkan;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+using VMASharp;
+
+namespace VMASharp
+{
+ public sealed class BlockAllocation : Allocation
+ {
+ internal VulkanMemoryBlock Block;
+ internal long offset;
+ internal SuballocationType suballocationType;
+ internal bool canBecomeLost;
+
+ internal BlockAllocation(VulkanMemoryAllocator allocator, int currentFrameIndex) : base(allocator, currentFrameIndex)
+ {
+ }
+
+ public override DeviceMemory DeviceMemory
+ {
+ get => this.Block.DeviceMemory;
+ }
+
+ public override long Offset
+ {
+ get => this.offset;
+ internal set => this.offset = value;
+ }
+
+ public override IntPtr MappedData
+ {
+ get
+ {
+ if (this.mapCount != 0)
+ {
+ IntPtr mapdata = this.Block.MappedData;
+
+ Debug.Assert(mapdata != default);
+
+ return new IntPtr(mapdata.ToInt64() + this.offset);
+ }
+ else
+ {
+ return default;
+ }
+ }
+ }
+
+ internal override bool CanBecomeLost => this.canBecomeLost;
+
+ internal void InitBlockAllocation(VulkanMemoryBlock block, long offset, long alignment, long size, int memoryTypeIndex, SuballocationType subType, bool mapped, bool canBecomeLost)
+ {
+ this.Block = block;
+ this.offset = offset;
+ this.alignment = alignment;
+ this.size = size;
+ this.memoryTypeIndex = memoryTypeIndex;
+ this.mapCount = mapped ? int.MinValue : 0;
+ this.suballocationType = subType;
+ this.canBecomeLost = canBecomeLost;
+ }
+
+ internal void ChangeAllocation(VulkanMemoryBlock block, long offset)
+ {
+ Debug.Assert(block != null && offset >= 0);
+
+ if (!object.ReferenceEquals(block, this.Block))
+ {
+ int mapRefCount = this.mapCount & int.MaxValue;
+
+ if (this.IsPersistantMapped)
+ {
+ mapRefCount += 1;
+ }
+
+ this.Block.Unmap(mapRefCount);
+ block.Map(mapRefCount);
+
+ this.Block = block;
+ }
+
+ this.Offset = offset;
+ }
+
+ private void BlockAllocMap()
+ {
+ if ((this.mapCount & int.MaxValue) < int.MaxValue)
+ {
+ this.mapCount += 1;
+ }
+ else
+ {
+ throw new InvalidOperationException("Allocation mapped too many times simultaniously");
+ }
+ }
+
+ private void BlockAllocUnmap()
+ {
+ if ((this.mapCount & int.MaxValue) > 0)
+ {
+ this.mapCount -= 1;
+ }
+ else
+ {
+ throw new InvalidOperationException("Unmapping allocation not previously mapped");
+ }
+ }
+
+ public override IntPtr Map()
+ {
+ if (this.CanBecomeLost)
+ {
+ throw new InvalidOperationException("Cannot map an allocation that can become lost");
+ }
+
+ var data = this.Block.Map(1);
+
+ data = new IntPtr(data.ToInt64() + this.Offset);
+
+ this.BlockAllocMap();
+
+ return data;
+ }
+
+ public override void Unmap()
+ {
+ this.BlockAllocUnmap();
+ this.Block.Unmap(1);
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/BlockList.cs b/src/Vulkan/VMASharp_OriginalSources/BlockList.cs
new file mode 100644
index 0000000000..b2085dc20f
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/BlockList.cs
@@ -0,0 +1,775 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Linq;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp
+{
+ using Metadata;
+
+ internal class BlockList : IDisposable
+ {
+ private const int AllocationTryCount = 32;
+
+ private readonly List blocks = new List();
+ private readonly ReaderWriterLockSlim mutex = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
+
+ private readonly int minBlockCount, maxBlockCount;
+ private readonly bool explicitBlockSize;
+
+ private readonly Func metaObjectCreate;
+
+ private bool hasEmptyBlock;
+ private uint nextBlockID;
+
+ public BlockList(VulkanMemoryAllocator allocator, VulkanMemoryPool? pool, int memoryTypeIndex,
+ long preferredBlockSize, int minBlockCount, int maxBlockCount, long bufferImageGranularity,
+ int frameInUseCount, bool explicitBlockSize, Func algorithm)
+ {
+ this.Allocator = allocator;
+ this.ParentPool = pool;
+ this.MemoryTypeIndex = memoryTypeIndex;
+ this.PreferredBlockSize = preferredBlockSize;
+ this.minBlockCount = minBlockCount;
+ this.maxBlockCount = maxBlockCount;
+ this.BufferImageGranularity = bufferImageGranularity;
+ this.FrameInUseCount = frameInUseCount;
+ this.explicitBlockSize = explicitBlockSize;
+
+ metaObjectCreate = algorithm;
+ }
+
+ public void Dispose()
+ {
+ foreach (var block in this.blocks)
+ {
+ block.Dispose();
+ }
+ }
+
+ public VulkanMemoryAllocator Allocator { get; }
+
+ public VulkanMemoryPool? ParentPool { get; }
+
+ public bool IsCustomPool { get => this.ParentPool != null; }
+
+ public int MemoryTypeIndex { get; }
+
+ public long PreferredBlockSize { get; }
+
+ public long BufferImageGranularity { get; }
+
+ public int FrameInUseCount { get; }
+
+ public bool IsEmpty
+ {
+ get
+ {
+ this.mutex.EnterReadLock();
+
+ try
+ {
+ return this.blocks.Count == 0;
+ }
+ finally
+ {
+ this.mutex.ExitReadLock();
+ }
+ }
+ }
+
+ public bool IsCorruptedDetectionEnabled { get => false; }
+
+ public int BlockCount { get => blocks.Count; }
+
+ public VulkanMemoryBlock this[int index]
+ {
+ get => blocks[index];
+ }
+
+ private IEnumerable BlocksInReverse //Just gonna take advantage of C#...
+ {
+ get
+ {
+ List localList = this.blocks;
+
+ for (int index = localList.Count - 1; index >= 0; --index)
+ {
+ yield return localList[index];
+ }
+ }
+ }
+
+ public void CreateMinBlocks()
+ {
+ if (this.blocks.Count > 0)
+ {
+ throw new InvalidOperationException("Block list not empty");
+ }
+
+ for (int i = 0; i < this.minBlockCount; ++i)
+ {
+ var res = this.CreateBlock(this.PreferredBlockSize, out _);
+
+ if (res != Result.Success)
+ {
+ throw new AllocationException("Unable to allocate device memory block", res);
+ }
+ }
+ }
+
+ public void GetPoolStats(out PoolStats stats)
+ {
+ this.mutex.EnterReadLock();
+
+ try
+ {
+ stats = new PoolStats();
+ stats.BlockCount = this.blocks.Count;
+
+ foreach (var block in this.blocks)
+ {
+ Debug.Assert(block != null);
+
+ block.Validate();
+
+ block.MetaData.AddPoolStats(ref stats);
+ }
+ }
+ finally
+ {
+ this.mutex.ExitReadLock();
+ }
+ }
+
+ public Allocation Allocate(int currentFrame, long size, long alignment, in AllocationCreateInfo allocInfo, SuballocationType suballocType)
+ {
+ this.mutex.EnterWriteLock();
+
+ try
+ {
+ return this.AllocatePage(currentFrame, size, alignment, allocInfo, suballocType);
+ }
+ finally
+ {
+ this.mutex.ExitWriteLock();
+ }
+ }
+
+ public void Free(Allocation allocation)
+ {
+ VulkanMemoryBlock? blockToDelete = null;
+
+ bool budgetExceeded = false;
+ {
+ int heapIndex = this.Allocator.MemoryTypeIndexToHeapIndex(this.MemoryTypeIndex);
+ this.Allocator.GetBudget(heapIndex, out var budget);
+ budgetExceeded = budget.Usage >= budget.Budget;
+ }
+
+ this.mutex.EnterWriteLock();
+
+ try
+ {
+ var blockAlloc = (BlockAllocation)allocation;
+
+ VulkanMemoryBlock block = blockAlloc.Block;
+
+ //Corruption Detection TODO
+
+ if (allocation.IsPersistantMapped)
+ {
+ block.Unmap(1);
+ }
+
+ block.MetaData.Free(blockAlloc);
+
+ block.Validate();
+
+ bool canDeleteBlock = this.blocks.Count > this.minBlockCount;
+
+ if (block.MetaData.IsEmpty)
+ {
+ if ((this.hasEmptyBlock || budgetExceeded) && canDeleteBlock)
+ {
+ blockToDelete = block;
+ this.Remove(block);
+ }
+ }
+ else if (this.hasEmptyBlock && canDeleteBlock)
+ {
+ block = this.blocks[^1];
+
+ if (block.MetaData.IsEmpty)
+ {
+ blockToDelete = block;
+ this.blocks.RemoveAt(this.blocks.Count - 1);
+ }
+ }
+
+ this.UpdateHasEmptyBlock();
+ this.IncrementallySortBlocks();
+ }
+ finally
+ {
+ this.mutex.ExitWriteLock();
+ }
+
+ if (blockToDelete != null)
+ {
+ blockToDelete.Dispose();
+ }
+ }
+
+ public void AddStats(Stats stats)
+ {
+ var memTypeIndex = this.MemoryTypeIndex;
+ var memHeapIndex = this.Allocator.MemoryTypeIndexToHeapIndex(memTypeIndex);
+
+ this.mutex.EnterReadLock();
+
+ try
+ {
+ foreach (var block in this.blocks)
+ {
+ Debug.Assert(block != null);
+ block.Validate();
+
+ block.MetaData.CalcAllocationStatInfo(out var info);
+ StatInfo.Add(ref stats.Total, info);
+ StatInfo.Add(ref stats.MemoryType[memTypeIndex], info);
+ StatInfo.Add(ref stats.MemoryHeap[memHeapIndex], info);
+ }
+ }
+ finally
+ {
+ this.mutex.ExitReadLock();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Lost Allocation Count
+ ///
+ public int MakePoolAllocationsLost(int currentFrame)
+ {
+ this.mutex.EnterWriteLock();
+
+ try
+ {
+ int lostAllocationCount = 0;
+
+ foreach (var block in this.blocks)
+ {
+ Debug.Assert(block != null);
+
+ lostAllocationCount += block.MetaData.MakeAllocationsLost(currentFrame, this.FrameInUseCount);
+ }
+
+ return lostAllocationCount;
+ }
+ finally
+ {
+ this.mutex.ExitWriteLock();
+ }
+ }
+
+ public Result CheckCorruption()
+ {
+ throw new NotImplementedException();
+ }
+
+ public int CalcAllocationCount()
+ {
+ int res = 0;
+
+ foreach (var block in blocks)
+ {
+ res += block.MetaData.AllocationCount;
+ }
+
+ return res;
+ }
+
+ public bool IsBufferImageGranularityConflictPossible()
+ {
+ if (BufferImageGranularity == 1)
+ return false;
+
+ SuballocationType lastSuballocType = SuballocationType.Free;
+
+ foreach (var block in blocks)
+ {
+ var metadata = block.MetaData as BlockMetadata_Generic;
+ Debug.Assert(metadata != null);
+
+ if (metadata.IsBufferImageGranularityConflictPossible(this.BufferImageGranularity, ref lastSuballocType))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private long CalcMaxBlockSize()
+ {
+ long result = 0;
+
+ for (int i = this.blocks.Count - 1; i >= 0; --i)
+ {
+ var blockSize = this.blocks[i].MetaData.Size;
+
+ if (result < blockSize)
+ {
+ result = blockSize;
+ }
+
+ if (result >= this.PreferredBlockSize)
+ {
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ [SkipLocalsInit]
+ private Allocation AllocatePage(int currentFrame, long size, long alignment, in AllocationCreateInfo createInfo, SuballocationType suballocType)
+ {
+ bool canMakeOtherLost = (createInfo.Flags & AllocationCreateFlags.CanMakeOtherLost) != 0;
+ bool mapped = (createInfo.Flags & AllocationCreateFlags.Mapped) != 0;
+
+ long freeMemory;
+
+ {
+ int heapIndex = this.Allocator.MemoryTypeIndexToHeapIndex(this.MemoryTypeIndex);
+
+ this.Allocator.GetBudget(heapIndex, out AllocationBudget heapBudget);
+
+ freeMemory = (heapBudget.Usage < heapBudget.Budget) ? (heapBudget.Budget - heapBudget.Usage) : 0;
+ }
+
+ bool canFallbackToDedicated = !this.IsCustomPool;
+ bool canCreateNewBlock = ((createInfo.Flags & AllocationCreateFlags.NeverAllocate) == 0) && (this.blocks.Count < this.maxBlockCount) && (freeMemory >= size || !canFallbackToDedicated);
+
+ var strategy = createInfo.Strategy;
+
+ //if (this.algorithm == (uint)PoolCreateFlags.LinearAlgorithm && this.maxBlockCount > 1)
+ //{
+ // canMakeOtherLost = false;
+ //}
+
+ //if (isUpperAddress && (this.algorithm != (uint)PoolCreateFlags.LinearAlgorithm || this.maxBlockCount > 1))
+ //{
+ // throw new AllocationException("Upper address allocation unavailable", Result.ErrorFeatureNotPresent);
+ //}
+
+ switch (strategy)
+ {
+ case 0:
+ strategy = AllocationStrategyFlags.BestFit;
+ break;
+ case AllocationStrategyFlags.BestFit:
+ case AllocationStrategyFlags.WorstFit:
+ case AllocationStrategyFlags.FirstFit:
+ break;
+ default:
+ throw new AllocationException("Invalid allocation strategy", Result.ErrorFeatureNotPresent);
+ }
+
+ if (size + 2 * Helpers.DebugMargin > this.PreferredBlockSize)
+ {
+ throw new AllocationException("Allocation size larger than block size", Result.ErrorOutOfDeviceMemory);
+ }
+
+ AllocationContext context = new AllocationContext(
+ currentFrame,
+ this.FrameInUseCount,
+ this.BufferImageGranularity,
+ size,
+ alignment,
+ strategy,
+ suballocType,
+ canMakeOtherLost);
+
+ Allocation? alloc;
+
+ if (!canMakeOtherLost || canCreateNewBlock)
+ {
+ AllocationCreateFlags allocFlagsCopy = createInfo.Flags & ~AllocationCreateFlags.CanMakeOtherLost;
+
+ if (strategy == AllocationStrategyFlags.BestFit)
+ {
+ foreach (var block in this.blocks)
+ {
+ alloc = this.AllocateFromBlock(block, in context, allocFlagsCopy, createInfo.UserData);
+
+ if (alloc != null)
+ {
+ //Possibly Log here
+ return alloc;
+ }
+ }
+ }
+ else
+ {
+ foreach (var curBlock in this.BlocksInReverse)
+ {
+ alloc = this.AllocateFromBlock(curBlock, in context, allocFlagsCopy, createInfo.UserData);
+
+ if (alloc != null)
+ {
+ //Possibly Log here
+ return alloc;
+ }
+ }
+ }
+ }
+
+ if (canCreateNewBlock)
+ {
+ AllocationCreateFlags allocFlagsCopy = createInfo.Flags & ~AllocationCreateFlags.CanMakeOtherLost;
+
+ long newBlockSize = this.PreferredBlockSize;
+ int newBlockSizeShift = 0;
+ const int NewBlockSizeShiftMax = 3;
+
+ if (!this.explicitBlockSize)
+ {
+ long maxExistingBlockSize = this.CalcMaxBlockSize();
+
+ for (int i = 0; i < NewBlockSizeShiftMax; ++i)
+ {
+ long smallerNewBlockSize = newBlockSize / 2;
+ if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
+ {
+ newBlockSize = smallerNewBlockSize;
+ newBlockSizeShift += 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ int newBlockIndex = 0;
+
+ var res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? this.CreateBlock(newBlockSize, out newBlockIndex) : Result.ErrorOutOfDeviceMemory;
+
+ if (!this.explicitBlockSize)
+ {
+ while (res < 0 && newBlockSizeShift < NewBlockSizeShiftMax)
+ {
+ long smallerNewBlockSize = newBlockSize / 2;
+
+ if (smallerNewBlockSize >= size)
+ {
+ newBlockSize = smallerNewBlockSize;
+ newBlockSizeShift += 1;
+ res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? this.CreateBlock(newBlockSize, out newBlockIndex) : Result.ErrorOutOfDeviceMemory;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if (res == Result.Success)
+ {
+ var block = this.blocks[newBlockIndex];
+
+ alloc = this.AllocateFromBlock(block, in context, allocFlagsCopy, createInfo.UserData);
+
+ if (alloc != null)
+ {
+ //Possibly Log here
+ return alloc;
+ }
+ }
+ }
+
+ if (canMakeOtherLost)
+ {
+ int tryIndex = 0;
+
+ for (; tryIndex < AllocationTryCount; ++tryIndex)
+ {
+ VulkanMemoryBlock? bestRequestBlock = null;
+
+ Unsafe.SkipInit(out AllocationRequest bestAllocRequest);
+
+ long bestRequestCost = long.MaxValue;
+
+ if (strategy == AllocationStrategyFlags.BestFit)
+ {
+ foreach (var curBlock in this.blocks)
+ {
+ if (curBlock.MetaData.TryCreateAllocationRequest(in context, out var request))
+ {
+ long currRequestCost = request.CalcCost();
+
+ if (bestRequestBlock == null || currRequestCost < bestRequestCost)
+ {
+ bestRequestBlock = curBlock;
+ bestAllocRequest = request;
+ bestRequestCost = currRequestCost;
+
+ if (bestRequestCost == 0)
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ foreach (var curBlock in this.BlocksInReverse)
+ {
+ if (curBlock.MetaData.TryCreateAllocationRequest(in context, out var request))
+ {
+ long curRequestCost = request.CalcCost();
+
+ if (bestRequestBlock == null || curRequestCost < bestRequestCost || strategy == AllocationStrategyFlags.FirstFit)
+ {
+ bestRequestBlock = curBlock;
+ bestRequestCost = curRequestCost;
+ bestAllocRequest = request;
+
+ if (bestRequestCost == 0 || strategy == AllocationStrategyFlags.FirstFit)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (bestRequestBlock != null)
+ {
+ if (mapped)
+ {
+ bestRequestBlock.Map(1);
+ }
+
+ if (bestRequestBlock.MetaData.MakeRequestedAllocationsLost(currentFrame, this.FrameInUseCount, ref bestAllocRequest))
+ {
+ var talloc = new BlockAllocation(this.Allocator, this.Allocator.CurrentFrameIndex);
+
+ bestRequestBlock.MetaData.Alloc(in bestAllocRequest, suballocType, size, talloc);
+
+ this.UpdateHasEmptyBlock();
+
+ //(allocation as BlockAllocation).InitBlockAllocation();
+
+ try
+ {
+ bestRequestBlock.Validate(); //Won't be called in release builds
+ }
+ catch
+ {
+ talloc.Dispose();
+ throw;
+ }
+
+ talloc.UserData = createInfo.UserData;
+
+ this.Allocator.Budget.AddAllocation(this.Allocator.MemoryTypeIndexToHeapIndex(this.MemoryTypeIndex), size);
+
+ //Maybe put memory init and corruption detection here
+
+ return talloc;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (tryIndex == AllocationTryCount)
+ {
+ throw new AllocationException("", Result.ErrorTooManyObjects);
+ }
+ }
+
+ throw new AllocationException("Unable to allocate memory");
+ }
+
+ private Allocation? AllocateFromBlock(VulkanMemoryBlock block, in AllocationContext context, AllocationCreateFlags flags, object? userData)
+ {
+ Debug.Assert((flags & AllocationCreateFlags.CanMakeOtherLost) == 0);
+ bool mapped = (flags & AllocationCreateFlags.Mapped) != 0;
+
+ if (block.MetaData.TryCreateAllocationRequest(in context, out var request))
+ {
+ Debug.Assert(request.ItemsToMakeLostCount == 0);
+
+ if (mapped)
+ {
+ block.Map(1);
+ }
+
+ var allocation = new BlockAllocation(this.Allocator, this.Allocator.CurrentFrameIndex);
+
+ block.MetaData.Alloc(in request, context.SuballocationType, context.AllocationSize, allocation);
+
+ allocation.InitBlockAllocation(block, request.Offset, context.AllocationAlignment, context.AllocationSize, this.MemoryTypeIndex,
+ context.SuballocationType, mapped, (flags & AllocationCreateFlags.CanBecomeLost) != 0);
+
+ this.UpdateHasEmptyBlock();
+
+ block.Validate();
+
+ allocation.UserData = userData;
+
+ this.Allocator.Budget.AddAllocation(this.Allocator.MemoryTypeIndexToHeapIndex(this.MemoryTypeIndex), context.AllocationSize);
+
+ return allocation;
+ }
+
+ return null;
+ }
+
+ private unsafe Result CreateBlock(long blockSize, out int newBlockIndex)
+ {
+ newBlockIndex = -1;
+
+ MemoryAllocateInfo info = new MemoryAllocateInfo
+ {
+ SType = StructureType.MemoryAllocateInfo,
+ MemoryTypeIndex = (uint)this.MemoryTypeIndex,
+ AllocationSize = (ulong)blockSize
+ };
+
+ // Every standalone block can potentially contain a buffer with BufferUsageFlags.BufferUsageShaderDeviceAddressBitKhr - always enable the feature
+ MemoryAllocateFlagsInfoKHR allocFlagsInfo = new MemoryAllocateFlagsInfoKHR(StructureType.MemoryAllocateFlagsInfoKhr);
+ if (Allocator.UseKhrBufferDeviceAddress)
+ {
+ allocFlagsInfo.Flags = MemoryAllocateFlags.MemoryAllocateDeviceAddressBitKhr;
+ info.PNext = &allocFlagsInfo;
+ }
+
+ var res = this.Allocator.AllocateVulkanMemory(in info, out DeviceMemory mem);
+
+ if (res < 0)
+ {
+ return res;
+ }
+
+ var metaObject = this.metaObjectCreate(blockSize);
+
+ if (metaObject.Size != blockSize)
+ {
+ throw new InvalidOperationException("Returned Metadata object reports incorrect block size");
+ }
+
+ var block = new VulkanMemoryBlock(this.Allocator, this.ParentPool, this.MemoryTypeIndex, mem, this.nextBlockID++, metaObject);
+
+ this.blocks.Add(block);
+
+ newBlockIndex = this.blocks.Count - 1;
+
+ return Result.Success;
+ }
+
+ private void FreeEmptyBlocks(ref Defragmentation.DefragmentationStats stats)
+ {
+ for (int i = this.blocks.Count - 1; i >= 0; --i)
+ {
+ var block = this.blocks[i];
+
+ if (block.MetaData.IsEmpty)
+ {
+ if (this.blocks.Count > this.minBlockCount)
+ {
+ stats.DeviceMemoryBlocksFreed += 1;
+ stats.BytesFreed += block.MetaData.Size;
+
+ this.blocks.RemoveAt(i);
+ block.Dispose();
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ this.UpdateHasEmptyBlock();
+ }
+
+ private void UpdateHasEmptyBlock()
+ {
+ this.hasEmptyBlock = false;
+
+ foreach (var block in blocks)
+ {
+ if (block.MetaData.IsEmpty)
+ {
+ this.hasEmptyBlock = true;
+ break;
+ }
+ }
+ }
+
+ private void Remove(VulkanMemoryBlock block)
+ {
+ var res = blocks.Remove(block);
+ Debug.Assert(res, "");
+ }
+
+ private void IncrementallySortBlocks()
+ {
+ if ((uint)this.blocks.Count > 1)
+ {
+ var prevBlock = this.blocks[0];
+ int i = 1;
+
+ do
+ {
+ var curBlock = this.blocks[i];
+
+ if (prevBlock.MetaData.SumFreeSize > curBlock.MetaData.SumFreeSize)
+ {
+ this.blocks[i - 1] = curBlock;
+ this.blocks[i] = prevBlock;
+ return;
+ }
+
+ prevBlock = curBlock;
+ i += 1;
+ }
+ while (i < this.blocks.Count);
+ }
+ }
+
+ public class DefragmentationContext
+ {
+ private readonly BlockList List;
+
+ public DefragmentationContext(BlockList list)
+ {
+ this.List = list;
+ }
+
+ //public void Defragment(DefragmentationStats stats, DefragmentationFlags flags, ulong maxCpuBytesToMove, )
+
+ //public void End(DefragmentationStats stats)
+
+ //public uint ProcessDefragmentations(DefragmentationPassMoveInfo move, uint maxMoves)
+
+ //public void CommitDefragmentations(DefragmentationStats stats)
+ }
+
+
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Budget.cs b/src/Vulkan/VMASharp_OriginalSources/Budget.cs
new file mode 100644
index 0000000000..088d214e2a
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Budget.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Diagnostics;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp
+{
+ public struct AllocationBudget
+ {
+ public long BlockBytes;
+ public long AllocationBytes;
+ public long Usage;
+ public long Budget;
+
+ public AllocationBudget(long blockBytes, long allocationBytes, long usage, long budget)
+ {
+ BlockBytes = blockBytes;
+ AllocationBytes = allocationBytes;
+ Usage = usage;
+ Budget = budget;
+ }
+ }
+
+ internal class CurrentBudgetData
+ {
+ public readonly InternalBudgetStruct[] BudgetData = new InternalBudgetStruct[Vk.MaxMemoryHeaps];
+ public readonly ReaderWriterLockSlim BudgetMutex = new ReaderWriterLockSlim();
+ public int OperationsSinceBudgetFetch;
+
+ public CurrentBudgetData()
+ {
+ }
+
+ public void AddAllocation(int heapIndex, long allocationSize)
+ {
+ if ((uint)heapIndex >= Vk.MaxMemoryHeaps)
+ {
+ throw new ArgumentOutOfRangeException(nameof(heapIndex));
+ }
+
+ Interlocked.Add(ref this.BudgetData[heapIndex].AllocationBytes, allocationSize);
+ Interlocked.Increment(ref this.OperationsSinceBudgetFetch);
+ }
+
+ public void RemoveAllocation(int heapIndex, long allocationSize)
+ {
+ ref InternalBudgetStruct heap = ref BudgetData[heapIndex];
+
+ Debug.Assert(heap.AllocationBytes >= allocationSize);
+
+ Interlocked.Add(ref heap.AllocationBytes, -allocationSize); //Subtraction
+
+ Interlocked.Increment(ref this.OperationsSinceBudgetFetch);
+ }
+
+ internal struct InternalBudgetStruct
+ {
+ public long BlockBytes;
+ public long AllocationBytes;
+ public long VulkanUsage;
+ public long VulkanBudget;
+ public long BlockBytesAtBudgetFetch;
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/DedicatedAllocation.cs b/src/Vulkan/VMASharp_OriginalSources/DedicatedAllocation.cs
new file mode 100644
index 0000000000..0b32cdb22c
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/DedicatedAllocation.cs
@@ -0,0 +1,110 @@
+using Silk.NET.Vulkan;
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Text;
+using VMASharp;
+
+namespace VMASharp
+{
+ internal class DedicatedAllocation : Allocation
+ {
+ internal DeviceMemory memory;
+ internal IntPtr mappedData;
+
+ public DedicatedAllocation(VulkanMemoryAllocator allocator, int memTypeIndex, DeviceMemory memory, SuballocationType suballocType, IntPtr mappedData, long size) : base(allocator, 0)
+ {
+ this.memory = memory;
+ this.mappedData = mappedData;
+ this.memoryTypeIndex = memTypeIndex;
+ }
+
+ public override DeviceMemory DeviceMemory => this.memory;
+
+ public override long Offset { get => 0; internal set => throw new InvalidOperationException(); }
+
+ public override IntPtr MappedData => this.mapCount != 0 ? this.mappedData : default;
+
+ internal override bool CanBecomeLost => false;
+
+ internal unsafe Result DedicatedAllocMap(out IntPtr pData)
+ {
+ if (this.mapCount != 0)
+ {
+ if ((this.mapCount & int.MaxValue) < int.MaxValue)
+ {
+ Debug.Assert(this.mappedData != default);
+
+ pData = this.mappedData;
+ this.mapCount += 1;
+
+ return Result.Success;
+ }
+ else
+ {
+ throw new InvalidOperationException("Dedicated allocation mapped too many times simultaneously");
+ }
+ }
+ else
+ {
+ pData = default;
+
+ IntPtr tmp;
+ var res = VkApi.MapMemory(this.Allocator.Device, this.memory, 0, Vk.WholeSize, 0, (void**)&tmp);
+
+ if (res == Result.Success)
+ {
+ this.mappedData = tmp;
+ this.mapCount = 1;
+ pData = tmp;
+ }
+
+ return res;
+ }
+ }
+
+ internal void DedicatedAllocUnmap()
+ {
+ if ((this.mapCount & int.MaxValue) != 0)
+ {
+ this.mapCount -= 1;
+
+ if (this.mapCount == 0)
+ {
+ this.mappedData = default;
+ VkApi.UnmapMemory(this.Allocator.Device, this.memory);
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException("Unmapping dedicated allocation not previously mapped");
+ }
+ }
+
+ public void CalcStatsInfo(out StatInfo stats)
+ {
+ StatInfo.Init(out stats);
+ stats.BlockCount = 1;
+ stats.AllocationCount = 1;
+ stats.UsedBytes = this.Size;
+ stats.AllocationSizeMin = stats.AllocationSizeMax = this.Size;
+ }
+
+ public override IntPtr Map()
+ {
+ var res = DedicatedAllocMap(out var pData);
+
+ if (res != Result.Success)
+ {
+ throw new MapMemoryException(res);
+ }
+
+ return pData;
+ }
+
+ public override void Unmap()
+ {
+ DedicatedAllocUnmap();
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/BlockListDefragmentationContext.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/BlockListDefragmentationContext.cs
new file mode 100644
index 0000000000..319a4a5bfd
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/BlockListDefragmentationContext.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp.Defragmentation
+{
+ internal class BlockListDefragmentationContext
+ {
+ public Result Result;
+ public bool MutexLocked;
+
+ public readonly List blockContexts = new List();
+ public readonly List DefragMoves = new List();
+
+ public int DefragMovesProcessed, DefragMovedCommitted;
+ public bool HasDefragmentationPlanned;
+
+
+ public BlockListDefragmentationContext(VulkanMemoryAllocator allocator, VulkanMemoryPool? customPool, BlockList list, uint currentFrame)
+ {
+
+ }
+
+ public VulkanMemoryPool? CustomPool { get; }
+
+ public BlockList BlockList { get; }
+
+ public DefragmentationAlgorithm Algorithm { get; }
+
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationAlgorithm.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationAlgorithm.cs
new file mode 100644
index 0000000000..3083499861
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationAlgorithm.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp.Defragmentation
+{
+ internal abstract class DefragmentationAlgorithm : IDisposable
+ {
+ protected readonly VulkanMemoryAllocator Allocator;
+ protected readonly BlockList BlockList;
+ protected readonly uint CurrentFrame;
+
+ protected DefragmentationAlgorithm(VulkanMemoryAllocator allocator, BlockList list, uint currentFrame)
+ {
+ this.Allocator = allocator;
+ this.BlockList = list;
+ this.CurrentFrame = currentFrame;
+ }
+
+ public abstract ulong BytesMoved { get; }
+
+ public abstract int AllocationsMoved { get; }
+
+ public virtual void Dispose()
+ {
+ }
+
+ public abstract void AddAllocation(Allocation alloc, out bool changed);
+
+ public abstract void AddAll();
+
+ public abstract Result Defragment(ulong maxBytesToMove, int maxAllocationsToMove, DefragmentationFlags flags, out DefragmentationMove[] moves);
+
+ protected class AllocateInfo
+ {
+ public Allocation Allocation;
+ public bool Changed;
+
+ public AllocateInfo()
+ { }
+
+ public AllocateInfo(Allocation allocation)
+ {
+ this.Allocation = allocation;
+ }
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationContext.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationContext.cs
new file mode 100644
index 0000000000..bd5fd511f0
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/DefragmentationContext.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Silk.NET.Vulkan;
+using VMASharp;
+
+namespace VMASharp.Defragmentation
+{
+ public sealed class DefragmentationContext : IDisposable
+ {
+ private readonly VulkanMemoryAllocator Allocator;
+ private readonly uint currentFrame;
+ private readonly uint Flags;
+ private DefragmentationStats Stats;
+
+ private ulong MaxCPUBytesToMove, MaxGPUBytesToMove;
+ private int MaxCPUAllocationsToMove, MaxGPUAllocationsToMove;
+
+ private readonly BlockListDefragmentationContext[] DefaultPoolContexts = new BlockListDefragmentationContext[Vk.MaxMemoryTypes];
+ private readonly List CustomPoolContexts = new List();
+
+
+ internal DefragmentationContext(VulkanMemoryAllocator allocator, uint currentFrame, uint flags, DefragmentationStats stats)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ internal void AddPools(params VulkanMemoryPool[] Pools)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal void AddAllocations(Allocation[] allocations, out bool[] allocationsChanged)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result Defragment(ulong maxCPUBytesToMove, int maxCPUAllocationsToMove, ulong maxGPUBytesToMove,
+ int maxGPUAllocationsToMove, CommandBuffer cbuffer, DefragmentationStats stats,
+ DefragmentationFlags flags)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result DefragmentationPassBegin(ref DefragmentationPassMoveInfo[] Info)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result DefragmentationPassEnd()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/FastDefragAlgorithm.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/FastDefragAlgorithm.cs
new file mode 100644
index 0000000000..a893cfe75f
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/FastDefragAlgorithm.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using Silk.NET.Vulkan;
+using VMASharp;
+
+namespace VMASharp.Defragmentation
+{
+ using Metadata;
+
+ internal sealed class FastDefragAlgorithm : DefragmentationAlgorithm
+ {
+ private readonly bool overlappingMoveSupported;
+ private int allocationCount;
+ private bool allAllocations;
+
+ private ulong bytesMoved;
+ private int allocationsMoved;
+
+ private readonly List blockInfos = new List();
+
+ public FastDefragAlgorithm(VulkanMemoryAllocator allocator, BlockList list, uint currentFrame, bool overlappingMoveSupported) : base(allocator, list, currentFrame)
+ {
+ this.overlappingMoveSupported = overlappingMoveSupported;
+ }
+
+ public override ulong BytesMoved => throw new NotImplementedException();
+
+ public override int AllocationsMoved => throw new NotImplementedException();
+
+ public override void AddAll()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void AddAllocation(Allocation alloc, out bool changed)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Result Defragment(ulong maxBytesToMove, int maxAllocationsToMove, DefragmentationFlags flags, out DefragmentationMove[] moves)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void PreprocessMetadata()
+ {
+
+ }
+
+ private void PostprocessMetadata()
+ {
+
+ }
+
+ private void InsertSuballoc(BlockMetadata_Generic metadata, in Suballocation suballoc)
+ {
+
+ }
+
+ private struct BlockInfo
+ {
+ public int OrigBlockIndex;
+ }
+
+ private class FreeSpaceDatabase
+ {
+ private const int MaxCount = 4;
+
+ private FreeSpace[] FreeSpaces = new FreeSpace[MaxCount];
+
+ public FreeSpaceDatabase()
+ {
+ for (int i = 0; i < FreeSpaces.Length; ++i)
+ {
+ FreeSpaces[i].BlockInfoIndex = -1;
+ }
+ }
+
+ public void Register(int blockInfoIndex, long offset, long size)
+ {
+ if (size < Helpers.MinFreeSuballocationSizeToRegister)
+ {
+ return;
+ }
+
+ int bestIndex = -1;
+ for (int i = 0; i < FreeSpaces.Length; ++i)
+ {
+ ref FreeSpace space = ref FreeSpaces[i];
+
+ if (space.BlockInfoIndex == -1)
+ {
+ bestIndex = i;
+ break;
+ }
+
+ if (space.Size < size && (bestIndex == -1 || space.Size < FreeSpaces[bestIndex].Size))
+ {
+ bestIndex = i;
+ }
+ }
+
+ if (bestIndex != -1)
+ {
+ ref FreeSpace bestSpace = ref FreeSpaces[bestIndex];
+
+ bestSpace.BlockInfoIndex = blockInfoIndex;
+ bestSpace.Offset = offset;
+ bestSpace.Size = size;
+ }
+ }
+
+ public bool Fetch(long alignment, long size, out int blockInfoIndex, out long destOffset)
+ {
+ int bestIndex = -1;
+ long bestFreeSpaceAfter = 0;
+
+ for (int i = 0; i < FreeSpaces.Length; ++i)
+ {
+ ref FreeSpace space = ref FreeSpaces[i];
+
+ if (space.BlockInfoIndex == -1)
+ break;
+
+ long tmpOffset = Helpers.AlignUp(space.Offset, alignment);
+
+ if (tmpOffset + size <= space.Offset + space.Size)
+ {
+ long freeSpaceAfter = (space.Offset + space.Size) - (tmpOffset + size);
+
+ if (bestIndex == -1 || freeSpaceAfter > bestFreeSpaceAfter)
+ {
+ bestIndex = i;
+ bestFreeSpaceAfter = freeSpaceAfter;
+ }
+ }
+ }
+
+ if (bestIndex != -1)
+ {
+ ref FreeSpace bestSpace = ref FreeSpaces[bestIndex];
+
+ blockInfoIndex = bestSpace.BlockInfoIndex;
+ destOffset = Helpers.AlignUp(bestSpace.Offset, alignment);
+
+ if (bestFreeSpaceAfter >= Helpers.MinFreeSuballocationSizeToRegister)
+ {
+ long alignmentPlusSize = (destOffset - bestSpace.Offset) + size;
+
+ bestSpace.Offset += alignmentPlusSize;
+ bestSpace.Size -= alignmentPlusSize;
+ }
+ else
+ {
+ bestSpace.BlockInfoIndex = -1;
+ }
+
+ return true;
+ }
+
+ blockInfoIndex = default;
+ destOffset = default;
+ return false;
+ }
+
+ private struct FreeSpace
+ {
+ public int BlockInfoIndex;
+ public long Offset, Size;
+ }
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/GenericDefragAlgorithm.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/GenericDefragAlgorithm.cs
new file mode 100644
index 0000000000..5cdb2314cf
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/GenericDefragAlgorithm.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp.Defragmentation
+{
+ internal sealed class GenericDefragAlgorithm : DefragmentationAlgorithm
+ {
+ private int allocationCount;
+ private bool allAllocations;
+
+ private ulong bytesMoved;
+ private int allocationsMoved;
+
+ public GenericDefragAlgorithm(VulkanMemoryAllocator allocator, BlockList list, uint currentFrame, bool overlappingMoveSupported) : base(allocator, list, currentFrame)
+ {
+ }
+
+ public override ulong BytesMoved => throw new NotImplementedException();
+
+ public override int AllocationsMoved => throw new NotImplementedException();
+
+ public override void AddAll()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void AddAllocation(Allocation alloc, out bool changed)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Result Defragment(ulong maxBytesToMove, int maxAllocationsToMove, DefragmentationFlags flags, out DefragmentationMove[] moves)
+ {
+ throw new NotImplementedException();
+ }
+
+ private class BlockInfo
+ {
+ public int OriginalBlockIndex;
+ public VulkanMemoryBlock Block;
+ public bool HasNonMovableAllocations;
+ public readonly List Allocations = new List();
+
+ public void CalcHasNonMovableAllocations()
+ {
+ this.HasNonMovableAllocations = this.Block.MetaData.AllocationCount != this.Allocations.Count;
+ }
+
+ public void SortAllocationsBySizeDescending()
+ {
+ this.Allocations.Sort(delegate (AllocateInfo info1, AllocateInfo info2)
+ {
+ return info1.Allocation.Size.CompareTo(info2.Allocation.Size);
+ });
+ }
+
+ public void SortAllocationsByOffsetDescending()
+ {
+ this.Allocations.Sort(delegate (AllocateInfo info1, AllocateInfo info2)
+ {
+ return info1.Allocation.Offset.CompareTo(info2.Allocation.Offset);
+ });
+ }
+ }
+
+ private readonly List Blocks = new List();
+
+ //private Result DefragmentRound()
+
+ private int CalcBlocksWithNonMovableCount()
+ {
+ throw new NotImplementedException();
+ }
+
+ private static bool MoveMakesSense(int destBlockIndex, ulong destOffset, int sourceBlockIndex, ulong sourceOffset)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Defragmentation/Structures.cs b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/Structures.cs
new file mode 100644
index 0000000000..f75725f5a0
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Defragmentation/Structures.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Silk.NET.Vulkan;
+using VMASharp;
+using Buffer = Silk.NET.Vulkan.Buffer;
+
+namespace VMASharp.Defragmentation
+{
+ public struct DefragmentationInfo2
+ {
+ public DefragmentationFlags Flags;
+
+ public Allocation[] Allocations;
+
+ public bool[] AllocationsChanged;
+
+ public VulkanMemoryPool[] Pools;
+
+ public ulong MaxCPUBytesToMove;
+
+ public int MaxCPUAllocationsToMove;
+
+ public ulong MaxGPUBytesToMove;
+
+ public int MaxGPUAllocationsToMove;
+
+ public CommandBuffer CommandBuffer;
+ }
+
+ public struct DefragmentationPassMoveInfo
+ {
+ public Allocation Allocation;
+
+ public DeviceMemory Memory;
+
+ public ulong Offset;
+ }
+
+ public struct DefragmentationInfo
+ {
+ public ulong MaxBytesToMove;
+
+ public int MaxAllocationsToMove;
+ }
+
+ public class DefragmentationStats
+ {
+ public long BytesMoved;
+
+ public long BytesFreed;
+
+ public int AllocationsMoved;
+
+ public int DeviceMemoryBlocksFreed;
+ }
+
+ internal struct DefragmentationMove
+ {
+ public int SourceBlockIndex, DestinationBlockIndex;
+
+ public ulong SourceOffset, DestinationOffset, Size;
+
+ public Allocation Allocation;
+
+ public VulkanMemoryBlock SourceBlock, DestinationBlock;
+ }
+
+ internal struct BlockDefragmentationContext
+ {
+ public enum BlockFlags
+ {
+ Used = 0x01
+ }
+
+ public BlockFlags Flags;
+ public Buffer Buffer;
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Exceptions.cs b/src/Vulkan/VMASharp_OriginalSources/Exceptions.cs
new file mode 100644
index 0000000000..50f15fb7aa
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Exceptions.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Silk.NET.Vulkan;
+
+namespace VMASharp
+{
+ public class VulkanResultException : ApplicationException
+ {
+ public readonly Result? Result;
+
+ public VulkanResultException(string message) : base(message)
+ {
+ }
+
+ public VulkanResultException(string message, Exception? innerException) : base(message, innerException)
+ {
+ }
+
+ public VulkanResultException(Result res) : base("Vulkan returned an API error code")
+ {
+ Result = res;
+ }
+
+ public VulkanResultException(string message, Result res) : base(message)
+ {
+ Result = res;
+ }
+ }
+
+ public class AllocationException : VulkanResultException
+ {
+ public AllocationException(string message) : base (message)
+ {
+ }
+
+ public AllocationException(string message, Exception? innerException) : base (message, innerException)
+ {
+ }
+
+ public AllocationException(Result res) : base(res)
+ {
+ }
+
+ public AllocationException(string message, Result res) : base (message, res)
+ {
+ }
+ }
+
+ public class DefragmentationException : VulkanResultException
+ {
+ public DefragmentationException(string message) : base(message)
+ {
+ }
+
+ public DefragmentationException(Result res) : base(res)
+ {
+ }
+
+ public DefragmentationException(string message, Result res) : base(message, res)
+ {
+ }
+ }
+
+ public class MapMemoryException : VulkanResultException
+ {
+ public MapMemoryException(string message) : base(message)
+ {
+ }
+
+ public MapMemoryException(Result res) : base("Mapping a Device Memory block encountered an issue", res)
+ {
+ }
+
+ public MapMemoryException(string message, Result res) : base(message, res)
+ {
+ }
+ }
+
+ public class ValidationFailedException : ApplicationException
+ {
+ public ValidationFailedException() : base("Validation of Allocator structures found a bug!")
+ {
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Helpers.cs b/src/Vulkan/VMASharp_OriginalSources/Helpers.cs
new file mode 100644
index 0000000000..0f7c65fa99
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Helpers.cs
@@ -0,0 +1,321 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Numerics;
+using Silk.NET.Core;
+
+namespace VMASharp
+{
+ internal static class Helpers
+ {
+ public const long MinFreeSuballocationSizeToRegister = 16;
+ public const int FrameIndexLost = -1;
+ public const uint CorruptionDetectionMagicValue = 0x7F84E666;
+
+ public const byte AllocationFillPattern_Created = 0xDC;
+ public const byte AllocationFillPattern_Destroyed = 0xEF;
+ public const bool DebugInitializeAllocations = false;
+
+ public const long DebugMargin = 0;
+ public const long DebugAlignment = 1;
+ public const long DebugMinBufferImageGranularity = 1;
+
+ public const AllocationStrategyFlags InternalAllocationStrategy_MinOffset = (AllocationStrategyFlags)0x10000000u;
+
+ internal static readonly Version32 VulkanAPIVersion_1_0 = new Version32(1, 0, 0);
+ internal static readonly Version32 VulkanAPIVersion_1_1 = new Version32(1, 1, 0);
+
+ public static readonly Comparison> SuballocationNodeItemSizeLess = (first, second) => first.Value.Size.CompareTo(second.Value.Size);
+ public static readonly Comparison SuballocationItemSizeLess = (first, second) => first.Size.CompareTo(second.Size);
+
+ public static readonly Func DefaultMetaObjectCreate = size => new Metadata.BlockMetadata_Generic(size);
+
+ public static bool IsPow2(int v)
+ {
+ return BitOperations.PopCount((uint)v) == 1;
+ }
+
+ public static bool IsPow2(long v)
+ {
+ return BitOperations.PopCount((ulong)v) == 1;
+ }
+
+ public static int NextPow2(int v)
+ {
+ if (IsPow2(v))
+ return v;
+
+ return 1 << (32 - BitOperations.LeadingZeroCount((uint)v));
+ }
+
+ public static long NextPow2(long v)
+ {
+ if (IsPow2(v))
+ return v;
+
+ return 1L << (64 - BitOperations.LeadingZeroCount((ulong)v));
+ }
+
+ public static int PrevPow(int v)
+ {
+ return 1 << (31 - BitOperations.LeadingZeroCount((uint)v));
+ }
+
+ public static long PrevPow(long v)
+ {
+ return 1L << (63 - BitOperations.LeadingZeroCount((ulong)v));
+ }
+
+ public static bool BlocksOnSamePage(long resourceAOffset, long resourceASize, long resourceBOffset, long pageSize)
+ {
+ Debug.Assert(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
+
+ long resourceAEnd = resourceAOffset + resourceASize - 1;
+ long resourceAEndPage = resourceAEnd & ~(pageSize - 1);
+ long resourceBStart = resourceBOffset;
+ long resourceBStartPage = resourceBStart & ~(pageSize - 1);
+ return resourceAEndPage == resourceBStartPage;
+ }
+
+ public static bool IsBufferImageGranularityConflict(SuballocationType type1, SuballocationType type2)
+ {
+ if (type1 > type2)
+ {
+ //Swap
+ var type3 = type1;
+ type1 = type2;
+ type2 = type3;
+ }
+
+ switch(type1)
+ {
+ case SuballocationType.Free:
+ return false;
+ case SuballocationType.Unknown:
+ return true;
+ case SuballocationType.Buffer:
+ return type2 == SuballocationType.Image_Unknown || type2 == SuballocationType.Image_Optimal;
+ case SuballocationType.Image_Unknown:
+ return type2 == SuballocationType.Image_Unknown || type2 == SuballocationType.Image_Linear || type2 == SuballocationType.Image_Optimal;
+ case SuballocationType.Image_Linear:
+ return type2 == SuballocationType.Image_Optimal;
+ case SuballocationType.Image_Optimal:
+ return false;
+ default:
+ Debug.Assert(false);
+ return true;
+ }
+ }
+
+ public static long AlignUp(long value, long alignment)
+ {
+ return ((value + alignment - 1) / alignment) * alignment;
+ }
+
+ public static long AlignDown(long value, long alignment)
+ {
+ return (long)((ulong)value / (ulong)alignment * (ulong)alignment);
+ }
+
+ public interface IComparer_Single
+ {
+ int Compare(T item);
+ }
+
+ public interface IComparer_Normal
+ {
+ int Compare(T l, T r);
+ }
+
+ public static int BinarySearch(this List list, T value, TComp comp) where TComp : struct, IComparer_Normal
+ {
+ int begin = 0, end = list.Count - 1;
+
+ while (begin <= end)
+ {
+ int mid = (begin + end) / 2;
+
+ int comparison = comp.Compare(list[mid], value);
+
+ if (comparison == 0)
+ {
+ return mid;
+ }
+
+ if (comparison < 0)
+ {
+ begin = mid + 1;
+ }
+ else
+ {
+ end = mid - 1;
+ }
+ }
+
+ return ~begin;
+ }
+
+ public static int BinarySearch(this List list, TState state, Func SearchCompare)
+ {
+ int begin = 0, end = list.Count - 1;
+
+ while (begin <= end)
+ {
+ int mid = (begin + end) / 2;
+
+ int comparison = SearchCompare(list[mid], state);
+
+ if (comparison == 0)
+ {
+ return mid;
+ }
+
+ if (comparison < 0)
+ {
+ begin = mid + 1;
+ }
+ else
+ {
+ end = mid - 1;
+ }
+ }
+
+ return ~begin;
+ }
+
+ public static int BinarySearch(this List list, T value, Comparison comparer)
+ {
+ int begin = 0, end = list.Count - 1;
+
+ while (begin <= end)
+ {
+ int mid = (begin + end) / 2;
+
+ int comparison = comparer(list[mid], value);
+
+ if (comparison == 0)
+ {
+ return mid;
+ }
+
+ if (comparison < 0)
+ {
+ begin = mid + 1;
+ }
+ else
+ {
+ end = mid - 1;
+ }
+ }
+
+ return ~begin;
+ }
+
+ public static int BinarySearch_Leftmost(this List list, TComp comp) where TComp : struct, IComparer_Single
+ {
+ int begin = 0, end = list.Count, comparison = -1;
+
+ while (begin < end)
+ {
+ int mid = (begin + end) / 2;
+
+ comparison = comp.Compare(list[mid]);
+
+ if (comparison < 0)
+ {
+ begin = mid + 1;
+ }
+ else
+ {
+ end = mid;
+ }
+ }
+
+ return (comparison == 0) ? begin : ~begin;
+ }
+
+ public static int BinarySearch_Leftmost(this List list, T value, Comparison comparer)
+ {
+ int begin = 0, end = list.Count;
+
+ while (begin < end)
+ {
+ int mid = (begin + end) / 2;
+
+ int comparison = comparer(list[mid], value);
+
+ if (comparison < 0)
+ {
+ begin = mid + 1;
+ }
+ else
+ {
+ end = mid;
+ }
+ }
+
+ return (comparer(list[begin], value) == 0) ? begin : ~begin;
+ }
+
+ public static int InsertSorted(this List list, T value)
+ {
+ int i = list.BinarySearch(value);
+
+ if (i < 0)
+ {
+ i = ~i;
+ }
+
+ list.Insert(i, value);
+
+ return i;
+ }
+
+ public static int InsertSorted(this List list, T value, Comparison comparison)
+ {
+ int i = list.BinarySearch(value, comparison);
+
+ if (i < 0)
+ {
+ i = ~i;
+ }
+
+ list.Insert(i, value);
+
+ return i;
+ }
+
+ public static int FindIndex(this List list, TState state, Func predicate)
+ {
+ for (int i = 0; i < list.Count; ++i)
+ {
+ if (predicate(list[i], state))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ [Conditional("DEBUG")]
+ public static void AssertNotNull(this T instance) where T: class
+ {
+ Debug.Assert(instance != null);
+ }
+
+ [Conditional("DEBUG")]
+ public static void AssertNotNull(this IntPtr ptr)
+ {
+ Debug.Assert(ptr != default);
+ }
+
+ public static void Validate([System.Diagnostics.CodeAnalysis.DoesNotReturnIf(false)] bool assertion)
+ {
+ if (!assertion)
+ {
+ throw new ValidationFailedException();
+ }
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Metadata/BlockMetadata_Generic.cs b/src/Vulkan/VMASharp_OriginalSources/Metadata/BlockMetadata_Generic.cs
new file mode 100644
index 0000000000..8e0a657398
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Metadata/BlockMetadata_Generic.cs
@@ -0,0 +1,788 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace VMASharp.Metadata
+{
+ internal sealed class BlockMetadata_Generic : IBlockMetadata
+ {
+ public long Size { get; }
+
+ private int freeCount;
+
+ private long sumFreeSize;
+
+ private readonly LinkedList suballocations = new LinkedList();
+
+ private readonly List> freeSuballocationsBySize = new List>();
+
+ public int AllocationCount => suballocations.Count - freeCount;
+
+ public long SumFreeSize => sumFreeSize;
+
+ public long UnusedRangeSizeMax
+ {
+ get
+ {
+ var count = this.freeSuballocationsBySize.Count;
+
+ if (count != 0)
+ {
+ return this.freeSuballocationsBySize[count - 1].ValueRef.Size;
+ }
+
+ return 0;
+ }
+ }
+
+ public bool IsEmpty => (this.suballocations.Count == 1) && (this.freeCount == 1);
+
+ public BlockMetadata_Generic(long blockSize)
+ {
+ this.Size = blockSize;
+ this.freeCount = 1;
+ this.sumFreeSize = blockSize;
+
+ Debug.Assert(blockSize > Helpers.MinFreeSuballocationSizeToRegister);
+
+ var node = this.suballocations.AddLast(new Suballocation(0, blockSize));
+
+ this.freeSuballocationsBySize.Add(node);
+ }
+
+ public void Alloc(in AllocationRequest request, SuballocationType type, long allocSize, BlockAllocation allocation)
+ {
+ Debug.Assert(request.Type == AllocationRequestType.Normal);
+ Debug.Assert(request.Item != null);
+
+ if (request.Item is not LinkedListNode requestNode)
+ {
+ throw new InvalidOperationException();
+ }
+
+ Debug.Assert(object.ReferenceEquals(requestNode.List, this.suballocations));
+
+ ref Suballocation suballoc = ref requestNode.ValueRef;
+
+ Debug.Assert(suballoc.Type == SuballocationType.Free);
+ Debug.Assert(request.Offset >= suballoc.Offset);
+
+ long paddingBegin = request.Offset - suballoc.Offset;
+
+ Debug.Assert(suballoc.Size >= paddingBegin + allocSize);
+
+ long paddingEnd = suballoc.Size - paddingBegin - allocSize;
+
+ UnregisterFreeSuballocation(requestNode);
+
+ suballoc.Offset = request.Offset;
+ suballoc.Size = allocSize;
+ suballoc.Type = type;
+ suballoc.Allocation = allocation;
+
+ if (paddingEnd > 0)
+ {
+ var newNode = this.suballocations.AddAfter(requestNode, new Suballocation(request.Offset + allocSize, paddingEnd));
+ RegisterFreeSuballocation(newNode);
+ }
+
+ if (paddingBegin > 0)
+ {
+ var newNode = this.suballocations.AddBefore(requestNode, new Suballocation(request.Offset - paddingBegin, paddingBegin));
+ RegisterFreeSuballocation(newNode);
+ }
+
+ if (paddingBegin > 0)
+ {
+ if (paddingEnd > 0)
+ {
+ this.freeCount += 1;
+ }
+ }
+ else if (paddingEnd <= 0)
+ {
+ this.freeCount -= 1;
+ }
+
+ this.sumFreeSize -= allocSize;
+ }
+
+ public void CheckCorruption(nuint blockDataPointer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool TryCreateAllocationRequest(in AllocationContext context, out AllocationRequest request)
+ {
+ request = default;
+
+ request.Type = AllocationRequestType.Normal;
+
+ if (context.CanMakeOtherLost == false && this.sumFreeSize < context.AllocationSize + 2 * Helpers.DebugMargin)
+ {
+ return false;
+ }
+
+ var contextCopy = context;
+ contextCopy.CanMakeOtherLost = false;
+
+ int freeSuballocCount = this.freeSuballocationsBySize.Count;
+ if (freeSuballocCount > 0)
+ {
+ if (context.Strategy == AllocationStrategyFlags.BestFit)
+ {
+ var index = this.freeSuballocationsBySize.FindIndex(context.AllocationSize + 2 * Helpers.DebugMargin, (node, size) => node.ValueRef.Size >= size);
+
+ for (; index < freeSuballocCount; ++index)
+ {
+ var suballocNode = this.freeSuballocationsBySize[index];
+
+ if (this.CheckAllocation(in contextCopy, suballocNode, ref request))
+ {
+ request.Item = suballocNode;
+ return true;
+ }
+ }
+ }
+ else if (context.Strategy == Helpers.InternalAllocationStrategy_MinOffset)
+ {
+ for (var node = this.suballocations.First; node != null; node = node.Next)
+ {
+ if (node.Value.Type == SuballocationType.Free
+ && this.CheckAllocation(in contextCopy, node, ref request))
+ {
+ request.Item = node;
+ return true;
+ }
+ }
+ }
+ else //Worst Fit, First Fit
+ {
+ for (int i = freeSuballocCount; i >= 0; --i)
+ {
+ var item = this.freeSuballocationsBySize[i];
+
+ if (this.CheckAllocation(in contextCopy, item, ref request))
+ {
+ request.Item = item;
+ return true;
+ }
+ }
+ }
+ }
+
+ if (context.CanMakeOtherLost)
+ {
+ bool found = false;
+ AllocationRequest tmpRequest = default;
+
+ for (LinkedListNode? tNode = this.suballocations.First; tNode != null; tNode = tNode.Next)
+ {
+ if (this.CheckAllocation(in context, tNode, ref tmpRequest))
+ {
+ if (context.Strategy == AllocationStrategyFlags.FirstFit)
+ {
+ request = tmpRequest;
+ request.Item = tNode;
+ break;
+ }
+
+ if (!found || tmpRequest.CalcCost() < request.CalcCost())
+ {
+ request = tmpRequest;
+ request.Item = tNode;
+ found = true;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ return false;
+ }
+
+ public void Free(BlockAllocation allocation)
+ {
+ for (LinkedListNode? node = this.suballocations.First; node != null; node = node.Next)
+ {
+ ref var suballoc = ref node.ValueRef;
+
+ if (object.ReferenceEquals(suballoc.Allocation, allocation))
+ {
+ this.FreeSuballocation(node);
+ return;
+ }
+ }
+
+ throw new InvalidOperationException("Allocation not found!");
+ }
+
+ public void FreeAtOffset(long offset)
+ {
+ for (LinkedListNode? node = this.suballocations.First; node != null; node = node.Next)
+ {
+ ref var suballoc = ref node.ValueRef;
+
+ if (suballoc.Offset == offset)
+ {
+ this.FreeSuballocation(node);
+ return;
+ }
+ }
+
+ throw new InvalidOperationException("Allocation not found!");
+ }
+
+ public int MakeAllocationsLost(int currentFrame, int frameInUseCount)
+ {
+ int lost = 0;
+
+ for (var node = this.suballocations.First; node != null; node = node.Next)
+ {
+ ref var suballoc = ref node.ValueRef;
+
+ if (suballoc.Type != SuballocationType.Free &&
+ suballoc.Allocation!.CanBecomeLost &&
+ suballoc.Allocation.MakeLost(currentFrame, frameInUseCount))
+ {
+ node = FreeSuballocation(node);
+ lost += 1;
+ }
+ }
+
+ return lost;
+ }
+
+ public bool MakeRequestedAllocationsLost(int currentFrame, int frameInUseCount, ref AllocationRequest request)
+ {
+ if (request.Type != AllocationRequestType.Normal)
+ {
+ throw new ArgumentException("Allocation Request Type was not normal");
+ }
+
+ LinkedListNode? tNode = request.Item as LinkedListNode ?? throw new InvalidOperationException();
+
+ while (request.ItemsToMakeLostCount > 0)
+ {
+ if (tNode.ValueRef.Type == SuballocationType.Free)
+ {
+ tNode = tNode.Next;
+ }
+
+ Debug.Assert(tNode != null);
+
+ ref var suballoc = ref tNode.ValueRef;
+
+ Debug.Assert(suballoc.Allocation != null);
+ Debug.Assert(suballoc.Allocation.CanBecomeLost);
+
+ if (suballoc.Allocation.MakeLost(currentFrame, frameInUseCount))
+ {
+ request.Item = tNode = FreeSuballocation(tNode);
+ request.ItemsToMakeLostCount -= 1;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ Debug.Assert(request.Item != null);
+ Debug.Assert(Unsafe.As>(request.Item).ValueRef.Type == SuballocationType.Free);
+
+ return true;
+ }
+
+ public void Validate()
+ {
+ Helpers.Validate(this.suballocations.Count > 0);
+
+ long calculatedOffset = 0, calculatedSumFreeSize = 0;
+ int calculatedFreeCount = 0, freeSuballocationsToRegister = 0;
+
+ bool prevFree = false;
+
+ foreach (Suballocation subAlloc in this.suballocations)
+ {
+ Helpers.Validate(subAlloc.Offset == calculatedOffset);
+
+ bool currFree = subAlloc.Type == SuballocationType.Free;
+
+ if (currFree)
+ {
+ Helpers.Validate(!prevFree);
+ Helpers.Validate(subAlloc.Allocation == null);
+
+ calculatedSumFreeSize += subAlloc.Size;
+ calculatedFreeCount += 1;
+
+ if (subAlloc.Size >= Helpers.MinFreeSuballocationSizeToRegister)
+ {
+ freeSuballocationsToRegister += 1;
+ }
+
+ Helpers.Validate(subAlloc.Size >= Helpers.DebugMargin);
+ }
+ else
+ {
+ Helpers.Validate(subAlloc.Allocation != null);
+ Helpers.Validate(subAlloc.Allocation!.Offset == subAlloc.Offset);
+ Helpers.Validate(subAlloc.Allocation.Size == subAlloc.Size);
+ Helpers.Validate(Helpers.DebugMargin == 0 || prevFree);
+ }
+
+ calculatedOffset += subAlloc.Size;
+ prevFree = currFree;
+ }
+
+ Helpers.Validate(this.freeSuballocationsBySize.Count == freeSuballocationsToRegister);
+
+ this.ValidateFreeSuballocationList();
+
+ Helpers.Validate(calculatedOffset == this.Size);
+ Helpers.Validate(calculatedSumFreeSize == this.sumFreeSize);
+ Helpers.Validate(calculatedFreeCount == this.freeCount);
+ }
+
+ [Conditional("DEBUG")]
+ private void ValidateFreeSuballocationList()
+ {
+ long lastSize = 0;
+
+ for (int i = 0, count = this.freeSuballocationsBySize.Count; i < count; ++i)
+ {
+ var node = this.freeSuballocationsBySize[i];
+
+ Helpers.Validate(node.ValueRef.Type == SuballocationType.Free);
+ Helpers.Validate(node.ValueRef.Size >= Helpers.MinFreeSuballocationSizeToRegister);
+ Helpers.Validate(node.ValueRef.Size >= lastSize);
+
+ lastSize = node.ValueRef.Size;
+ }
+ }
+
+ private bool CheckAllocation(in AllocationContext context, LinkedListNode node, ref AllocationRequest request)
+ {
+ if (context.AllocationSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(context.AllocationSize));
+ }
+
+ if (context.SuballocationType == SuballocationType.Free)
+ {
+ throw new ArgumentException("Invalid Allocation Type", nameof(context.SuballocationType));
+ }
+
+ if (context.BufferImageGranularity <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(context.BufferImageGranularity));
+ }
+
+ request.ItemsToMakeLostCount = 0;
+ request.SumFreeSize = 0;
+ request.SumItemSize = 0;
+
+ ref Suballocation suballocItem = ref node.ValueRef;
+
+ if (context.CanMakeOtherLost)
+ {
+ if (suballocItem.Type == SuballocationType.Free)
+ {
+ request.SumFreeSize = suballocItem.Size;
+ }
+ else
+ {
+ if (suballocItem.Allocation.CanBecomeLost && suballocItem.Allocation.LastUseFrameIndex + context.FrameInUseCount < context.CurrentFrame)
+ {
+ request.ItemsToMakeLostCount += 1;
+ request.SumItemSize = suballocItem.Size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (this.Size - suballocItem.Offset < context.AllocationSize)
+ {
+ return false;
+ }
+
+ var offset = (Helpers.DebugMargin > 0) ? suballocItem.Offset + Helpers.DebugMargin : suballocItem.Offset;
+
+ request.Offset = Helpers.AlignUp(offset, context.AllocationAlignment);
+
+ AccountForBackwardGranularityConflict(node, context.BufferImageGranularity, context.SuballocationType, ref request);
+
+ if (request.Offset >= suballocItem.Offset + suballocItem.Size)
+ {
+ return false;
+ }
+
+ long paddingBegin = request.Offset - suballocItem.Offset;
+ long requiredEndMargin = Helpers.DebugMargin;
+ long totalSize = paddingBegin + context.AllocationSize + requiredEndMargin;
+
+ if (suballocItem.Offset + totalSize > this.Size)
+ {
+ return false;
+ }
+
+ var prevNode = node;
+
+ if (totalSize > suballocItem.Size)
+ {
+ long remainingSize = totalSize - suballocItem.Size;
+ while (remainingSize > 0)
+ {
+ if (prevNode.Next == null)
+ {
+ return false;
+ }
+
+ prevNode = prevNode.Next;
+
+ ref var tmpSuballoc = ref prevNode.ValueRef; //TODO: Make better reference to this variable
+
+ if (prevNode.ValueRef.Type == SuballocationType.Free)
+ {
+ request.SumFreeSize += prevNode.ValueRef.Size;
+ }
+ else
+ {
+ Debug.Assert(prevNode.ValueRef.Allocation != null);
+
+ if (tmpSuballoc.Allocation.CanBecomeLost && tmpSuballoc.Allocation.LastUseFrameIndex + context.FrameInUseCount < context.CurrentFrame)
+ {
+ request.ItemsToMakeLostCount += 1;
+
+ request.SumItemSize += tmpSuballoc.Size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ remainingSize = (tmpSuballoc.Size < remainingSize) ? remainingSize - tmpSuballoc.Size : 0;
+ }
+ }
+
+ if (context.BufferImageGranularity > 1)
+ {
+ var nextNode = prevNode.Next;
+
+ while (nextNode != null)
+ {
+ ref Suballocation nextItem = ref nextNode.ValueRef;
+
+ if (Helpers.BlocksOnSamePage(request.Offset, context.AllocationSize, nextItem.Offset, context.BufferImageGranularity))
+ {
+ if (Helpers.IsBufferImageGranularityConflict(context.SuballocationType, nextItem.Type))
+ {
+ Debug.Assert(nextItem.Allocation != null);
+
+ if (nextItem.Allocation.CanBecomeLost && nextItem.Allocation.LastUseFrameIndex + context.FrameInUseCount < context.CurrentFrame)
+ {
+ request.ItemsToMakeLostCount += 1;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ nextNode = nextNode.Next;
+ }
+ }
+ }
+ else
+ {
+ request.SumFreeSize = suballocItem.Size;
+
+ if (suballocItem.Size < context.AllocationSize)
+ {
+ return false;
+ }
+
+ var offset = suballocItem.Offset;
+
+ if (Helpers.DebugMargin > 0)
+ {
+ offset += Helpers.DebugMargin;
+ }
+
+ request.Offset = Helpers.AlignUp(offset, context.AllocationAlignment);
+
+ AccountForBackwardGranularityConflict(node, context.BufferImageGranularity, context.SuballocationType, ref request);
+
+ long paddingBegin = request.Offset - suballocItem.Offset, requiredEndMargin = Helpers.DebugMargin;
+
+ if (paddingBegin + context.AllocationSize + requiredEndMargin > suballocItem.Size)
+ {
+ return false;
+ }
+
+ if (context.BufferImageGranularity > 1)
+ {
+ var nextNode = node.Next;
+
+ while (nextNode != null)
+ {
+ ref var nextItem = ref nextNode.ValueRef;
+
+ if (Helpers.BlocksOnSamePage(request.Offset, context.AllocationSize, nextItem.Offset, context.BufferImageGranularity))
+ {
+ if (Helpers.IsBufferImageGranularityConflict(context.SuballocationType, nextItem.Type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ nextNode = nextNode.Next;
+ }
+ }
+ }
+
+ return true;
+
+ static void AccountForBackwardGranularityConflict(LinkedListNode node, long granularity, SuballocationType suballocType, ref AllocationRequest request)
+ {
+ if (granularity == 1)
+ {
+ return;
+ }
+
+ LinkedListNode prevNode = node;
+
+ while (prevNode.Previous != null)
+ {
+ prevNode = prevNode.Previous;
+
+ ref var prevAlloc = ref prevNode.ValueRef;
+
+ if (Helpers.BlocksOnSamePage(prevAlloc.Offset, prevAlloc.Size, request.Offset, granularity))
+ {
+ if (Helpers.IsBufferImageGranularityConflict(prevAlloc.Type, suballocType))
+ {
+ request.Offset = Helpers.AlignUp(request.Offset, granularity);
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ private void MergeFreeWithNext(LinkedListNode node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(object.ReferenceEquals(node.List, this.suballocations));
+ Debug.Assert(node.ValueRef.Type == SuballocationType.Free);
+
+ var nextNode = node.Next;
+
+ Debug.Assert(nextNode != null);
+ Debug.Assert(nextNode.ValueRef.Type == SuballocationType.Free);
+
+ ref Suballocation item = ref node.ValueRef, nextItem = ref nextNode.ValueRef;
+
+ item.Size += nextItem.Size;
+ this.freeCount -= 1;
+ this.suballocations.Remove(nextNode);
+ }
+
+ private LinkedListNode FreeSuballocation(LinkedListNode item)
+ {
+ ref var suballoc = ref item.ValueRef;
+
+ suballoc.Type = SuballocationType.Free;
+ suballoc.Allocation = null;
+
+ this.freeCount += 1;
+ this.sumFreeSize += suballoc.Size;
+
+ var nextItem = item.Next;
+ var prevItem = item.Previous;
+
+ if (nextItem != null && nextItem.ValueRef.Type == SuballocationType.Free)
+ {
+ UnregisterFreeSuballocation(nextItem);
+ MergeFreeWithNext(item);
+ }
+
+ if (prevItem != null && prevItem.ValueRef.Type == SuballocationType.Free)
+ {
+ UnregisterFreeSuballocation(prevItem);
+ MergeFreeWithNext(prevItem);
+ RegisterFreeSuballocation(prevItem);
+ return prevItem;
+ }
+ else
+ {
+ RegisterFreeSuballocation(item);
+ return item;
+ }
+ }
+
+ private void RegisterFreeSuballocation(LinkedListNode item)
+ {
+ Debug.Assert(item.ValueRef.Type == SuballocationType.Free);
+ Debug.Assert(item.ValueRef.Size > 0);
+
+ this.ValidateFreeSuballocationList();
+
+ if (item.Value.Size >= Helpers.MinFreeSuballocationSizeToRegister)
+ {
+ if (this.freeSuballocationsBySize.Count == 0)
+ {
+ this.freeSuballocationsBySize.Add(item);
+ }
+ else
+ {
+ this.freeSuballocationsBySize.InsertSorted(item, Helpers.SuballocationNodeItemSizeLess);
+ }
+ }
+
+ this.ValidateFreeSuballocationList();
+ }
+
+ private void UnregisterFreeSuballocation(LinkedListNode item)
+ {
+ Debug.Assert(item.ValueRef.Type == SuballocationType.Free);
+ Debug.Assert(item.ValueRef.Size > 0);
+
+ if (item.ValueRef.Size >= Helpers.MinFreeSuballocationSizeToRegister)
+ {
+ int index = this.freeSuballocationsBySize.BinarySearch_Leftmost(item, Helpers.SuballocationNodeItemSizeLess);
+
+ Debug.Assert(index >= 0);
+
+ while (index < this.freeSuballocationsBySize.Count)
+ {
+ var tmp = this.freeSuballocationsBySize[index];
+
+ if (object.ReferenceEquals(tmp, item))
+ {
+ break;
+ }
+ else if (tmp.ValueRef.Size != item.ValueRef.Size)
+ {
+ throw new InvalidOperationException("Suballocation Not Found");
+ }
+
+ index += 1;
+ }
+
+ this.freeSuballocationsBySize.RemoveAt(index);
+
+ this.ValidateFreeSuballocationList();
+ }
+ }
+
+ public void CalcAllocationStatInfo(out StatInfo outInfo)
+ {
+ outInfo = default;
+
+ outInfo.BlockCount = 1;
+
+ int rangeCount = this.suballocations.Count;
+ outInfo.AllocationCount = rangeCount - this.freeCount;
+ outInfo.UnusedRangeCount = this.freeCount;
+
+ outInfo.UnusedBytes = this.sumFreeSize;
+ outInfo.UsedBytes = this.Size - outInfo.UnusedBytes;
+
+ outInfo.AllocationSizeMin = long.MaxValue;
+ outInfo.AllocationSizeMax = 0;
+ outInfo.UnusedRangeSizeMin = long.MaxValue;
+ outInfo.UnusedRangeSizeMax = 0;
+
+ for (var node = this.suballocations.First; node != null; node = node.Next)
+ {
+ ref var item = ref node.ValueRef;
+
+ if (item.Type != SuballocationType.Free)
+ {
+ if (item.Size < outInfo.AllocationSizeMin)
+ outInfo.AllocationSizeMin = item.Size;
+
+ if (item.Size > outInfo.AllocationSizeMax)
+ outInfo.AllocationSizeMax = item.Size;
+ }
+ else
+ {
+ if (item.Size < outInfo.UnusedRangeSizeMin)
+ outInfo.UnusedRangeSizeMin = item.Size;
+
+ if (item.Size > outInfo.UnusedRangeSizeMax)
+ outInfo.UnusedRangeSizeMax = item.Size;
+ }
+ }
+ }
+
+ public void AddPoolStats(ref PoolStats stats)
+ {
+ int rangeCount = this.suballocations.Count;
+
+ stats.Size += this.Size;
+
+ stats.UnusedSize += this.sumFreeSize;
+
+ stats.AllocationCount += rangeCount - this.freeCount;
+
+ stats.UnusedRangeCount += this.freeCount;
+
+ var tmp = this.UnusedRangeSizeMax;
+
+ if (tmp > stats.UnusedRangeSizeMax)
+ stats.UnusedRangeSizeMax = tmp;
+ }
+
+ public bool IsBufferImageGranularityConflictPossible(long bufferImageGranularity, ref SuballocationType type)
+ {
+ if (bufferImageGranularity == 1 || this.IsEmpty)
+ {
+ return false;
+ }
+
+ long minAlignment = long.MaxValue;
+ bool typeConflict = false;
+
+ for (var node = this.suballocations.First; node != null; node = node.Next)
+ {
+ ref var suballoc = ref node.ValueRef;
+
+ SuballocationType thisType = suballoc.Type;
+
+ if (thisType != SuballocationType.Free)
+ {
+ minAlignment = Math.Min(minAlignment, suballoc.Allocation.Alignment);
+
+ if (Helpers.IsBufferImageGranularityConflict(type, thisType))
+ {
+ typeConflict = true;
+ }
+
+ type = thisType;
+ }
+ }
+
+ return typeConflict || minAlignment >= bufferImageGranularity;
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Metadata/IBlockMetadata.cs b/src/Vulkan/VMASharp_OriginalSources/Metadata/IBlockMetadata.cs
new file mode 100644
index 0000000000..6926181418
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Metadata/IBlockMetadata.cs
@@ -0,0 +1,65 @@
+namespace VMASharp.Metadata
+{
+ ///
+ /// Allocation book-keeping for individual Device Memory Blocks.
+ ///
+ public interface IBlockMetadata
+ {
+ ///
+ /// Must return the total block size
+ ///
+ long Size { get; }
+
+ ///
+ /// Number of allocations held in this block
+ ///
+ int AllocationCount { get; }
+
+ ///
+ /// Total free bytes in this block
+ ///
+ long SumFreeSize { get; }
+
+ ///
+ ///
+ ///
+ long UnusedRangeSizeMax { get; }
+
+ ///
+ /// Does this block have 0 allocations?
+ ///
+ bool IsEmpty { get; }
+
+ ///
+ /// Validates all data structures inside this object. Throws an exception if validation fails.
+ /// Only called in Debug builds of VMASharp
+ ///
+ void Validate();
+
+ void CalcAllocationStatInfo(out StatInfo outInfo);
+
+ void AddPoolStats(ref PoolStats stats);
+
+ ///
+ /// Tries to find a place for suballocation with given parameters inside this block.
+ /// If succeeded, fills pAllocationRequest and returns true.
+ /// If failed, returns false.
+ ///
+ /// Contextual information for this allocation request
+ ///
+ /// Returns whether the method succeeds
+ bool TryCreateAllocationRequest(in AllocationContext context, out AllocationRequest request);
+
+ bool MakeRequestedAllocationsLost(int currentFrame, int frameInUseCount, ref AllocationRequest request);
+
+ int MakeAllocationsLost(int currentFrame, int frameInUseCount);
+
+ void CheckCorruption(nuint blockDataPointer);
+
+ void Alloc(in AllocationRequest request, SuballocationType type, long allocSize, BlockAllocation allocation);
+
+ void Free(BlockAllocation allocation);
+
+ void FreeAtOffset(long offset);
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Stats.cs b/src/Vulkan/VMASharp_OriginalSources/Stats.cs
new file mode 100644
index 0000000000..8711e95aed
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Stats.cs
@@ -0,0 +1,109 @@
+using System;
+using Silk.NET.Vulkan;
+
+namespace VMASharp
+{
+ public struct StatInfo
+ {
+ public int BlockCount, AllocationCount, UnusedRangeCount;
+ public long UsedBytes, UnusedBytes;
+ public long AllocationSizeMin, AllocationSizeAvg, AllocationSizeMax;
+ public long UnusedRangeSizeMin, UnusedRangeSizeAvg, UnusedRangeSizeMax;
+
+ internal static void Init(out StatInfo info)
+ {
+ info = default;
+ info.AllocationSizeMin = long.MaxValue;
+ info.UnusedRangeSizeMin = long.MaxValue;
+ }
+
+ internal static void Add(ref StatInfo info, in StatInfo other)
+ {
+ info.BlockCount += other.BlockCount;
+ info.AllocationCount += other.AllocationCount;
+ info.UnusedRangeCount += other.UnusedRangeCount;
+ info.UsedBytes += other.UsedBytes;
+ info.UnusedBytes += other.UnusedBytes;
+
+ if (info.AllocationSizeMin > other.AllocationSizeMin)
+ {
+ info.AllocationSizeMin = other.AllocationSizeMin;
+ }
+
+ if (info.AllocationSizeMax < other.AllocationSizeMax)
+ {
+ info.AllocationSizeMax = other.AllocationSizeMax;
+ }
+
+ if (info.UnusedRangeSizeMin > other.UnusedRangeSizeMin)
+ {
+ info.UnusedRangeSizeMin = other.UnusedRangeSizeMin;
+ }
+
+ if (info.UnusedRangeSizeMax < other.UnusedRangeSizeMax)
+ {
+ info.UnusedRangeSizeMax = other.UnusedRangeSizeMax;
+ }
+ }
+
+ internal static void PostProcessCalcStatInfo(ref StatInfo info)
+ {
+ info.AllocationSizeAvg = info.UsedBytes / info.AllocationCount;
+ info.UnusedRangeSizeAvg = info.UnusedBytes / info.UnusedRangeCount;
+ }
+ }
+
+ public class Stats
+ {
+ public readonly StatInfo[] MemoryType;
+ public readonly StatInfo[] MemoryHeap;
+ public StatInfo Total;
+
+ internal Stats(StatInfo[] memoryTypes, StatInfo[] memoryHeaps, in StatInfo total)
+ {
+ MemoryType = memoryTypes;
+ MemoryHeap = memoryHeaps;
+ Total = total;
+ }
+
+ internal Stats()
+ {
+ StatInfo.Init(out StatInfo total);
+
+ MemoryType = new StatInfo[Vk.MaxMemoryTypes];
+ MemoryHeap = new StatInfo[Vk.MaxMemoryHeaps];
+
+ for (int i = 0; i < Vk.MaxMemoryTypes; ++i)
+ StatInfo.Init(out MemoryType[i]);
+
+ for (int i = 0; i < Vk.MaxMemoryHeaps; ++i)
+ StatInfo.Init(out MemoryHeap[i]);
+ }
+
+ internal void PostProcess()
+ {
+ StatInfo.PostProcessCalcStatInfo(ref this.Total);
+
+ for (int i = 0; i < Vk.MaxMemoryTypes; ++i)
+ StatInfo.PostProcessCalcStatInfo(ref this.MemoryType[i]);
+
+ for (int i = 0; i < Vk.MaxMemoryHeaps; ++i)
+ StatInfo.PostProcessCalcStatInfo(ref this.MemoryHeap[i]);
+ }
+ }
+
+ public struct PoolStats
+ {
+ public long Size;
+
+ public long UnusedSize;
+
+ public int AllocationCount;
+
+ public int UnusedRangeCount;
+
+ public long UnusedRangeSizeMax;
+
+ public int BlockCount;
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/Suballocation.cs b/src/Vulkan/VMASharp_OriginalSources/Suballocation.cs
new file mode 100644
index 0000000000..96db4407ec
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/Suballocation.cs
@@ -0,0 +1,17 @@
+namespace VMASharp
+{
+ internal struct Suballocation
+ {
+ public long Offset, Size;
+ public BlockAllocation? Allocation;
+ public SuballocationType Type;
+
+ public Suballocation(long offset, long size, SuballocationType type = SuballocationType.Free, BlockAllocation? alloc = null)
+ {
+ Offset = offset;
+ Size = size;
+ Allocation = alloc;
+ Type = type;
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryAllocator.cs b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryAllocator.cs
new file mode 100644
index 0000000000..7523bfa34f
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryAllocator.cs
@@ -0,0 +1,1448 @@
+using Silk.NET.Vulkan;
+using Silk.NET.Core;
+using Silk.NET.Vulkan.Extensions.KHR;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Numerics;
+
+using Buffer = Silk.NET.Vulkan.Buffer;
+
+namespace VMASharp
+{
+ using Defragmentation;
+ using VMASharp;
+
+ public sealed unsafe class VulkanMemoryAllocator : IDisposable
+ {
+ const long SmallHeapMaxSize = 1024L * 1024 * 1024;
+ const BufferUsageFlags UnknownBufferUsage = unchecked((BufferUsageFlags)uint.MaxValue);
+
+ internal Vk VkApi { get; }
+
+ public Device Device { get; }
+ internal readonly Instance Instance;
+
+ internal readonly Version32 VulkanAPIVersion;
+
+ internal readonly bool UseExtMemoryBudget;
+ internal readonly bool UseAMDDeviceCoherentMemory;
+ internal readonly bool UseKhrBufferDeviceAddress;
+
+ internal uint HeapSizeLimitMask;
+
+ internal PhysicalDeviceProperties physicalDeviceProperties;
+ internal PhysicalDeviceMemoryProperties memoryProperties;
+
+ internal readonly BlockList[] BlockLists = new BlockList[Vk.MaxMemoryTypes]; //Default Pools
+
+ internal DedicatedAllocationHandler[] DedicatedAllocations = new DedicatedAllocationHandler[Vk.MaxMemoryTypes];
+
+ private long PreferredLargeHeapBlockSize;
+ private PhysicalDevice PhysicalDevice;
+ private int CurrentFrame;
+ private uint GPUDefragmentationMemoryTypeBits = uint.MaxValue;
+
+ private readonly ReaderWriterLockSlim PoolsMutex = new ReaderWriterLockSlim();
+ private readonly List Pools = new List();
+ internal uint NextPoolID;
+
+ internal CurrentBudgetData Budget = new CurrentBudgetData();
+
+ public VulkanMemoryAllocator(in VulkanMemoryAllocatorCreateInfo createInfo)
+ {
+ if (createInfo.VulkanAPIObject == null)
+ {
+ throw new ArgumentNullException(nameof(createInfo.VulkanAPIObject), "API vtable is null");
+ }
+
+ this.VkApi = createInfo.VulkanAPIObject;
+
+ if (createInfo.Instance.Handle == default)
+ {
+ throw new ArgumentNullException("createInfo.Instance");
+ }
+
+ if (createInfo.LogicalDevice.Handle == default)
+ {
+ throw new ArgumentNullException("createInfo.LogicalDevice");
+ }
+
+ if (createInfo.PhysicalDevice.Handle == default)
+ {
+ throw new ArgumentNullException("createInfo.PhysicalDevice");
+ }
+
+ if (createInfo.VulkanAPIVersion < Vk.Version11)
+ {
+ throw new NotSupportedException("Vulkan API Version of less than 1.1 is not supported");
+ }
+
+ this.Instance = createInfo.Instance;
+ this.PhysicalDevice = createInfo.PhysicalDevice;
+ this.Device = createInfo.LogicalDevice;
+
+ this.VulkanAPIVersion = createInfo.VulkanAPIVersion;
+
+ if (this.VulkanAPIVersion == 0)
+ {
+ this.VulkanAPIVersion = Vk.Version10;
+ }
+
+ this.UseExtMemoryBudget = (createInfo.Flags & AllocatorCreateFlags.ExtMemoryBudget) != 0;
+ this.UseAMDDeviceCoherentMemory = (createInfo.Flags & AllocatorCreateFlags.AMDDeviceCoherentMemory) != 0;
+ this.UseKhrBufferDeviceAddress = (createInfo.Flags & AllocatorCreateFlags.BufferDeviceAddress) != 0;
+
+ VkApi.GetPhysicalDeviceProperties(this.PhysicalDevice, out this.physicalDeviceProperties);
+ VkApi.GetPhysicalDeviceMemoryProperties(this.PhysicalDevice, out this.memoryProperties);
+
+ Debug.Assert(Helpers.IsPow2(Helpers.DebugAlignment));
+ Debug.Assert(Helpers.IsPow2(Helpers.DebugMinBufferImageGranularity));
+ Debug.Assert(Helpers.IsPow2((long)this.PhysicalDeviceProperties.Limits.BufferImageGranularity));
+ Debug.Assert(Helpers.IsPow2((long)this.PhysicalDeviceProperties.Limits.NonCoherentAtomSize));
+
+ this.PreferredLargeHeapBlockSize = (createInfo.PreferredLargeHeapBlockSize != 0) ? createInfo.PreferredLargeHeapBlockSize : (256L * 1024 * 1024);
+
+ this.GlobalMemoryTypeBits = this.CalculateGlobalMemoryTypeBits();
+
+ if (createInfo.HeapSizeLimits != null)
+ {
+ Span memoryHeaps = MemoryMarshal.CreateSpan(ref this.MemoryHeaps.Element0, this.MemoryHeapCount);
+
+ int heapLimitLength = Math.Min(createInfo.HeapSizeLimits.Length, (int)Vk.MaxMemoryHeaps);
+
+ for (int heapIndex = 0; heapIndex < heapLimitLength; ++heapIndex)
+ {
+ long limit = createInfo.HeapSizeLimits[heapIndex];
+
+ if (limit <= 0)
+ {
+ continue;
+ }
+
+ this.HeapSizeLimitMask |= 1u << heapIndex;
+ ref MemoryHeap heap = ref memoryHeaps[heapIndex];
+
+ if ((ulong)limit < heap.Size)
+ {
+ heap.Size = (ulong)limit;
+ }
+ }
+ }
+
+ for (int memTypeIndex = 0; memTypeIndex < this.MemoryTypeCount; ++memTypeIndex)
+ {
+ long preferredBlockSize = this.CalcPreferredBlockSize(memTypeIndex);
+
+ this.BlockLists[memTypeIndex] =
+ new BlockList(this, null, memTypeIndex, preferredBlockSize, 0, int.MaxValue, this.BufferImageGranularity, createInfo.FrameInUseCount, false, Helpers.DefaultMetaObjectCreate);
+
+ ref DedicatedAllocationHandler alloc = ref DedicatedAllocations[memTypeIndex];
+
+ alloc.Allocations = new List();
+ alloc.Mutex = new ReaderWriterLockSlim();
+ }
+
+ if (UseExtMemoryBudget)
+ {
+ UpdateVulkanBudget();
+ }
+ }
+
+ public int CurrentFrameIndex { get; set; }
+
+ internal long BufferImageGranularity
+ {
+ get
+ {
+ return (long)Math.Max(1, PhysicalDeviceProperties.Limits.BufferImageGranularity);
+ }
+ }
+
+ internal int MemoryHeapCount => (int)MemoryProperties.MemoryHeapCount;
+
+ internal int MemoryTypeCount => (int)MemoryProperties.MemoryTypeCount;
+
+ internal bool IsIntegratedGPU
+ {
+ get => this.PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.IntegratedGpu;
+ }
+
+ internal uint GlobalMemoryTypeBits { get; private set; }
+
+
+ public void Dispose()
+ {
+ if (this.Pools.Count != 0)
+ {
+ throw new InvalidOperationException("");
+ }
+
+ int i = this.MemoryTypeCount;
+
+ while (i-- != 0)
+ {
+ if (this.DedicatedAllocations[i].Allocations.Count != 0)
+ {
+ throw new InvalidOperationException("Unfreed dedicatedAllocations found");
+ }
+
+ this.BlockLists[i].Dispose();
+ }
+ }
+
+ public ref readonly PhysicalDeviceProperties PhysicalDeviceProperties => ref this.physicalDeviceProperties;
+
+ public ref readonly PhysicalDeviceMemoryProperties MemoryProperties => ref this.memoryProperties;
+
+ public MemoryPropertyFlags GetMemoryTypeProperties(int memoryTypeIndex)
+ {
+ return this.MemoryTypes[memoryTypeIndex].PropertyFlags;
+ }
+
+ public int? FindMemoryTypeIndex(uint memoryTypeBits, in AllocationCreateInfo allocInfo)
+ {
+ memoryTypeBits &= this.GlobalMemoryTypeBits;
+
+ if (allocInfo.MemoryTypeBits != 0)
+ {
+ memoryTypeBits &= allocInfo.MemoryTypeBits;
+ }
+
+ MemoryPropertyFlags requiredFlags = allocInfo.RequiredFlags, preferredFlags = allocInfo.PreferredFlags, notPreferredFlags = default;
+
+ switch (allocInfo.Usage)
+ {
+ case MemoryUsage.Unknown:
+ break;
+ case MemoryUsage.GPU_Only:
+ if (this.IsIntegratedGPU || (preferredFlags & MemoryPropertyFlags.MemoryPropertyHostVisibleBit) == 0)
+ {
+ preferredFlags |= MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
+ }
+ break;
+ case MemoryUsage.CPU_Only:
+ requiredFlags |= MemoryPropertyFlags.MemoryPropertyHostVisibleBit | MemoryPropertyFlags.MemoryPropertyHostCoherentBit;
+ break;
+ case MemoryUsage.CPU_To_GPU:
+ requiredFlags |= MemoryPropertyFlags.MemoryPropertyHostVisibleBit;
+ if (!this.IsIntegratedGPU || (preferredFlags & MemoryPropertyFlags.MemoryPropertyHostVisibleBit) == 0)
+ {
+ preferredFlags |= MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
+ }
+ break;
+ case MemoryUsage.GPU_To_CPU:
+ requiredFlags |= MemoryPropertyFlags.MemoryPropertyHostVisibleBit;
+ preferredFlags |= MemoryPropertyFlags.MemoryPropertyHostCachedBit;
+ break;
+ case MemoryUsage.CPU_Copy:
+ notPreferredFlags |= MemoryPropertyFlags.MemoryPropertyDeviceLocalBit;
+ break;
+ case MemoryUsage.GPU_LazilyAllocated:
+ requiredFlags |= MemoryPropertyFlags.MemoryPropertyLazilyAllocatedBit;
+ break;
+ default:
+ throw new ArgumentException("Invalid Usage Flags");
+ }
+
+ if (((allocInfo.RequiredFlags | allocInfo.PreferredFlags) & (MemoryPropertyFlags.MemoryPropertyDeviceCoherentBitAmd | MemoryPropertyFlags.MemoryPropertyDeviceUncachedBitAmd)) == 0)
+ {
+ notPreferredFlags |= MemoryPropertyFlags.MemoryPropertyDeviceCoherentBitAmd;
+ }
+
+ int? memoryTypeIndex = null;
+ int minCost = int.MaxValue;
+ uint memTypeBit = 1;
+
+ for (int memTypeIndex = 0; memTypeIndex < this.MemoryTypeCount; ++memTypeIndex, memTypeBit <<= 1)
+ {
+ if ((memTypeBit & memoryTypeBits) == 0)
+ continue;
+
+ var currFlags = this.MemoryTypes[memTypeIndex].PropertyFlags;
+
+ if ((requiredFlags & ~currFlags) != 0)
+ continue;
+
+ int currCost = BitOperations.PopCount((uint)(preferredFlags & ~currFlags));
+
+ currCost += BitOperations.PopCount((uint)(currFlags & notPreferredFlags));
+
+ if (currCost < minCost)
+ {
+ if (currCost == 0)
+ {
+ return memTypeIndex;
+ }
+
+ memoryTypeIndex = memTypeIndex;
+ minCost = currCost;
+ }
+ }
+
+ return memoryTypeIndex;
+ }
+
+ public int? FindMemoryTypeIndexForBufferInfo(in BufferCreateInfo bufferInfo, in AllocationCreateInfo allocInfo)
+ {
+ Buffer buffer;
+ fixed (BufferCreateInfo* pBufferInfo = &bufferInfo)
+ {
+ var res = VkApi.CreateBuffer(this.Device, pBufferInfo, null, &buffer);
+
+ if (res != Result.Success)
+ {
+ throw new VulkanResultException(res);
+ }
+ }
+
+ MemoryRequirements memReq;
+ VkApi.GetBufferMemoryRequirements(this.Device, buffer, &memReq);
+
+ var tmp = this.FindMemoryTypeIndex(memReq.MemoryTypeBits, in allocInfo);
+
+ VkApi.DestroyBuffer(this.Device, buffer, null);
+
+ return tmp;
+ }
+
+ public int? FindMemoryTypeIndexForImageInfo(in ImageCreateInfo imageInfo, in AllocationCreateInfo allocInfo)
+ {
+ Image image;
+ fixed (ImageCreateInfo* pImageInfo = &imageInfo)
+ {
+ var res = VkApi.CreateImage(this.Device, pImageInfo, null, &image);
+
+ if (res != Result.Success)
+ {
+ throw new VulkanResultException(res);
+ }
+ }
+
+ MemoryRequirements memReq;
+ VkApi.GetImageMemoryRequirements(this.Device, image, &memReq);
+
+ var tmp = this.FindMemoryTypeIndex(memReq.MemoryTypeBits, in allocInfo);
+
+ VkApi.DestroyImage(this.Device, image, null);
+
+ return tmp;
+ }
+
+ ///
+ /// Allocate a block of memory
+ ///
+ /// Memory Requirements for the allocation
+ /// Allocation Creation information
+ /// An object representing the allocation
+ public Allocation AllocateMemory(in MemoryRequirements requirements, in AllocationCreateInfo createInfo)
+ {
+ DedicatedAllocationInfo dedicatedInfo = DedicatedAllocationInfo.Default;
+
+ return this.AllocateMemory(in requirements, in dedicatedInfo, in createInfo, SuballocationType.Unknown);
+ }
+
+ ///
+ /// Allocate a block of memory with the memory requirements of ,
+ /// optionally binding it to the allocation
+ ///
+ ///
+ ///
+ /// Whether to bind to the allocation
+ ///
+ public Allocation AllocateMemoryForBuffer(Buffer buffer, in AllocationCreateInfo createInfo, bool BindToBuffer = false)
+ {
+ DedicatedAllocationInfo dedicatedInfo = DedicatedAllocationInfo.Default;
+
+ dedicatedInfo.DedicatedBuffer = buffer;
+
+ this.GetBufferMemoryRequirements(buffer, out MemoryRequirements memReq, out dedicatedInfo.RequiresDedicatedAllocation, out dedicatedInfo.PrefersDedicatedAllocation);
+
+ var alloc = this.AllocateMemory(in memReq, in dedicatedInfo, in createInfo, SuballocationType.Buffer);
+
+ if (BindToBuffer)
+ {
+ alloc.BindBufferMemory(buffer);
+ }
+
+ return alloc;
+ }
+
+ ///
+ /// Allocate a block of memory with the memory requirements of ,
+ /// optionally binding it to the allocation
+ ///
+ ///
+ ///
+ /// Whether to bind to the allocation
+ ///
+ public Allocation AllocateMemoryForImage(Image image, in AllocationCreateInfo createInfo, bool BindToImage = false)
+ {
+ DedicatedAllocationInfo dedicatedInfo = DedicatedAllocationInfo.Default;
+
+ dedicatedInfo.DedicatedImage = image;
+
+ this.GetImageMemoryRequirements(image, out var memReq, out dedicatedInfo.RequiresDedicatedAllocation, out dedicatedInfo.PrefersDedicatedAllocation);
+
+ var alloc = this.AllocateMemory(in memReq, in dedicatedInfo, in createInfo, SuballocationType.Image_Unknown);
+
+ if (BindToImage)
+ {
+ alloc.BindImageMemory(image);
+ }
+
+ return alloc;
+ }
+
+ public Result CheckCorruption(uint memoryTypeBits)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Buffer CreateBuffer(in BufferCreateInfo bufferInfo, in AllocationCreateInfo allocInfo, out Allocation allocation)
+ {
+ Result res;
+ Buffer buffer;
+
+ Allocation? alloc = null;
+
+ fixed (BufferCreateInfo* pInfo = &bufferInfo)
+ {
+ res = VkApi.CreateBuffer(this.Device, pInfo, null, &buffer);
+
+ if (res < 0)
+ {
+ throw new AllocationException("Buffer creation failed", res);
+ }
+ }
+
+ try
+ {
+ DedicatedAllocationInfo dedicatedInfo = default;
+
+ dedicatedInfo.DedicatedBuffer = buffer;
+ dedicatedInfo.DedicatedBufferUsage = bufferInfo.Usage;
+
+ this.GetBufferMemoryRequirements(buffer, out MemoryRequirements memReq, out dedicatedInfo.RequiresDedicatedAllocation, out dedicatedInfo.PrefersDedicatedAllocation);
+
+ alloc = this.AllocateMemory(in memReq, in dedicatedInfo, in allocInfo, SuballocationType.Buffer);
+ }
+ catch
+ {
+ VkApi.DestroyBuffer(this.Device, buffer, null);
+ throw;
+ }
+
+ if ((allocInfo.Flags & AllocationCreateFlags.DontBind) == 0)
+ {
+ res = alloc.BindBufferMemory(buffer);
+
+ if (res != Result.Success)
+ {
+ VkApi.DestroyBuffer(this.Device, buffer, null);
+ alloc.Dispose();
+
+ throw new AllocationException("Unable to bind memory to buffer", res);
+ }
+ }
+
+ allocation = alloc;
+
+ return buffer;
+ }
+
+ ///
+ /// Create a vulkan image and allocate a block of memory to go with it.
+ /// Unless specified otherwise with , the Image will be bound to the memory for you.
+ ///
+ /// Information to create the image
+ /// Information to allocate memory for the image
+ /// The object corresponding to the allocation
+ /// The created image
+ ///
+ public Image CreateImage(in ImageCreateInfo imageInfo, in AllocationCreateInfo allocInfo, out Allocation allocation)
+ {
+ if (imageInfo.Extent.Width == 0 ||
+ imageInfo.Extent.Height == 0 ||
+ imageInfo.Extent.Depth == 0 ||
+ imageInfo.MipLevels == 0 ||
+ imageInfo.ArrayLayers == 0)
+ {
+ throw new ArgumentException("Invalid Image Info");
+ }
+
+ Result res;
+ Image image;
+ Allocation alloc;
+
+ fixed (ImageCreateInfo* pInfo = &imageInfo)
+ {
+ res = VkApi.CreateImage(this.Device, pInfo, null, &image);
+
+ if (res < 0)
+ {
+ throw new AllocationException("Image creation failed", res);
+ }
+ }
+
+ SuballocationType suballocType = imageInfo.Tiling == ImageTiling.Optimal ? SuballocationType.Image_Optimal : SuballocationType.Image_Linear;
+
+ try
+ {
+ alloc = this.AllocateMemoryForImage(image, allocInfo);
+ }
+ catch
+ {
+ VkApi.DestroyImage(this.Device, image, null);
+ throw;
+ }
+
+ if ((allocInfo.Flags & AllocationCreateFlags.DontBind) == 0)
+ {
+ res = alloc.BindImageMemory(image);
+
+ if (res != Result.Success)
+ {
+ VkApi.DestroyImage(this.Device, image, null);
+ alloc.Dispose();
+
+ throw new AllocationException("Unable to Bind memory to image", res);
+ }
+ }
+
+ allocation = alloc;
+
+ return image;
+ }
+
+ private ref PhysicalDeviceMemoryProperties.MemoryTypesBuffer MemoryTypes
+ {
+ get => ref this.memoryProperties.MemoryTypes;
+ }
+
+ private ref PhysicalDeviceMemoryProperties.MemoryHeapsBuffer MemoryHeaps
+ {
+ get => ref this.memoryProperties.MemoryHeaps;
+ }
+
+ internal int MemoryTypeIndexToHeapIndex(int typeIndex)
+ {
+ Debug.Assert(typeIndex < this.MemoryProperties.MemoryTypeCount);
+ return (int)MemoryTypes[typeIndex].HeapIndex;
+ }
+
+ internal bool IsMemoryTypeNonCoherent(int memTypeIndex)
+ {
+ return (MemoryTypes[memTypeIndex].PropertyFlags & (MemoryPropertyFlags.MemoryPropertyHostVisibleBit | MemoryPropertyFlags.MemoryPropertyHostCoherentBit)) == MemoryPropertyFlags.MemoryPropertyHostVisibleBit;
+ }
+
+ internal long GetMemoryTypeMinAlignment(int memTypeIndex)
+ {
+ return IsMemoryTypeNonCoherent(memTypeIndex) ? (long)Math.Max(1, this.PhysicalDeviceProperties.Limits.NonCoherentAtomSize) : 1;
+ }
+
+ internal void GetBufferMemoryRequirements(Buffer buffer, out MemoryRequirements memReq, out bool requiresDedicatedAllocation, out bool prefersDedicatedAllocation)
+ {
+ var req = new BufferMemoryRequirementsInfo2
+ {
+ SType = StructureType.BufferMemoryRequirementsInfo2,
+ Buffer = buffer
+ };
+
+ var dedicatedRequirements = new MemoryDedicatedRequirements
+ {
+ SType = StructureType.MemoryDedicatedRequirements,
+ };
+
+ var memReq2 = new MemoryRequirements2
+ {
+ SType = StructureType.MemoryRequirements2,
+ PNext = &dedicatedRequirements
+ };
+
+ VkApi.GetBufferMemoryRequirements2(this.Device, &req, &memReq2);
+
+ memReq = memReq2.MemoryRequirements;
+ requiresDedicatedAllocation = dedicatedRequirements.RequiresDedicatedAllocation != 0;
+ prefersDedicatedAllocation = dedicatedRequirements.PrefersDedicatedAllocation != 0;
+ }
+
+ internal void GetImageMemoryRequirements(Image image, out MemoryRequirements memReq, out bool requiresDedicatedAllocation, out bool prefersDedicatedAllocation)
+ {
+ var req = new ImageMemoryRequirementsInfo2
+ {
+ SType = StructureType.ImageMemoryRequirementsInfo2,
+ Image = image
+ };
+
+ var dedicatedRequirements = new MemoryDedicatedRequirements
+ {
+ SType = StructureType.MemoryDedicatedRequirements,
+ };
+
+ var memReq2 = new MemoryRequirements2
+ {
+ SType = StructureType.MemoryRequirements2,
+ PNext = &dedicatedRequirements
+ };
+
+ VkApi.GetImageMemoryRequirements2(this.Device, &req, &memReq2);
+
+ memReq = memReq2.MemoryRequirements;
+ requiresDedicatedAllocation = dedicatedRequirements.RequiresDedicatedAllocation != 0;
+ prefersDedicatedAllocation = dedicatedRequirements.PrefersDedicatedAllocation != 0;
+ }
+
+ internal Allocation AllocateMemory(in MemoryRequirements memReq, in DedicatedAllocationInfo dedicatedInfo,
+ in AllocationCreateInfo createInfo, SuballocationType suballocType)
+ {
+ Debug.Assert(Helpers.IsPow2((long)memReq.Alignment));
+
+ if (memReq.Size == 0)
+ throw new ArgumentException("Allocation size cannot be 0");
+
+ const AllocationCreateFlags CheckFlags1 = AllocationCreateFlags.DedicatedMemory | AllocationCreateFlags.NeverAllocate;
+ const AllocationCreateFlags CheckFlags2 = AllocationCreateFlags.Mapped | AllocationCreateFlags.CanBecomeLost;
+
+ if ((createInfo.Flags & CheckFlags1) == CheckFlags1)
+ {
+ throw new ArgumentException("Specifying AllocationCreateFlags.DedicatedMemory with AllocationCreateFlags.NeverAllocate is invalid");
+ }
+ else if ((createInfo.Flags & CheckFlags2) == CheckFlags2)
+ {
+ throw new ArgumentException("Specifying AllocationCreateFlags.Mapped with AllocationCreateFlags.CanBecomeLost is invalid");
+ }
+
+ if (dedicatedInfo.RequiresDedicatedAllocation)
+ {
+ if ((createInfo.Flags & AllocationCreateFlags.NeverAllocate) != 0)
+ {
+ throw new AllocationException("AllocationCreateFlags.NeverAllocate specified while dedicated allocation required", Result.ErrorOutOfDeviceMemory);
+ }
+
+ if (createInfo.Pool != null)
+ {
+ throw new ArgumentException("Pool specified while dedicated allocation required");
+ }
+ }
+
+ if (createInfo.Pool != null && (createInfo.Flags & AllocationCreateFlags.DedicatedMemory) != 0)
+ {
+ throw new ArgumentException("Specified AllocationCreateFlags.DedicatedMemory when createInfo.Pool is not null");
+ }
+
+ if (createInfo.Pool != null)
+ {
+ int memoryTypeIndex = createInfo.Pool.BlockList.MemoryTypeIndex;
+ long alignmentForPool = Math.Max((long)memReq.Alignment, this.GetMemoryTypeMinAlignment(memoryTypeIndex));
+
+ AllocationCreateInfo infoForPool = createInfo;
+
+ if ((createInfo.Flags & AllocationCreateFlags.Mapped) != 0 && (this.MemoryTypes[memoryTypeIndex].PropertyFlags & MemoryPropertyFlags.MemoryPropertyHostVisibleBit) == 0)
+ {
+ infoForPool.Flags &= ~AllocationCreateFlags.Mapped;
+ }
+
+ return createInfo.Pool.BlockList.Allocate(this.CurrentFrameIndex, (long)memReq.Size, alignmentForPool, infoForPool, suballocType);
+ }
+ else
+ {
+ uint memoryTypeBits = memReq.MemoryTypeBits;
+ var typeIndex = this.FindMemoryTypeIndex(memoryTypeBits, createInfo);
+
+ if (typeIndex == null)
+ {
+ throw new AllocationException("Unable to find suitable memory type for allocation", Result.ErrorFeatureNotPresent);
+ }
+
+ long alignmentForType = Math.Max((long)memReq.Alignment, this.GetMemoryTypeMinAlignment((int)typeIndex));
+
+ return this.AllocateMemoryOfType((long)memReq.Size, alignmentForType, in dedicatedInfo, in createInfo, (int)typeIndex, suballocType);
+ }
+ }
+
+ public void FreeMemory(Allocation allocation)
+ {
+ if (allocation is null)
+ {
+ throw new ArgumentNullException(nameof(allocation));
+ }
+
+ if (allocation.TouchAllocation())
+ {
+ if (allocation is BlockAllocation blockAlloc)
+ {
+ BlockList list;
+ var pool = blockAlloc.Block.ParentPool;
+
+ if (pool != null)
+ {
+ list = pool.BlockList;
+ }
+ else
+ {
+ list = this.BlockLists[allocation.MemoryTypeIndex];
+ Debug.Assert(list != null);
+ }
+
+ list.Free(allocation);
+ }
+ else
+ {
+ var dedicated = allocation as DedicatedAllocation;
+
+ Debug.Assert(dedicated != null);
+
+ FreeDedicatedMemory(dedicated);
+ }
+ }
+
+ this.Budget.RemoveAllocation(this.MemoryTypeIndexToHeapIndex(allocation.MemoryTypeIndex), allocation.Size);
+ }
+
+ public Stats CalculateStats()
+ {
+ var newStats = new Stats();
+
+ for (int i = 0; i < this.MemoryTypeCount; ++i)
+ {
+ var list = this.BlockLists[i];
+
+ Debug.Assert(list != null);
+
+ list.AddStats(newStats);
+ }
+
+ this.PoolsMutex.EnterReadLock();
+ try
+ {
+ foreach (var pool in this.Pools)
+ {
+ pool.BlockList.AddStats(newStats);
+ }
+ }
+ finally
+ {
+ this.PoolsMutex.ExitReadLock();
+ }
+
+ for (int typeIndex = 0; typeIndex < this.MemoryTypeCount; ++typeIndex)
+ {
+ int heapIndex = this.MemoryTypeIndexToHeapIndex(typeIndex);
+
+ ref DedicatedAllocationHandler handler = ref DedicatedAllocations[typeIndex];
+
+ handler.Mutex.EnterReadLock();
+
+ try
+ {
+ foreach (var alloc in handler.Allocations)
+ {
+ ((DedicatedAllocation)alloc).CalcStatsInfo(out var stat);
+
+ StatInfo.Add(ref newStats.Total, stat);
+ StatInfo.Add(ref newStats.MemoryType[typeIndex], stat);
+ StatInfo.Add(ref newStats.MemoryHeap[heapIndex], stat);
+ }
+ }
+ finally
+ {
+ handler.Mutex.ExitReadLock();
+ }
+ }
+
+ newStats.PostProcess();
+
+ return newStats;
+ }
+
+ internal void GetBudget(int heapIndex, out AllocationBudget outBudget)
+ {
+ Unsafe.SkipInit(out outBudget);
+
+ if ((uint)heapIndex >= Vk.MaxMemoryHeaps)
+ {
+ throw new ArgumentOutOfRangeException(nameof(heapIndex));
+ }
+
+ if (this.UseExtMemoryBudget)
+ {
+ //Reworked to remove recursion
+ if (this.Budget.OperationsSinceBudgetFetch >= 30)
+ {
+ this.UpdateVulkanBudget(); //Outside of mutex lock
+ }
+
+ this.Budget.BudgetMutex.EnterReadLock();
+ try
+ {
+ ref var heapBudget = ref this.Budget.BudgetData[heapIndex];
+
+ outBudget.BlockBytes = heapBudget.BlockBytes;
+ outBudget.AllocationBytes = heapBudget.AllocationBytes;
+
+ if (heapBudget.VulkanUsage + outBudget.BlockBytes > heapBudget.BlockBytesAtBudgetFetch)
+ {
+ outBudget.Usage = heapBudget.VulkanUsage + outBudget.BlockBytes - heapBudget.BlockBytesAtBudgetFetch;
+ }
+ else
+ {
+ outBudget.Usage = 0;
+ }
+
+ outBudget.Budget = Math.Min(heapBudget.VulkanBudget, (long)this.MemoryHeaps[heapIndex].Size);
+ }
+ finally
+ {
+ this.Budget.BudgetMutex.ExitReadLock();
+ }
+ }
+ else
+ {
+ ref var heapBudget = ref this.Budget.BudgetData[heapIndex];
+
+ outBudget.BlockBytes = heapBudget.BlockBytes;
+ outBudget.AllocationBytes = heapBudget.AllocationBytes;
+
+ outBudget.Usage = heapBudget.BlockBytes;
+ outBudget.Budget = (long)(this.MemoryHeaps[heapIndex].Size * 8 / 10); //80% heuristics
+ }
+ }
+
+ internal void GetBudget(int firstHeap, AllocationBudget[] outBudgets)
+ {
+ Debug.Assert(outBudgets != null && outBudgets.Length != 0);
+ Array.Clear(outBudgets, 0, outBudgets.Length);
+
+ if ((uint)(outBudgets.Length + firstHeap) >= Vk.MaxMemoryHeaps)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if (this.UseExtMemoryBudget)
+ {
+ //Reworked to remove recursion
+ if (this.Budget.OperationsSinceBudgetFetch >= 30)
+ {
+ this.UpdateVulkanBudget(); //Outside of mutex lock
+ }
+
+ this.Budget.BudgetMutex.EnterReadLock();
+ try
+ {
+ for (int i = 0; i < outBudgets.Length; ++i)
+ {
+ int heapIndex = i + firstHeap;
+ ref AllocationBudget outBudget = ref outBudgets[i];
+
+ ref var heapBudget = ref this.Budget.BudgetData[heapIndex];
+
+ outBudget.BlockBytes = heapBudget.BlockBytes;
+ outBudget.AllocationBytes = heapBudget.AllocationBytes;
+
+ if (heapBudget.VulkanUsage + outBudget.BlockBytes > heapBudget.BlockBytesAtBudgetFetch)
+ {
+ outBudget.Usage = heapBudget.VulkanUsage + outBudget.BlockBytes - heapBudget.BlockBytesAtBudgetFetch;
+ }
+ else
+ {
+ outBudget.Usage = 0;
+ }
+
+ outBudget.Budget = Math.Min(heapBudget.VulkanBudget, (long)this.MemoryHeaps[heapIndex].Size);
+ }
+ }
+ finally
+ {
+ this.Budget.BudgetMutex.ExitReadLock();
+ }
+ }
+ else
+ {
+ for (int i = 0; i < outBudgets.Length; ++i)
+ {
+ int heapIndex = i + firstHeap;
+ ref AllocationBudget outBudget = ref outBudgets[i];
+ ref var heapBudget = ref this.Budget.BudgetData[heapIndex];
+
+ outBudget.BlockBytes = heapBudget.BlockBytes;
+ outBudget.AllocationBytes = heapBudget.AllocationBytes;
+
+ outBudget.Usage = heapBudget.BlockBytes;
+ outBudget.Budget = (long)(this.MemoryHeaps[heapIndex].Size * 8 / 10); //80% heuristics
+ }
+ }
+ }
+
+ internal Result DefragmentationBegin(in DefragmentationInfo2 info, DefragmentationStats stats, DefragmentationContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result DefragmentationEnd(DefragmentationContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result DefragmentationPassBegin(ref DefragmentationPassMoveInfo[] passInfo, DefragmentationContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result DefragmentationPassEnd(DefragmentationContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ public VulkanMemoryPool CreatePool(in AllocationPoolCreateInfo createInfo)
+ {
+ var tmpCreateInfo = createInfo;
+
+ if (tmpCreateInfo.MaxBlockCount == 0)
+ {
+ tmpCreateInfo.MaxBlockCount = int.MaxValue;
+ }
+
+ if (tmpCreateInfo.MinBlockCount > tmpCreateInfo.MaxBlockCount)
+ {
+ throw new ArgumentException("Min block count is higher than max block count");
+ }
+
+ if (tmpCreateInfo.MemoryTypeIndex >= this.MemoryTypeCount || ((1u << tmpCreateInfo.MemoryTypeIndex) & this.GlobalMemoryTypeBits) == 0)
+ {
+ throw new ArgumentException("Invalid memory type index");
+ }
+
+ long preferredBlockSize = this.CalcPreferredBlockSize(tmpCreateInfo.MemoryTypeIndex);
+
+ var pool = new VulkanMemoryPool(this, tmpCreateInfo, preferredBlockSize);
+
+ this.PoolsMutex.EnterWriteLock();
+ try
+ {
+ this.Pools.Add(pool);
+ }
+ finally
+ {
+ this.PoolsMutex.ExitWriteLock();
+ }
+
+ return pool;
+ }
+
+ internal void DestroyPool(VulkanMemoryPool pool)
+ {
+ this.PoolsMutex.EnterWriteLock();
+ try
+ {
+ bool success = this.Pools.Remove(pool);
+ Debug.Assert(success, "Pool not found in allocator");
+ }
+ finally
+ {
+ this.PoolsMutex.ExitWriteLock();
+ }
+ }
+
+ internal void GetPoolStats(VulkanMemoryPool pool, out PoolStats stats)
+ {
+ pool.BlockList.GetPoolStats(out stats);
+ }
+
+ internal int MakePoolAllocationsLost(VulkanMemoryPool pool)
+ {
+ return pool.BlockList.MakePoolAllocationsLost(this.CurrentFrame);
+ }
+
+ internal Result CheckPoolCorruption(VulkanMemoryPool pool)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Allocation CreateLostAllocation()
+ {
+ throw new NotImplementedException();
+ }
+
+ internal Result AllocateVulkanMemory(in MemoryAllocateInfo allocInfo, out DeviceMemory memory)
+ {
+ int heapIndex = this.MemoryTypeIndexToHeapIndex((int)allocInfo.MemoryTypeIndex);
+ ref var budgetData = ref this.Budget.BudgetData[heapIndex];
+
+ if ((this.HeapSizeLimitMask & (1u << heapIndex)) != 0)
+ {
+ long heapSize, blockBytes, blockBytesAfterAlloc;
+
+ heapSize = (long)this.MemoryHeaps[heapIndex].Size;
+
+ do
+ {
+ blockBytes = budgetData.BlockBytes;
+ blockBytesAfterAlloc = blockBytes + (long)allocInfo.AllocationSize;
+
+ if (blockBytesAfterAlloc > heapSize)
+ {
+ throw new AllocationException("Budget limit reached for heap index " + heapIndex, Result.ErrorOutOfDeviceMemory);
+ }
+ }
+ while (Interlocked.CompareExchange(ref budgetData.BlockBytes, blockBytesAfterAlloc, blockBytes) != blockBytes);
+ }
+ else
+ {
+ Interlocked.Add(ref budgetData.BlockBytes, (long)allocInfo.AllocationSize);
+ }
+
+ fixed (MemoryAllocateInfo* pInfo = &allocInfo)
+ fixed (DeviceMemory* pMemory = &memory)
+ {
+ var res = VkApi.AllocateMemory(this.Device, pInfo, null, pMemory);
+
+ if (res == Result.Success)
+ {
+ Interlocked.Increment(ref this.Budget.OperationsSinceBudgetFetch);
+ }
+ else
+ {
+ Interlocked.Add(ref budgetData.BlockBytes, -(long)allocInfo.AllocationSize);
+ }
+
+ return res;
+ }
+ }
+
+ internal void FreeVulkanMemory(int memoryType, long size, DeviceMemory memory)
+ {
+ VkApi.FreeMemory(this.Device, memory, null);
+
+ Interlocked.Add(ref this.Budget.BudgetData[this.MemoryTypeIndexToHeapIndex(memoryType)].BlockBytes, -size);
+ }
+
+ internal Result BindVulkanBuffer(Buffer buffer, DeviceMemory memory, long offset, void* pNext)
+ {
+ if (pNext != null)
+ {
+ var info = new BindBufferMemoryInfo(pNext: pNext, buffer: buffer, memory: memory, memoryOffset: (ulong)offset);
+
+ return VkApi.BindBufferMemory2(this.Device, 1, &info);
+ }
+ else
+ {
+ return VkApi.BindBufferMemory(this.Device, buffer, memory, (ulong)offset);
+ }
+ }
+
+ internal Result BindVulkanImage(Image image, DeviceMemory memory, long offset, void* pNext)
+ {
+ if (pNext != default)
+ {
+ var info = new BindImageMemoryInfo
+ {
+ SType = StructureType.BindBufferMemoryInfo,
+ PNext = pNext,
+ Image = image,
+ Memory = memory,
+ MemoryOffset = (ulong)offset
+ };
+
+ return VkApi.BindImageMemory2(this.Device, 1, &info);
+ }
+ else
+ {
+ return VkApi.BindImageMemory(this.Device, image, memory, (ulong)offset);
+ }
+ }
+
+ internal void FillAllocation(Allocation allocation, byte pattern)
+ {
+ if (Helpers.DebugInitializeAllocations && !allocation.CanBecomeLost &&
+ (this.MemoryTypes[allocation.MemoryTypeIndex].PropertyFlags & MemoryPropertyFlags.MemoryPropertyHostVisibleBit) != 0)
+ {
+ IntPtr pData = allocation.Map();
+
+ Unsafe.InitBlockUnaligned(ref *(byte*)pData, pattern, (uint)allocation.Size);
+
+ this.FlushOrInvalidateAllocation(allocation, 0, long.MaxValue, CacheOperation.Flush);
+
+ allocation.Unmap();
+ }
+ }
+
+ internal uint GetGPUDefragmentationMemoryTypeBits()
+ {
+ uint memTypeBits = this.GPUDefragmentationMemoryTypeBits;
+ if (memTypeBits == uint.MaxValue)
+ {
+ memTypeBits = this.CalculateGpuDefragmentationMemoryTypeBits();
+ this.GPUDefragmentationMemoryTypeBits = memTypeBits;
+ }
+ return memTypeBits;
+ }
+
+ private long CalcPreferredBlockSize(int memTypeIndex)
+ {
+ var heapIndex = this.MemoryTypeIndexToHeapIndex(memTypeIndex);
+
+ Debug.Assert((uint)heapIndex < Vk.MaxMemoryHeaps);
+
+ var heapSize = (long)MemoryHeaps[heapIndex].Size;
+
+ return Helpers.AlignUp(heapSize <= SmallHeapMaxSize ? (heapSize / 8) : this.PreferredLargeHeapBlockSize, 32);
+ }
+
+ private Allocation AllocateMemoryOfType(long size, long alignment, in DedicatedAllocationInfo dedicatedInfo, in AllocationCreateInfo createInfo,
+ int memoryTypeIndex, SuballocationType suballocType)
+ {
+ var finalCreateInfo = createInfo;
+
+ if ((finalCreateInfo.Flags & AllocationCreateFlags.Mapped) != 0 && (this.MemoryTypes[memoryTypeIndex].PropertyFlags & MemoryPropertyFlags.MemoryPropertyHostVisibleBit) == 0)
+ {
+ finalCreateInfo.Flags &= ~AllocationCreateFlags.Mapped;
+ }
+
+ if (finalCreateInfo.Usage == MemoryUsage.GPU_LazilyAllocated)
+ {
+ finalCreateInfo.Flags |= AllocationCreateFlags.DedicatedMemory;
+ }
+
+ var blockList = this.BlockLists[memoryTypeIndex];
+
+ long preferredBlockSize = blockList.PreferredBlockSize;
+ bool preferDedicatedMemory = (dedicatedInfo.RequiresDedicatedAllocation | dedicatedInfo.PrefersDedicatedAllocation) || size > preferredBlockSize / 2;
+
+ if (preferDedicatedMemory && (finalCreateInfo.Flags & AllocationCreateFlags.NeverAllocate) == 0 && finalCreateInfo.Pool == null)
+ {
+ finalCreateInfo.Flags |= AllocationCreateFlags.DedicatedMemory;
+ }
+
+ Exception? blockAllocException = null;
+
+ if ((finalCreateInfo.Flags & AllocationCreateFlags.DedicatedMemory) == 0)
+ {
+ try
+ {
+ return blockList.Allocate(this.CurrentFrameIndex, size, alignment, finalCreateInfo, suballocType);
+ }
+ catch (Exception e)
+ {
+ blockAllocException = e;
+ }
+ }
+
+ //Try a dedicated allocation if a block allocation failed, or if specified as a dedicated allocation
+ if ((finalCreateInfo.Flags & AllocationCreateFlags.NeverAllocate) != 0)
+ {
+ throw new AllocationException("Block List allocation failed, and `AllocationCreateFlags.NeverAllocate` specified", blockAllocException);
+ }
+
+ return this.AllocateDedicatedMemory(size, suballocType, memoryTypeIndex,
+ (finalCreateInfo.Flags & AllocationCreateFlags.WithinBudget) != 0,
+ (finalCreateInfo.Flags & AllocationCreateFlags.Mapped) != 0,
+ finalCreateInfo.UserData, in dedicatedInfo);
+ }
+
+ private Allocation AllocateDedicatedMemoryPage(
+ long size, SuballocationType suballocType, int memTypeIndex, in MemoryAllocateInfo allocInfo, bool map,
+ object? userData)
+ {
+ var res = this.AllocateVulkanMemory(in allocInfo, out var memory);
+
+ if (res < 0)
+ {
+ throw new AllocationException("Dedicated memory allocation Failed", res);
+ }
+
+ IntPtr mappedData = default;
+ if (map)
+ {
+ res = VkApi.MapMemory(this.Device, memory, 0, Vk.WholeSize, 0, (void**)&mappedData);
+
+ if (res < 0)
+ {
+ this.FreeVulkanMemory(memTypeIndex, size, memory);
+
+ throw new AllocationException("Unable to map dedicated allocation", res);
+ }
+ }
+
+ var allocation = new DedicatedAllocation(this, memTypeIndex, memory, suballocType, mappedData, size)
+ {
+ UserData = userData
+ };
+
+ this.Budget.AddAllocation(this.MemoryTypeIndexToHeapIndex(memTypeIndex), size);
+
+ this.FillAllocation(allocation, Helpers.AllocationFillPattern_Created);
+
+ return allocation;
+ }
+
+ private Allocation AllocateDedicatedMemory(long size, SuballocationType suballocType, int memTypeIndex, bool withinBudget, bool map, object? userData, in DedicatedAllocationInfo dedicatedInfo)
+ {
+ int heapIndex = this.MemoryTypeIndexToHeapIndex(memTypeIndex);
+
+ if (withinBudget)
+ {
+ this.GetBudget(heapIndex, out var budget);
+ if (budget.Usage + size > budget.Budget)
+ {
+ throw new AllocationException("Memory Budget limit reached for heap index " + heapIndex, Result.ErrorOutOfDeviceMemory);
+ }
+ }
+
+ MemoryAllocateInfo allocInfo = new MemoryAllocateInfo
+ {
+ SType = StructureType.MemoryAllocateInfo,
+ MemoryTypeIndex = (uint)memTypeIndex,
+ AllocationSize = (ulong)size
+ };
+
+ Debug.Assert(!(dedicatedInfo.DedicatedBuffer.Handle != default && dedicatedInfo.DedicatedImage.Handle != default), "dedicated buffer and dedicated image were both specified");
+
+ var dedicatedAllocInfo = new MemoryDedicatedAllocateInfo(StructureType.MemoryDedicatedAllocateInfo);
+
+ if (dedicatedInfo.DedicatedBuffer.Handle != default)
+ {
+ dedicatedAllocInfo.Buffer = dedicatedInfo.DedicatedBuffer;
+ allocInfo.PNext = &dedicatedAllocInfo;
+ }
+ else if (dedicatedInfo.DedicatedImage.Handle != default)
+ {
+ dedicatedAllocInfo.Image = dedicatedInfo.DedicatedImage;
+ allocInfo.PNext = &dedicatedAllocInfo;
+ }
+
+ var allocFlagsInfo = new MemoryAllocateFlagsInfoKHR(StructureType.MemoryAllocateFlagsInfoKhr);
+ if (UseKhrBufferDeviceAddress)
+ {
+ bool canContainBufferWithDeviceAddress = true;
+
+ if (dedicatedInfo.DedicatedBuffer.Handle != default)
+ {
+ canContainBufferWithDeviceAddress = dedicatedInfo.DedicatedBufferUsage == UnknownBufferUsage
+ || (dedicatedInfo.DedicatedBufferUsage & BufferUsageFlags.BufferUsageShaderDeviceAddressBitKhr) != 0;
+ }
+ else if (dedicatedInfo.DedicatedImage.Handle != default)
+ {
+ canContainBufferWithDeviceAddress = false;
+ }
+
+ if (canContainBufferWithDeviceAddress)
+ {
+ allocFlagsInfo.Flags = MemoryAllocateFlags.MemoryAllocateDeviceAddressBit;
+ allocFlagsInfo.PNext = allocInfo.PNext;
+ allocInfo.PNext = &allocFlagsInfo;
+ }
+ }
+
+ var alloc = this.AllocateDedicatedMemoryPage(size, suballocType, memTypeIndex, in allocInfo, map, userData);
+
+ //Register made allocations
+ ref DedicatedAllocationHandler handler = ref this.DedicatedAllocations[memTypeIndex];
+
+ handler.Mutex.EnterWriteLock();
+ try
+ {
+ handler.Allocations.InsertSorted(alloc, (alloc1, alloc2) => alloc1.Offset.CompareTo(alloc2.Offset));
+ }
+ finally
+ {
+ handler.Mutex.ExitWriteLock();
+ }
+
+ return alloc;
+ }
+
+ private void FreeDedicatedMemory(DedicatedAllocation allocation)
+ {
+ ref DedicatedAllocationHandler handler = ref this.DedicatedAllocations[allocation.MemoryTypeIndex];
+
+ handler.Mutex.EnterWriteLock();
+
+ try
+ {
+ bool success = handler.Allocations.Remove(allocation);
+
+ Debug.Assert(success);
+ }
+ finally
+ {
+ handler.Mutex.ExitWriteLock();
+ }
+
+ FreeVulkanMemory(allocation.MemoryTypeIndex, allocation.Size, allocation.DeviceMemory);
+ }
+
+ private uint CalculateGpuDefragmentationMemoryTypeBits()
+ {
+ throw new NotImplementedException();
+ }
+
+ private const uint AMDVendorID = 0x1002;
+
+ private uint CalculateGlobalMemoryTypeBits()
+ {
+ Debug.Assert(this.MemoryTypeCount > 0);
+
+ uint memoryTypeBits = uint.MaxValue;
+
+ if (this.PhysicalDeviceProperties.VendorID == AMDVendorID && !this.UseAMDDeviceCoherentMemory)
+ {
+ // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
+ for (int index = 0; index < this.MemoryTypeCount; ++index)
+ {
+ if ((this.MemoryTypes[index].PropertyFlags & MemoryPropertyFlags.MemoryPropertyDeviceCoherentBitAmd) != 0)
+ {
+ memoryTypeBits &= ~(1u << index);
+ }
+ }
+ }
+
+ return memoryTypeBits;
+ }
+
+ private void UpdateVulkanBudget()
+ {
+ Debug.Assert(UseExtMemoryBudget);
+
+ PhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = new PhysicalDeviceMemoryBudgetPropertiesEXT(StructureType.PhysicalDeviceMemoryBudgetPropertiesExt);
+
+ PhysicalDeviceMemoryProperties2 memProps = new PhysicalDeviceMemoryProperties2(StructureType.PhysicalDeviceMemoryProperties2, &budgetProps);
+
+ VkApi.GetPhysicalDeviceMemoryProperties2(this.PhysicalDevice, &memProps);
+
+ Budget.BudgetMutex.EnterWriteLock();
+ try
+ {
+ for (int i = 0; i < MemoryHeapCount; ++i)
+ {
+ ref var data = ref Budget.BudgetData[i];
+
+ data.VulkanUsage = (long)budgetProps.HeapUsage[i];
+ data.VulkanBudget = (long)budgetProps.HeapBudget[i];
+
+ data.BlockBytesAtBudgetFetch = data.BlockBytes;
+
+ // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
+
+ ref var heap = ref this.MemoryHeaps[i];
+
+ if (data.VulkanBudget == 0)
+ {
+ data.VulkanBudget = (long)(heap.Size * 8 / 10);
+ }
+ else if ((ulong)data.VulkanBudget > heap.Size)
+ {
+ data.VulkanBudget = (long)heap.Size;
+ }
+
+ if (data.VulkanUsage == 0 && data.BlockBytesAtBudgetFetch > 0)
+ {
+ data.VulkanUsage = data.BlockBytesAtBudgetFetch;
+ }
+ }
+
+ Budget.OperationsSinceBudgetFetch = 0;
+ }
+ finally
+ {
+ Budget.BudgetMutex.ExitWriteLock();
+ }
+ }
+
+ internal Result FlushOrInvalidateAllocation(Allocation allocation, long offset, long size, CacheOperation op)
+ {
+ int memTypeIndex = allocation.MemoryTypeIndex;
+ if (size > 0 && this.IsMemoryTypeNonCoherent(memTypeIndex))
+ {
+ long allocSize = allocation.Size;
+
+ Debug.Assert((ulong)offset <= (ulong)allocSize);
+
+ var nonCoherentAtomSize = (long)this.physicalDeviceProperties.Limits.NonCoherentAtomSize;
+
+ MappedMemoryRange memRange = new MappedMemoryRange(memory: allocation.DeviceMemory);
+
+ if (allocation is BlockAllocation blockAlloc)
+ {
+ memRange.Offset = (ulong)Helpers.AlignDown(offset, nonCoherentAtomSize);
+
+ if (size == long.MaxValue)
+ {
+ size = allocSize - offset;
+ }
+ else
+ {
+ Debug.Assert(offset + size <= allocSize);
+ }
+
+ memRange.Size = (ulong)Helpers.AlignUp(size + (offset - (long)memRange.Offset), nonCoherentAtomSize);
+
+ long allocOffset = blockAlloc.Offset;
+
+ Debug.Assert(allocOffset % nonCoherentAtomSize == 0);
+
+ long blockSize = blockAlloc.Block.MetaData.Size;
+
+ memRange.Offset += (ulong)allocOffset;
+ memRange.Size = Math.Min(memRange.Size, (ulong)blockSize - memRange.Offset);
+ }
+ else if (allocation is DedicatedAllocation)
+ {
+ memRange.Offset = (ulong)Helpers.AlignDown(offset, nonCoherentAtomSize);
+
+ if (size == long.MaxValue)
+ {
+ memRange.Size = (ulong)allocSize - memRange.Offset;
+ }
+ else
+ {
+ Debug.Assert(offset + size <= allocSize);
+
+ memRange.Size = (ulong)Helpers.AlignUp(size + (offset - (long)memRange.Offset), nonCoherentAtomSize);
+ }
+ }
+ else
+ {
+ Debug.Assert(false);
+ throw new ArgumentException("allocation type is not BlockAllocation or DedicatedAllocation");
+ }
+
+ switch (op)
+ {
+ case CacheOperation.Flush:
+ return VkApi.FlushMappedMemoryRanges(this.Device, 1, &memRange);
+ case CacheOperation.Invalidate:
+ return VkApi.InvalidateMappedMemoryRanges(this.Device, 1, &memRange);
+ default:
+ Debug.Assert(false);
+ throw new ArgumentException("Invalid Cache Operation value", nameof(op));
+ }
+ }
+
+ return Result.Success;
+ }
+
+ internal struct DedicatedAllocationHandler
+ {
+ public List Allocations;
+ public ReaderWriterLockSlim Mutex;
+ }
+
+ internal struct DedicatedAllocationInfo
+ {
+ public Buffer DedicatedBuffer;
+ public Image DedicatedImage;
+ public BufferUsageFlags DedicatedBufferUsage; //uint.MaxValue when unknown
+ public bool RequiresDedicatedAllocation;
+ public bool PrefersDedicatedAllocation;
+
+ public static readonly DedicatedAllocationInfo Default = new DedicatedAllocationInfo()
+ {
+ DedicatedBufferUsage = unchecked((BufferUsageFlags)uint.MaxValue)
+ };
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryBlock.cs b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryBlock.cs
new file mode 100644
index 0000000000..a224d6d605
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryBlock.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+
+using Silk.NET.Vulkan;
+using Buffer = Silk.NET.Vulkan.Buffer;
+
+namespace VMASharp
+{
+ using Metadata;
+
+ internal class VulkanMemoryBlock : IDisposable
+ {
+ private Vk VkApi => Allocator.VkApi;
+
+ private readonly VulkanMemoryAllocator Allocator;
+ internal readonly IBlockMetadata MetaData;
+ private readonly object SyncLock = new object();
+ private int mapCount;
+
+
+ public VulkanMemoryBlock(VulkanMemoryAllocator allocator, VulkanMemoryPool? pool, int memoryTypeIndex, DeviceMemory memory, uint id, IBlockMetadata metaObject)
+ {
+ Allocator = allocator;
+ ParentPool = pool;
+ MemoryTypeIndex = memoryTypeIndex;
+ DeviceMemory = memory;
+ ID = id;
+
+ MetaData = metaObject;
+ }
+
+ public VulkanMemoryPool? ParentPool { get; }
+
+ public DeviceMemory DeviceMemory { get; }
+
+ public int MemoryTypeIndex { get; }
+
+ public uint ID { get; }
+
+ public IntPtr MappedData { get; private set; }
+
+ public void Dispose()
+ {
+ if (!this.MetaData.IsEmpty)
+ {
+ throw new InvalidOperationException("Some allocations were not freed before destruction of this memory block!");
+ }
+
+ Debug.Assert(this.DeviceMemory.Handle != default);
+
+ this.Allocator.FreeVulkanMemory(this.MemoryTypeIndex, this.MetaData.Size, this.DeviceMemory);
+ }
+
+ [Conditional("DEBUG")]
+ public void Validate()
+ {
+ Helpers.Validate(this.DeviceMemory.Handle != default && this.MetaData.Size > 0);
+
+ MetaData.Validate();
+ }
+
+ public void CheckCorruption(VulkanMemoryAllocator allocator)
+ {
+ var data = this.Map(1);
+
+ try
+ {
+ this.MetaData.CheckCorruption((nuint)(nint)data);
+ }
+ finally
+ {
+ this.Unmap(1);
+ }
+ }
+
+ public unsafe IntPtr Map(int count)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+
+ lock (this.SyncLock)
+ {
+ Debug.Assert(this.mapCount >= 0);
+
+ if (this.mapCount > 0)
+ {
+ Debug.Assert(this.MappedData != default);
+
+ this.mapCount += count;
+ return this.MappedData;
+ }
+ else
+ {
+ if (count == 0)
+ {
+ return default;
+ }
+
+ IntPtr pData;
+ var res = VkApi.MapMemory(this.Allocator.Device, this.DeviceMemory, 0, Vk.WholeSize, 0, (void**)&pData);
+
+ if (res != Result.Success)
+ {
+ throw new MapMemoryException(res);
+ }
+
+ this.mapCount = count;
+ this.MappedData = pData;
+
+ return pData;
+ }
+ }
+ }
+
+ public void Unmap(int count)
+ {
+ if (count == 0)
+ {
+ return;
+ }
+
+ lock (this.SyncLock)
+ {
+ int newCount = this.mapCount - count;
+
+ if (newCount < 0)
+ {
+ throw new InvalidOperationException("Memory block is being unmapped while it was not previously mapped");
+ }
+
+ this.mapCount = newCount;
+
+ if (newCount == 0)
+ {
+ this.MappedData = default;
+ VkApi.UnmapMemory(this.Allocator.Device, this.DeviceMemory);
+ }
+ }
+ }
+
+ public unsafe Result BindBufferMemory(Allocation allocation, long allocationLocalOffset, Buffer buffer, void* pNext)
+ {
+ Debug.Assert(allocation is BlockAllocation blockAlloc && blockAlloc.Block == this);
+
+ Debug.Assert((ulong)allocationLocalOffset < (ulong)allocation.Size, "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
+
+ long memoryOffset = allocationLocalOffset + allocation.Offset;
+
+ lock (SyncLock)
+ {
+ return this.Allocator.BindVulkanBuffer(buffer, this.DeviceMemory, memoryOffset, pNext);
+ }
+ }
+
+ public unsafe Result BindImageMemory(Allocation allocation, long allocationLocalOffset, Image image, void* pNext)
+ {
+ Debug.Assert(allocation is BlockAllocation blockAlloc && blockAlloc.Block == this);
+
+ Debug.Assert((ulong)allocationLocalOffset < (ulong)allocation.Size, "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
+
+ long memoryOffset = allocationLocalOffset + allocation.Offset;
+
+ lock (this.SyncLock)
+ {
+ return this.Allocator.BindVulkanImage(image, this.DeviceMemory, memoryOffset, pNext);
+ }
+ }
+ }
+}
diff --git a/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryPool.cs b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryPool.cs
new file mode 100644
index 0000000000..ee7556c589
--- /dev/null
+++ b/src/Vulkan/VMASharp_OriginalSources/VulkanMemoryPool.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+using Silk.NET.Vulkan;
+using VMASharp;
+
+namespace VMASharp
+{
+ public sealed class VulkanMemoryPool : IDisposable
+ {
+ public VulkanMemoryAllocator Allocator { get; }
+
+ private Vk VkApi => Allocator.VkApi;
+
+
+ public string Name { get; set; }
+
+ internal uint ID { get; }
+
+ internal readonly BlockList BlockList;
+
+ internal VulkanMemoryPool(VulkanMemoryAllocator allocator, in AllocationPoolCreateInfo poolInfo, long preferredBlockSize)
+ {
+ if (allocator is null)
+ {
+ throw new ArgumentNullException(nameof(allocator));
+ }
+
+ this.Allocator = allocator;
+
+ ref int tmpRef = ref Unsafe.As(ref allocator.NextPoolID);
+
+ this.ID = (uint)Interlocked.Increment(ref tmpRef);
+
+ if (this.ID == 0)
+ throw new OverflowException();
+
+ this.BlockList = new BlockList(
+ allocator,
+ this,
+ poolInfo.MemoryTypeIndex,
+ poolInfo.BlockSize != 0 ? poolInfo.BlockSize : preferredBlockSize,
+ poolInfo.MinBlockCount,
+ poolInfo.MaxBlockCount,
+ (poolInfo.Flags & PoolCreateFlags.IgnoreBufferImageGranularity) != 0 ? 1 : allocator.BufferImageGranularity,
+ poolInfo.FrameInUseCount,
+ poolInfo.BlockSize != 0,
+ poolInfo.AllocationAlgorithmCreate ?? Helpers.DefaultMetaObjectCreate);
+
+ this.BlockList.CreateMinBlocks();
+ }
+
+ public void Dispose()
+ {
+ Allocator.DestroyPool(this);
+ }
+
+ public int MakeAllocationsLost()
+ {
+ return Allocator.MakePoolAllocationsLost(this);
+ }
+
+ public Result CheckForCorruption()
+ {
+ return Allocator.CheckPoolCorruption(this);
+ }
+
+ public void GetPoolStats(out PoolStats stats)
+ {
+ Allocator.GetPoolStats(this, out stats);
+ }
+ }
+}