Necessary refactoring to prepare for virtual allocator
diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index 11b7840..4f25bb6 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -2147,6 +2147,21 @@
     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,

     VmaAllocation VMA_NULLABLE allocation);

 

+/// Parameters of an existing virtual allocation, returned by TODO().

+struct VmaVirtualAllocationInfo

+{

+    /** \brief Size of the allocation.

+

+    Same value as passed in TODO::size.

+    */

+    VkDeviceSize size;

+    /** \brief Custom pointer associated with the allocation.

+

+    Same value as passed in TODO::pUserData or TODO::SetAllocationUserData().

+    */

+    void* VMA_NULLABLE pUserData;

+};

+

 #ifdef __cplusplus

 }

 #endif

@@ -4783,7 +4798,7 @@
 {

     VkDeviceSize offset;

     VkDeviceSize size;

-    VmaAllocation hAllocation;

+    void* userData;

     VmaSuballocationType type;

 };

 

@@ -4853,18 +4868,20 @@
 class VmaBlockMetadata

 {

 public:

-    VmaBlockMetadata(VmaAllocator hAllocator);

+    VmaBlockMetadata(VmaAllocator hAllocator, bool isVirtual);

     virtual ~VmaBlockMetadata() { }

     virtual void Init(VkDeviceSize size) { m_Size = size; }

 

     // Validates all data structures inside this object. If not valid, returns false.

     virtual bool Validate() const = 0;

+    bool IsVirtual() const { return m_IsVirtual; }

     VkDeviceSize GetSize() const { return m_Size; }

     virtual size_t GetAllocationCount() const = 0;

     virtual VkDeviceSize GetSumFreeSize() const = 0;

     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;

     // Returns true if this block is empty - contains only single free suballocation.

     virtual bool IsEmpty() const = 0;

+    virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const = 0;

 

     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;

     // Shouldn't modify blockCount.

@@ -4904,12 +4921,17 @@
         const VmaAllocationRequest& request,

         VmaSuballocationType type,

         VkDeviceSize allocSize,

-        VmaAllocation hAllocation) = 0;

+        void* userData) = 0;

 

     // Frees suballocation assigned to given memory region.

-    virtual void Free(const VmaAllocation allocation) = 0;

     virtual void FreeAtOffset(VkDeviceSize offset) = 0;

 

+    // Frees all allocations.

+    // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!

+    virtual void Clear() = 0;

+

+    virtual void SetAllocationUserData(VkDeviceSize offset, void* userData) = 0;

+

 protected:

     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }

 

@@ -4930,6 +4952,7 @@
 private:

     VkDeviceSize m_Size;

     const VkAllocationCallbacks* m_pAllocationCallbacks;

+    const bool m_IsVirtual;

 };

 

 #define VMA_VALIDATE(cond) do { if(!(cond)) { \

@@ -4941,7 +4964,7 @@
 {

     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)

 public:

-    VmaBlockMetadata_Generic(VmaAllocator hAllocator);

+    VmaBlockMetadata_Generic(VmaAllocator hAllocator, bool isVirtual);

     virtual ~VmaBlockMetadata_Generic();

     virtual void Init(VkDeviceSize size);

 

@@ -4983,10 +5006,12 @@
         const VmaAllocationRequest& request,

         VmaSuballocationType type,

         VkDeviceSize allocSize,

-        VmaAllocation hAllocation);

+        void* userData);

 

-    virtual void Free(const VmaAllocation allocation);

     virtual void FreeAtOffset(VkDeviceSize offset);

+    virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const;

+    virtual void Clear();

+    virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);

 

     ////////////////////////////////////////////////////////////////////////////////

     // For defragmentation

@@ -5119,7 +5144,7 @@
 {

     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)

 public:

-    VmaBlockMetadata_Linear(VmaAllocator hAllocator);

+    VmaBlockMetadata_Linear(VmaAllocator hAllocator, bool isVirtual);

     virtual ~VmaBlockMetadata_Linear();

     virtual void Init(VkDeviceSize size);

 

@@ -5161,10 +5186,12 @@
         const VmaAllocationRequest& request,

         VmaSuballocationType type,

         VkDeviceSize allocSize,

-        VmaAllocation hAllocation);

+        void* userData);

 

-    virtual void Free(const VmaAllocation allocation);

     virtual void FreeAtOffset(VkDeviceSize offset);

+    virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const;

+    virtual void Clear();

+    virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);

 

 private:

     /*

@@ -5249,7 +5276,7 @@
 {

     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)

 public:

-    VmaBlockMetadata_Buddy(VmaAllocator hAllocator);

+    VmaBlockMetadata_Buddy(VmaAllocator hAllocator, bool isVirtual);

     virtual ~VmaBlockMetadata_Buddy();

     virtual void Init(VkDeviceSize size);

 

@@ -5291,10 +5318,12 @@
         const VmaAllocationRequest& request,

         VmaSuballocationType type,

         VkDeviceSize allocSize,

-        VmaAllocation hAllocation);

+        void* userData);

 

-    virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }

-    virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }

+    virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VK_NULL_HANDLE, offset); }

+    virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const;

+    virtual void Clear();

+    virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);

 

 private:

     static const VkDeviceSize MIN_NODE_SIZE = 32;

@@ -5334,7 +5363,7 @@
             } free;

             struct

             {

-                VmaAllocation alloc;

+                void* userData;

             } allocation;

             struct

             {

@@ -5365,7 +5394,7 @@
     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;

     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }

     // Alloc passed just for validation. Can be null.

-    void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);

+    void FreeAtOffset(VmaAllocation allocForValidation, VkDeviceSize offset);

     void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;

     // Adds node to the front of FreeList at given level.

     // node->type must be FREE.

@@ -7411,9 +7440,10 @@
 ////////////////////////////////////////////////////////////////////////////////

 // class VmaBlockMetadata

 

-VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :

+VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator, bool isVirtual) :

     m_Size(0),

-    m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())

+    m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks()),

+    m_IsVirtual(isVirtual)

 {

 }

 

@@ -7485,8 +7515,8 @@
 ////////////////////////////////////////////////////////////////////////////////

 // class VmaBlockMetadata_Generic

 

-VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :

-    VmaBlockMetadata(hAllocator),

+VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator, bool isVirtual) :

+    VmaBlockMetadata(hAllocator, isVirtual),

     m_FreeCount(0),

     m_SumFreeSize(0),

     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),

@@ -7509,7 +7539,7 @@
     suballoc.offset = 0;

     suballoc.size = size;

     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-    suballoc.hAllocation = VK_NULL_HANDLE;

+    suballoc.userData = VMA_NULL;

 

     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);

     m_Suballocations.push_back(suballoc);

@@ -7543,7 +7573,11 @@
         // Two adjacent free suballocations are invalid. They should be merged.

         VMA_VALIDATE(!prevFree || !currFree);

 

-        VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));

+        VmaAllocation alloc = (VmaAllocation)subAlloc.userData;

+        if(!IsVirtual())

+        {

+            VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));

+        }

 

         if(currFree)

         {

@@ -7559,8 +7593,11 @@
         }

         else

         {

-            VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);

-            VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);

+            if(!IsVirtual())

+            {

+                VMA_VALIDATE(alloc->GetOffset() == subAlloc.offset);

+                VMA_VALIDATE(alloc->GetSize() == subAlloc.size);

+            }

 

             // Margin required between allocations - previous allocation must be free.

             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);

@@ -7672,7 +7709,8 @@
         }

         else

         {

-            PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);

+            VMA_ASSERT(!IsVirtual()); // TODO?

+            PrintDetailedMap_Allocation(json, suballoc.offset, (VmaAllocation)suballoc.userData);

         }

     }

 

@@ -7795,6 +7833,7 @@
 

     if(canMakeOtherLost)

     {

+        VMA_ASSERT(!IsVirtual());

         // Brute-force algorithm. TODO: Come up with something better.

 

         bool found = false;

@@ -7804,8 +7843,9 @@
             suballocIt != m_Suballocations.end();

             ++suballocIt)

         {

+            VmaAllocation const alloc = (VmaAllocation)suballocIt->userData;

             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||

-                suballocIt->hAllocation->CanBecomeLost())

+                alloc->CanBecomeLost())

             {

                 if(CheckAllocation(

                     currentFrameIndex,

@@ -7848,6 +7888,7 @@
     uint32_t frameInUseCount,

     VmaAllocationRequest* pAllocationRequest)

 {

+    VMA_ASSERT(!IsVirtual());

     VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);

 

     while(pAllocationRequest->itemsToMakeLostCount > 0)

@@ -7857,9 +7898,9 @@
             ++pAllocationRequest->item;

         }

         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());

-        VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);

-        VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());

-        if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))

+        VmaAllocation const alloc = (VmaAllocation)pAllocationRequest->item->userData;

+        VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());

+        if(alloc->MakeLost(currentFrameIndex, frameInUseCount))

         {

             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);

             --pAllocationRequest->itemsToMakeLostCount;

@@ -7879,14 +7920,16 @@
 

 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)

 {

+    VMA_ASSERT(!IsVirtual());

     uint32_t lostAllocationCount = 0;

     for(VmaSuballocationList::iterator it = m_Suballocations.begin();

         it != m_Suballocations.end();

         ++it)

     {

+        VmaAllocation const alloc = (VmaAllocation)it->userData;

         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&

-            it->hAllocation->CanBecomeLost() &&

-            it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))

+            alloc->CanBecomeLost() &&

+            alloc->MakeLost(currentFrameIndex, frameInUseCount))

         {

             it = FreeSuballocation(it);

             ++lostAllocationCount;

@@ -7921,7 +7964,7 @@
     const VmaAllocationRequest& request,

     VmaSuballocationType type,

     VkDeviceSize allocSize,

-    VmaAllocation hAllocation)

+    void* userData)

 {

     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);

     VMA_ASSERT(request.item != m_Suballocations.end());

@@ -7941,7 +7984,7 @@
     suballoc.offset = request.offset;

     suballoc.size = allocSize;

     suballoc.type = type;

-    suballoc.hAllocation = hAllocation;

+    suballoc.userData = userData;

 

     // If there are any free bytes remaining at the end, insert new free suballocation after current one.

     if(paddingEnd)

@@ -7982,23 +8025,6 @@
     m_SumFreeSize -= allocSize;

 }

 

-void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)

-{

-    for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();

-        suballocItem != m_Suballocations.end();

-        ++suballocItem)

-    {

-        VmaSuballocation& suballoc = *suballocItem;

-        if(suballoc.hAllocation == allocation)

-        {

-            FreeSuballocation(suballocItem);

-            VMA_HEAVY_ASSERT(Validate());

-            return;

-        }

-    }

-    VMA_ASSERT(0 && "Not found!");

-}

-

 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)

 {

     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();

@@ -8015,6 +8041,44 @@
     VMA_ASSERT(0 && "Not found!");

 }

 

+void VmaBlockMetadata_Generic::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const

+{

+    for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.begin();

+        suballocItem != m_Suballocations.end();

+        ++suballocItem)

+    {

+        const VmaSuballocation& suballoc = *suballocItem;

+        if (suballoc.offset == offset)

+        {

+            outInfo.size = suballoc.size;

+            outInfo.pUserData = suballoc.userData;

+            return;

+        }

+    }

+    VMA_ASSERT(0 && "Not found!");

+}

+

+void VmaBlockMetadata_Generic::Clear()

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

+void VmaBlockMetadata_Generic::SetAllocationUserData(VkDeviceSize offset, void* userData)

+{

+    for (VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();

+        suballocItem != m_Suballocations.end();

+        ++suballocItem)

+    {

+        VmaSuballocation& suballoc = *suballocItem;

+        if (suballoc.offset == offset)

+        {

+            suballoc.userData = userData;

+            return;

+        }

+    }

+    VMA_ASSERT(0 && "Not found!");

+}

+

 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const

 {

     VkDeviceSize lastSize = 0;

@@ -8055,14 +8119,16 @@
 

     if(canMakeOtherLost)

     {

+        VMA_ASSERT(!IsVirtual());

         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)

         {

             *pSumFreeSize = suballocItem->size;

         }

         else

         {

-            if(suballocItem->hAllocation->CanBecomeLost() &&

-                suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

+            VmaAllocation const alloc = (VmaAllocation)suballocItem->userData;

+            if(alloc->CanBecomeLost() &&

+                alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

             {

                 ++*itemsToMakeLostCount;

                 *pSumItemSize = suballocItem->size;

@@ -8158,9 +8224,10 @@
                 }

                 else

                 {

-                    VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);

-                    if(lastSuballocItem->hAllocation->CanBecomeLost() &&

-                        lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

+                    VmaAllocation const lastSuballocAlloc = (VmaAllocation)lastSuballocItem->userData;

+                    VMA_ASSERT(lastSuballocAlloc != VK_NULL_HANDLE);

+                    if(lastSuballocAlloc->CanBecomeLost() &&

+                        lastSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

                     {

                         ++*itemsToMakeLostCount;

                         *pSumItemSize += lastSuballocItem->size;

@@ -8188,9 +8255,10 @@
                 {

                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))

                     {

-                        VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);

-                        if(nextSuballoc.hAllocation->CanBecomeLost() &&

-                            nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

+                        VmaAllocation const nextSuballocAlloc = (VmaAllocation)nextSuballoc.userData;

+                        VMA_ASSERT(nextSuballocAlloc != VK_NULL_HANDLE);

+                        if(nextSuballocAlloc->CanBecomeLost() &&

+                            nextSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

                         {

                             ++*itemsToMakeLostCount;

                         }

@@ -8324,7 +8392,7 @@
     // Change this suballocation to be marked as free.

     VmaSuballocation& suballoc = *suballocItem;

     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-    suballoc.hAllocation = VK_NULL_HANDLE;

+    suballoc.userData = VMA_NULL;

 

     // Update totals.

     ++m_FreeCount;

@@ -8433,7 +8501,7 @@
     VkDeviceSize bufferImageGranularity,

     VmaSuballocationType& inOutPrevSuballocType) const

 {

-    if(bufferImageGranularity == 1 || IsEmpty())

+    if(bufferImageGranularity == 1 || IsEmpty() || IsVirtual())

     {

         return false;

     }

@@ -8445,7 +8513,8 @@
         const VmaSuballocationType suballocType = suballoc.type;

         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)

         {

-            minAlignment = VMA_MIN(minAlignment, suballoc.hAllocation->GetAlignment());

+            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+            minAlignment = VMA_MIN(minAlignment, alloc->GetAlignment());

             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))

             {

                 typeConflictFound = true;

@@ -8460,8 +8529,8 @@
 ////////////////////////////////////////////////////////////////////////////////

 // class VmaBlockMetadata_Linear

 

-VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :

-    VmaBlockMetadata(hAllocator),

+VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator, bool isVirtual) :

+    VmaBlockMetadata(hAllocator, isVirtual),

     m_SumFreeSize(0),

     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),

     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),

@@ -8496,14 +8565,14 @@
     if(!suballocations1st.empty())

     {

         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.

-        VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);

+        VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].userData != VMA_NULL);

         // Null item at the end should be just pop_back().

-        VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);

+        VMA_VALIDATE(suballocations1st.back().userData != VMA_NULL);

     }

     if(!suballocations2nd.empty())

     {

         // Null item at the end should be just pop_back().

-        VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);

+        VMA_VALIDATE(suballocations2nd.back().userData != VMA_NULL);

     }

 

     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());

@@ -8522,13 +8591,20 @@
             const VmaSuballocation& suballoc = suballocations2nd[i];

             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);

 

-            VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));

+            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+            if(!IsVirtual())

+            {

+                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));

+            }

             VMA_VALIDATE(suballoc.offset >= offset);

 

             if(!currFree)

             {

-                VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);

-                VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);

+                if(!IsVirtual())

+                {

+                    VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);

+                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);

+                }

                 sumUsedSize += suballoc.size;

             }

             else

@@ -8546,7 +8622,7 @@
     {

         const VmaSuballocation& suballoc = suballocations1st[i];

         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&

-            suballoc.hAllocation == VK_NULL_HANDLE);

+            suballoc.userData == VMA_NULL);

     }

 

     size_t nullItem1stCount = m_1stNullItemsBeginCount;

@@ -8556,14 +8632,21 @@
         const VmaSuballocation& suballoc = suballocations1st[i];

         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);

 

-        VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));

+        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+        if(!IsVirtual())

+        {

+            VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));

+        }

         VMA_VALIDATE(suballoc.offset >= offset);

         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);

 

         if(!currFree)

         {

-            VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);

-            VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);

+            if(!IsVirtual())

+            {

+                VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);

+                VMA_VALIDATE(alloc->GetSize() == suballoc.size);

+            }

             sumUsedSize += suballoc.size;

         }

         else

@@ -8584,13 +8667,20 @@
             const VmaSuballocation& suballoc = suballocations2nd[i];

             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);

 

-            VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));

+            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+            if(!IsVirtual())

+            {

+                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));

+            }

             VMA_VALIDATE(suballoc.offset >= offset);

 

             if(!currFree)

             {

-                VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);

-                VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);

+                if(!IsVirtual())

+                {

+                    VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);

+                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);

+                }

                 sumUsedSize += suballoc.size;

             }

             else

@@ -8682,6 +8772,8 @@
 

 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const

 {

+    VMA_ASSERT(!IsVirtual()); // TODO

+

     const VkDeviceSize size = GetSize();

     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();

     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();

@@ -8707,7 +8799,7 @@
         {

             // Find next non-null allocation or move nextAllocIndex to the end.

             while(nextAlloc2ndIndex < suballoc2ndCount &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 ++nextAlloc2ndIndex;

             }

@@ -8764,7 +8856,7 @@
     {

         // Find next non-null allocation or move nextAllocIndex to the end.

         while(nextAlloc1stIndex < suballoc1stCount &&

-            suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)

+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)

         {

             ++nextAlloc1stIndex;

         }

@@ -8820,7 +8912,7 @@
         {

             // Find next non-null allocation or move nextAllocIndex to the end.

             while(nextAlloc2ndIndex != SIZE_MAX &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 --nextAlloc2ndIndex;

             }

@@ -8875,6 +8967,8 @@
 

 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const

 {

+    VMA_ASSERT(!IsVirtual()); // TODO

+

     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();

     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();

     const VkDeviceSize size = GetSize();

@@ -8893,7 +8987,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex < suballoc2ndCount &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 ++nextAlloc2ndIndex;

             }

@@ -8946,7 +9040,7 @@
     {

         // Find next non-null allocation or move nextAllocIndex to the end.

         while(nextAlloc1stIndex < suballoc1stCount &&

-            suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)

+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)

         {

             ++nextAlloc1stIndex;

         }

@@ -8998,7 +9092,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex != SIZE_MAX &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 --nextAlloc2ndIndex;

             }

@@ -9070,7 +9164,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex < suballoc2ndCount &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 ++nextAlloc2ndIndex;

             }

@@ -9119,7 +9213,7 @@
     {

         // Find next non-null allocation or move nextAllocIndex to the end.

         while(nextAlloc1stIndex < suballoc1stCount &&

-            suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)

+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)

         {

             ++nextAlloc1stIndex;

         }

@@ -9166,7 +9260,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex != SIZE_MAX &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 --nextAlloc2ndIndex;

             }

@@ -9221,7 +9315,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex < suballoc2ndCount &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 ++nextAlloc2ndIndex;

             }

@@ -9241,7 +9335,7 @@
 

                 // 2. Process this allocation.

                 // There is allocation with suballoc.offset, suballoc.size.

-                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);

+                PrintDetailedMap_Allocation(json, suballoc.offset, (VmaAllocation)suballoc.userData);

 

                 // 3. Prepare for next iteration.

                 lastOffset = suballoc.offset + suballoc.size;

@@ -9268,7 +9362,7 @@
     {

         // Find next non-null allocation or move nextAllocIndex to the end.

         while(nextAlloc1stIndex < suballoc1stCount &&

-            suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)

+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)

         {

             ++nextAlloc1stIndex;

         }

@@ -9288,7 +9382,7 @@
 

             // 2. Process this allocation.

             // There is allocation with suballoc.offset, suballoc.size.

-            PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);

+            PrintDetailedMap_Allocation(json, suballoc.offset, (VmaAllocation)suballoc.userData);

 

             // 3. Prepare for next iteration.

             lastOffset = suballoc.offset + suballoc.size;

@@ -9316,7 +9410,7 @@
         {

             // Find next non-null allocation or move nextAlloc2ndIndex to the end.

             while(nextAlloc2ndIndex != SIZE_MAX &&

-                suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)

+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)

             {

                 --nextAlloc2ndIndex;

             }

@@ -9336,7 +9430,7 @@
 

                 // 2. Process this allocation.

                 // There is allocation with suballoc.offset, suballoc.size.

-                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);

+                PrintDetailedMap_Allocation(json, suballoc.offset, (VmaAllocation)suballoc.userData);

 

                 // 3. Prepare for next iteration.

                 lastOffset = suballoc.offset + suballoc.size;

@@ -9667,6 +9761,7 @@
 

         if(canMakeOtherLost)

         {

+            VMA_ASSERT(!IsVirtual());

             while(index1st < suballocations1st.size() &&

                 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)

             {

@@ -9678,9 +9773,10 @@
                 }

                 else

                 {

-                    VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);

-                    if(suballoc.hAllocation->CanBecomeLost() &&

-                        suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

+                    VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+                    VMA_ASSERT(alloc != VK_NULL_HANDLE);

+                    if(alloc->CanBecomeLost() &&

+                        alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

                     {

                         ++pAllocationRequest->itemsToMakeLostCount;

                         pAllocationRequest->sumItemSize += suballoc.size;

@@ -9702,11 +9798,12 @@
                     const VmaSuballocation& suballoc = suballocations1st[index1st];

                     if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))

                     {

-                        if(suballoc.hAllocation != VK_NULL_HANDLE)

+                        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+                        if (alloc != VK_NULL_HANDLE)

                         {

                             // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).

-                            if(suballoc.hAllocation->CanBecomeLost() &&

-                                suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

+                            if(alloc->CanBecomeLost() &&

+                                alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)

                             {

                                 ++pAllocationRequest->itemsToMakeLostCount;

                                 pAllocationRequest->sumItemSize += suballoc.size;

@@ -9783,6 +9880,8 @@
     uint32_t frameInUseCount,

     VmaAllocationRequest* pAllocationRequest)

 {

+    VMA_ASSERT(!IsVirtual());

+

     if(pAllocationRequest->itemsToMakeLostCount == 0)

     {

         return true;

@@ -9811,12 +9910,12 @@
         VmaSuballocation& suballoc = (*suballocations)[index];

         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)

         {

-            VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);

-            VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());

-            if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))

+            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

+            VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());

+            if(alloc->MakeLost(currentFrameIndex, frameInUseCount))

             {

                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-                suballoc.hAllocation = VK_NULL_HANDLE;

+                suballoc.userData = VMA_NULL;

                 m_SumFreeSize += suballoc.size;

                 if(suballocations == &AccessSuballocations1st())

                 {

@@ -9844,18 +9943,21 @@
 

 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)

 {

+    VMA_ASSERT(!IsVirtual());

+

     uint32_t lostAllocationCount = 0;

 

     SuballocationVectorType& suballocations1st = AccessSuballocations1st();

     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)

     {

         VmaSuballocation& suballoc = suballocations1st[i];

+        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&

-            suballoc.hAllocation->CanBecomeLost() &&

-            suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))

+            alloc->CanBecomeLost() &&

+            alloc->MakeLost(currentFrameIndex, frameInUseCount))

         {

             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-            suballoc.hAllocation = VK_NULL_HANDLE;

+            suballoc.userData = VMA_NULL;

             ++m_1stNullItemsMiddleCount;

             m_SumFreeSize += suballoc.size;

             ++lostAllocationCount;

@@ -9866,12 +9968,13 @@
     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)

     {

         VmaSuballocation& suballoc = suballocations2nd[i];

+        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;

         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&

-            suballoc.hAllocation->CanBecomeLost() &&

-            suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))

+            alloc->CanBecomeLost() &&

+            alloc->MakeLost(currentFrameIndex, frameInUseCount))

         {

             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-            suballoc.hAllocation = VK_NULL_HANDLE;

+            suballoc.userData = VMA_NULL;

             ++m_2ndNullItemsCount;

             m_SumFreeSize += suballoc.size;

             ++lostAllocationCount;

@@ -9933,9 +10036,9 @@
     const VmaAllocationRequest& request,

     VmaSuballocationType type,

     VkDeviceSize allocSize,

-    VmaAllocation hAllocation)

+    void* userData)

 {

-    const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };

+    const VmaSuballocation newSuballoc = { request.offset, allocSize, userData, type };

 

     switch(request.type)

     {

@@ -9996,11 +10099,6 @@
     m_SumFreeSize -= newSuballoc.size;

 }

 

-void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)

-{

-    FreeAtOffset(allocation->GetOffset());

-}

-

 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)

 {

     SuballocationVectorType& suballocations1st = AccessSuballocations1st();

@@ -10013,7 +10111,7 @@
         if(firstSuballoc.offset == offset)

         {

             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-            firstSuballoc.hAllocation = VK_NULL_HANDLE;

+            firstSuballoc.userData = VMA_NULL;

             m_SumFreeSize += firstSuballoc.size;

             ++m_1stNullItemsBeginCount;

             CleanupAfterFree();

@@ -10060,7 +10158,7 @@
         if(it != suballocations1st.end())

         {

             it->type = VMA_SUBALLOCATION_TYPE_FREE;

-            it->hAllocation = VK_NULL_HANDLE;

+            it->userData = VMA_NULL;

             ++m_1stNullItemsMiddleCount;

             m_SumFreeSize += it->size;

             CleanupAfterFree();

@@ -10080,7 +10178,7 @@
         if(it != suballocations2nd.end())

         {

             it->type = VMA_SUBALLOCATION_TYPE_FREE;

-            it->hAllocation = VK_NULL_HANDLE;

+            it->userData = VMA_NULL;

             ++m_2ndNullItemsCount;

             m_SumFreeSize += it->size;

             CleanupAfterFree();

@@ -10091,6 +10189,21 @@
     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");

 }

 

+void VmaBlockMetadata_Linear::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

+void VmaBlockMetadata_Linear::Clear()

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

+void VmaBlockMetadata_Linear::SetAllocationUserData(VkDeviceSize offset, void* userData)

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

 bool VmaBlockMetadata_Linear::ShouldCompact1st() const

 {

     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;

@@ -10120,7 +10233,7 @@
 

         // Find more null items at the beginning of 1st vector.

         while(m_1stNullItemsBeginCount < suballoc1stCount &&

-            suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)

+            suballocations1st[m_1stNullItemsBeginCount].userData == VMA_NULL)

         {

             ++m_1stNullItemsBeginCount;

             --m_1stNullItemsMiddleCount;

@@ -10128,7 +10241,7 @@
 

         // Find more null items at the end of 1st vector.

         while(m_1stNullItemsMiddleCount > 0 &&

-            suballocations1st.back().hAllocation == VK_NULL_HANDLE)

+            suballocations1st.back().userData == VMA_NULL)

         {

             --m_1stNullItemsMiddleCount;

             suballocations1st.pop_back();

@@ -10136,7 +10249,7 @@
 

         // Find more null items at the end of 2nd vector.

         while(m_2ndNullItemsCount > 0 &&

-            suballocations2nd.back().hAllocation == VK_NULL_HANDLE)

+            suballocations2nd.back().userData == VMA_NULL)

         {

             --m_2ndNullItemsCount;

             suballocations2nd.pop_back();

@@ -10144,7 +10257,7 @@
 

         // Find more null items at the beginning of 2nd vector.

         while(m_2ndNullItemsCount > 0 &&

-            suballocations2nd[0].hAllocation == VK_NULL_HANDLE)

+            suballocations2nd[0].userData == VMA_NULL)

         {

             --m_2ndNullItemsCount;

             VmaVectorRemove(suballocations2nd, 0);

@@ -10156,7 +10269,7 @@
             size_t srcIndex = m_1stNullItemsBeginCount;

             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)

             {

-                while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)

+                while(suballocations1st[srcIndex].userData == VMA_NULL)

                 {

                     ++srcIndex;

                 }

@@ -10189,7 +10302,7 @@
                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;

                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;

                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&

-                    suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)

+                    suballocations2nd[m_1stNullItemsBeginCount].userData == VMA_NULL)

                 {

                     ++m_1stNullItemsBeginCount;

                     --m_1stNullItemsMiddleCount;

@@ -10207,8 +10320,8 @@
 ////////////////////////////////////////////////////////////////////////////////

 // class VmaBlockMetadata_Buddy

 

-VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :

-    VmaBlockMetadata(hAllocator),

+VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator, bool isVirtual) :

+    VmaBlockMetadata(hAllocator, isVirtual),

     m_Root(VMA_NULL),

     m_AllocationCount(0),

     m_FreeCount(1),

@@ -10449,7 +10562,7 @@
     const VmaAllocationRequest& request,

     VmaSuballocationType type,

     VkDeviceSize allocSize,

-    VmaAllocation hAllocation)

+    void* userData)

 {

     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);

 

@@ -10514,13 +10627,28 @@
 

     // Convert to allocation node.

     currNode->type = Node::TYPE_ALLOCATION;

-    currNode->allocation.alloc = hAllocation;

+    currNode->allocation.userData = userData;

 

     ++m_AllocationCount;

     --m_FreeCount;

     m_SumFreeSize -= allocSize;

 }

 

+void VmaBlockMetadata_Buddy::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

+void VmaBlockMetadata_Buddy::Clear()

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

+void VmaBlockMetadata_Buddy::SetAllocationUserData(VkDeviceSize offset, void* userData)

+{

+    VMA_ASSERT(0 && "TODO implement");

+}

+

 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)

 {

     if(node->type == Node::TYPE_SPLIT)

@@ -10534,6 +10662,7 @@
 

 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const

 {

+    VMA_ASSERT(!IsVirtual()); // TODO

     VMA_VALIDATE(level < m_LevelCount);

     VMA_VALIDATE(curr->parent == parent);

     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));

@@ -10547,8 +10676,8 @@
         break;

     case Node::TYPE_ALLOCATION:

         ++ctx.calculatedAllocationCount;

-        ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();

-        VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);

+        VMA_VALIDATE(curr->allocation.userData != VMA_NULL);

+        ctx.calculatedSumFreeSize += levelNodeSize - ((VmaAllocation)(curr->allocation.userData))->GetSize();

         break;

     case Node::TYPE_SPLIT:

         {

@@ -10591,7 +10720,7 @@
     return level;

 }

 

-void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)

+void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation allocForValidation, VkDeviceSize offset)

 {

     // Find node and level.

     Node* node = m_Root;

@@ -10615,7 +10744,11 @@
     }

 

     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);

-    VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);

+    VmaAllocation const alloc = (VmaAllocation)node->allocation.userData;

+    if(!IsVirtual() && allocForValidation)

+    {

+        VMA_ASSERT(alloc == allocForValidation);

+    }

 

     ++m_FreeCount;

     --m_AllocationCount;

@@ -10644,6 +10777,7 @@
 

 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const

 {

+    VMA_ASSERT(!IsVirtual()); // TODO

     switch(node->type)

     {

     case Node::TYPE_FREE:

@@ -10654,7 +10788,7 @@
         break;

     case Node::TYPE_ALLOCATION:

         {

-            const VkDeviceSize allocSize = node->allocation.alloc->GetSize();

+            const VkDeviceSize allocSize = ((VmaAllocation)(node->allocation.userData))->GetSize();

             ++outInfo.allocationCount;

             outInfo.usedBytes += allocSize;

             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);

@@ -10740,6 +10874,7 @@
 #if VMA_STATS_STRING_ENABLED

 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const

 {

+    VMA_ASSERT(!IsVirtual()); // TODO

     switch(node->type)

     {

     case Node::TYPE_FREE:

@@ -10747,8 +10882,9 @@
         break;

     case Node::TYPE_ALLOCATION:

         {

-            PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);

-            const VkDeviceSize allocSize = node->allocation.alloc->GetSize();

+            VmaAllocation const alloc = (VmaAllocation)node->allocation.userData;

+            PrintDetailedMap_Allocation(json, node->offset, alloc);

+            const VkDeviceSize allocSize = alloc->GetSize();

             if(allocSize < levelNodeSize)

             {

                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);

@@ -10803,16 +10939,19 @@
     switch(algorithm)

     {

     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:

-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);

+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator,

+            false); // isVirtual

         break;

     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:

-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);

+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator,

+            false); // isVirtual

         break;

     default:

         VMA_ASSERT(0);

         // Fall-through.

     case 0:

-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);

+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator,

+            false); // isVirtual

     }

     m_pMetadata->Init(newSize);

 }

@@ -11630,7 +11769,7 @@
             pBlock->Unmap(m_hAllocator, 1);

         }

 

-        pBlock->m_pMetadata->Free(hAllocation);

+        pBlock->m_pMetadata->FreeAtOffset(hAllocation->GetOffset());

         VMA_HEAVY_ASSERT(pBlock->Validate());

 

         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);

@@ -12689,13 +12828,14 @@
         if(m_AllAllocations)

         {

             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;

-            for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();

+            VMA_ASSERT(!pMetadata->IsVirtual());

+            for (VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();

                 it != pMetadata->m_Suballocations.end();

                 ++it)

             {

                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)

                 {

-                    AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);

+                    AllocationInfo allocInfo = AllocationInfo((VmaAllocation)it->userData, VMA_NULL);

                     pBlockInfo->m_Allocations.push_back(allocInfo);

                 }

             }

@@ -12818,7 +12958,7 @@
         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();

             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )

         {

-            VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;

+            VmaAllocation const pAlloc = (VmaAllocation)srcSuballocIt->userData;

             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();

             const VkDeviceSize srcAllocSize = srcSuballocIt->size;

             if(m_AllocationsMoved == maxAllocationsToMove ||

@@ -12849,7 +12989,7 @@
 

                     VmaSuballocation suballoc = *srcSuballocIt;

                     suballoc.offset = dstAllocOffset;

-                    suballoc.hAllocation->ChangeOffset(dstAllocOffset);

+                    ((VmaAllocation)(suballoc.userData))->ChangeOffset(dstAllocOffset);

                     m_BytesMoved += srcAllocSize;

                     ++m_AllocationsMoved;

 

@@ -12877,7 +13017,7 @@
 

                     VmaSuballocation suballoc = *srcSuballocIt;

                     suballoc.offset = dstAllocOffset;

-                    suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);

+                    ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);

                     m_BytesMoved += srcAllocSize;

                     ++m_AllocationsMoved;

 

@@ -12943,7 +13083,7 @@
                     else

                     {

                         srcSuballocIt->offset = dstAllocOffset;

-                        srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);

+                        ((VmaAllocation)(srcSuballocIt->userData))->ChangeOffset(dstAllocOffset);

                         dstOffset = dstAllocOffset + srcAllocSize;

                         m_BytesMoved += srcAllocSize;

                         ++m_AllocationsMoved;

@@ -12968,7 +13108,7 @@
 

                     VmaSuballocation suballoc = *srcSuballocIt;

                     suballoc.offset = dstAllocOffset;

-                    suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);

+                    ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);

                     dstOffset = dstAllocOffset + srcAllocSize;

                     m_BytesMoved += srcAllocSize;

                     ++m_AllocationsMoved;