Added macro VMA_VALIDATE to simplify validation methods. Implemented proper calculation of VmaBlockMetadata_Buddy::GetAllocationCount.
diff --git a/src/VmaUsage.h b/src/VmaUsage.h
index bd31938..ec74a34 100644
--- a/src/VmaUsage.h
+++ b/src/VmaUsage.h
@@ -16,7 +16,7 @@
 include all public interface declarations. Example:

 */

 

-//#define VMA_HEAVY_ASSERT(expr) assert(expr)

+#define VMA_HEAVY_ASSERT(expr) assert(expr)

 //#define VMA_USE_STL_CONTAINERS 1

 //#define VMA_DEDICATED_ALLOCATION 0

 //#define VMA_DEBUG_MARGIN 16

diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 956e58d..53ee588 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -4670,6 +4670,11 @@
     VkDeviceSize m_Size;

 };

 

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

+        VMA_ASSERT(0 && "Validation failed: " ## #cond); \

+        return false; \

+    } } while(false)

+

 class VmaBlockMetadata_Generic : public VmaBlockMetadata

 {

     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)

@@ -4950,7 +4955,7 @@
     virtual void Init(VkDeviceSize size);

 

     virtual bool Validate() const;

-    virtual size_t GetAllocationCount() const;

+    virtual size_t GetAllocationCount() const { return m_AllocationCount; }

     virtual VkDeviceSize GetSumFreeSize() const;

     virtual VkDeviceSize GetUnusedRangeSizeMax() const;

     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }

@@ -4981,7 +4986,7 @@
 

     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);

 

-    virtual VkResult CheckCorruption(const void* pBlockData);

+    virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }

 

     virtual void Alloc(

         const VmaAllocationRequest& request,

@@ -4996,6 +5001,14 @@
 private:

     static const size_t MAX_LEVELS = 30; // TODO

 

+    struct ValidationContext

+    {

+        size_t calculatedAllocationCount;

+

+        ValidationContext() :

+            calculatedAllocationCount(0) { }

+    };

+

     struct Node

     {

         VkDeviceSize offset;

@@ -5032,9 +5045,11 @@
         Node* front;

         Node* back;

     } m_FreeList[MAX_LEVELS];

+    // Number of nodes in the tree with type == TYPE_ALLOCATION.

+    size_t m_AllocationCount;

 

     void DeleteNode(Node* node);

-    bool ValidateNode(const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;

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

     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;

     VkDeviceSize LevelToNodeSize(uint32_t level) const;

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

@@ -6580,10 +6595,7 @@
 

 bool VmaBlockMetadata_Generic::Validate() const

 {

-    if(m_Suballocations.empty())

-    {

-        return false;

-    }

+    VMA_VALIDATE(!m_Suballocations.empty());

     

     // Expected offset of new suballocation as calculated from previous ones.

     VkDeviceSize calculatedOffset = 0;

@@ -6604,22 +6616,13 @@
         const VmaSuballocation& subAlloc = *suballocItem;

         

         // Actual offset of this suballocation doesn't match expected one.

-        if(subAlloc.offset != calculatedOffset)

-        {

-            return false;

-        }

+        VMA_VALIDATE(subAlloc.offset == calculatedOffset);

 

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

         // Two adjacent free suballocations are invalid. They should be merged.

-        if(prevFree && currFree)

-        {

-            return false;

-        }

+        VMA_VALIDATE(!prevFree || !currFree);

 

-        if(currFree != (subAlloc.hAllocation == VK_NULL_HANDLE))

-        {

-            return false;

-        }

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

 

         if(currFree)

         {

@@ -6631,27 +6634,15 @@
             }

 

             // Margin required between allocations - every free space must be at least that large.

-            if(subAlloc.size < VMA_DEBUG_MARGIN)

-            {

-                return false;

-            }

+            VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);

         }

         else

         {

-            if(subAlloc.hAllocation->GetOffset() != subAlloc.offset)

-            {

-                return false;

-            }

-            if(subAlloc.hAllocation->GetSize() != subAlloc.size)

-            {

-                return false;

-            }

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

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

 

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

-            if(VMA_DEBUG_MARGIN > 0 && !prevFree)

-            {

-                return false;

-            }

+            VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);

         }

 

         calculatedOffset += subAlloc.size;

@@ -6660,10 +6651,7 @@
 

     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't

     // match expected one.

-    if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)

-    {

-        return false;

-    }

+    VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);

 

     VkDeviceSize lastSize = 0;

     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)

@@ -6671,27 +6659,18 @@
         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];

         

         // Only free suballocations can be registered in m_FreeSuballocationsBySize.

-        if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)

-        {

-            return false;

-        }

+        VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);

         // They must be sorted by size ascending.

-        if(suballocItem->size < lastSize)

-        {

-            return false;

-        }

+        VMA_VALIDATE(suballocItem->size >= lastSize);

 

         lastSize = suballocItem->size;

     }

 

     // Check if totals match calculacted values.

-    if(!ValidateFreeSuballocationList() ||

-        (calculatedOffset != GetSize()) ||

-        (calculatedSumFreeSize != m_SumFreeSize) ||

-        (calculatedFreeCount != m_FreeCount))

-    {

-        return false;

-    }

+    VMA_VALIDATE(ValidateFreeSuballocationList());

+    VMA_VALIDATE(calculatedOffset == GetSize());

+    VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);

+    VMA_VALIDATE(calculatedFreeCount == m_FreeCount);

 

     return true;

 }

@@ -7101,22 +7080,9 @@
     {

         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];

 

-        if(it->type != VMA_SUBALLOCATION_TYPE_FREE)

-        {

-            VMA_ASSERT(0);

-            return false;

-        }

-        if(it->size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)

-        {

-            VMA_ASSERT(0);

-            return false;

-        }

-        if(it->size < lastSize)

-        {

-            VMA_ASSERT(0);

-            return false;

-        }

-

+        VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);

+        VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);

+        VMA_VALIDATE(it->size >= lastSize);

         lastSize = it->size;

     }

     return true;

@@ -7551,45 +7517,26 @@
     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();

     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();

 

-    if(suballocations2nd.empty() != (m_2ndVectorMode == SECOND_VECTOR_EMPTY))

-    {

-        return false;

-    }

-    if(suballocations1st.empty() && !suballocations2nd.empty() &&

-        m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)

-    {

-        return false;

-    }

+    VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));

+    VMA_VALIDATE(!suballocations1st.empty() ||

+        suballocations2nd.empty() ||

+        m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);

+

     if(!suballocations1st.empty())

     {

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

-        if(suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)

-        {

-            return false;

-        }

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

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

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

-        {

-            return false;

-        }

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

     }

     if(!suballocations2nd.empty())

     {

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

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

-        {

-            return false;

-        }

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

     }

 

-    if(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount > suballocations1st.size())

-    {

-        return false;

-    }

-    if(m_2ndNullItemsCount > suballocations2nd.size())

-    {

-        return false;

-    }

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

+    VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());

 

     VkDeviceSize sumUsedSize = 0;

     const size_t suballoc1stCount = suballocations1st.size();

@@ -7604,25 +7551,13 @@
             const VmaSuballocation& suballoc = suballocations2nd[i];

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

 

-            if(currFree != (suballoc.hAllocation == VK_NULL_HANDLE))

-            {

-                return false;

-            }

-            if(suballoc.offset < offset)

-            {

-                return false;

-            }

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

+            VMA_VALIDATE(suballoc.offset >= offset);

 

             if(!currFree)

             {

-                if(suballoc.hAllocation->GetOffset() != suballoc.offset)

-                {

-                    return false;

-                }

-                if(suballoc.hAllocation->GetSize() != suballoc.size)

-                {

-                    return false;

-                }

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

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

                 sumUsedSize += suballoc.size;

             }

             else

@@ -7633,20 +7568,14 @@
             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;

         }

 

-        if(nullItem2ndCount != m_2ndNullItemsCount)

-        {

-            return false;

-        }

+        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);

     }

 

     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)

     {

         const VmaSuballocation& suballoc = suballocations1st[i];

-        if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE ||

-            suballoc.hAllocation != VK_NULL_HANDLE)

-        {

-            return false;

-        }

+        VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&

+            suballoc.hAllocation == VK_NULL_HANDLE);

     }

 

     size_t nullItem1stCount = m_1stNullItemsBeginCount;

@@ -7656,29 +7585,14 @@
         const VmaSuballocation& suballoc = suballocations1st[i];

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

 

-        if(currFree != (suballoc.hAllocation == VK_NULL_HANDLE))

-        {

-            return false;

-        }

-        if(suballoc.offset < offset)

-        {

-            return false;

-        }

-        if(i < m_1stNullItemsBeginCount && !currFree)

-        {

-            return false;

-        }

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

+        VMA_VALIDATE(suballoc.offset >= offset);

+        VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);

 

         if(!currFree)

         {

-            if(suballoc.hAllocation->GetOffset() != suballoc.offset)

-            {

-                return false;

-            }

-            if(suballoc.hAllocation->GetSize() != suballoc.size)

-            {

-                return false;

-            }

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

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

             sumUsedSize += suballoc.size;

         }

         else

@@ -7688,10 +7602,7 @@
 

         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;

     }

-    if(nullItem1stCount != m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount)

-    {

-        return false;

-    }

+    VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);

 

     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)

     {

@@ -7702,25 +7613,13 @@
             const VmaSuballocation& suballoc = suballocations2nd[i];

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

 

-            if(currFree != (suballoc.hAllocation == VK_NULL_HANDLE))

-            {

-                return false;

-            }

-            if(suballoc.offset < offset)

-            {

-                return false;

-            }

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

+            VMA_VALIDATE(suballoc.offset >= offset);

 

             if(!currFree)

             {

-                if(suballoc.hAllocation->GetOffset() != suballoc.offset)

-                {

-                    return false;

-                }

-                if(suballoc.hAllocation->GetSize() != suballoc.size)

-                {

-                    return false;

-                }

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

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

                 sumUsedSize += suballoc.size;

             }

             else

@@ -7731,20 +7630,11 @@
             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;

         }

 

-        if(nullItem2ndCount != m_2ndNullItemsCount)

-        {

-            return false;

-        }

+        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);

     }

 

-    if(offset > GetSize())

-    {

-        return false;

-    }

-    if(m_SumFreeSize != GetSize() - sumUsedSize)

-    {

-        return false;

-    }

+    VMA_VALIDATE(offset <= GetSize());

+    VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);

 

     return true;

 }

@@ -9278,7 +9168,8 @@
 // class VmaBlockMetadata_Buddy

 

 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :

-    m_Root(VMA_NULL)

+    m_Root(VMA_NULL),

+    m_AllocationCount(0)

 {

     memset(m_FreeList, 0, sizeof(m_FreeList));

 }

@@ -9305,42 +9196,32 @@
 bool VmaBlockMetadata_Buddy::Validate() const

 {

     // Validate tree.

-    if(!ValidateNode(VMA_NULL, m_Root, 0, GetSize()))

+    ValidationContext ctx;

+    if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, GetSize()))

     {

-        return false;

+        VMA_VALIDATE(false && "ValidateNode failed.");

     }

+    VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);

 

     // Validate free node lists.

     for(uint32_t level = 0; level < MAX_LEVELS; ++level)

     {

-        if(m_FreeList[level].front != VMA_NULL &&

-            m_FreeList[level].front->free.prev != VMA_NULL)

-        {

-            return false;

-        }

+        VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||

+            m_FreeList[level].front->free.prev == VMA_NULL);

 

         for(Node* node = m_FreeList[level].front;

             node != VMA_NULL;

             node = node->free.next)

         {

-            if(node->type != Node::TYPE_FREE)

-            {

-                return false;

-            }

+            VMA_VALIDATE(node->type == Node::TYPE_FREE);

             

             if(node->free.next == VMA_NULL)

             {

-                if(m_FreeList[level].back != node)

-                {

-                    return false;

-                }

+                VMA_VALIDATE(m_FreeList[level].back == node);

             }

             else

             {

-                if(node->free.next->free.prev != node)

-                {

-                    return false;

-                }

+                VMA_VALIDATE(node->free.next->free.prev == node);

             }

         }

     }

@@ -9348,11 +9229,6 @@
     return true;

 }

 

-size_t VmaBlockMetadata_Buddy::GetAllocationCount() const

-{

-    return 0; // TODO

-}

-

 VkDeviceSize VmaBlockMetadata_Buddy::GetSumFreeSize() const

 {

     return 0; // TODO

@@ -9453,11 +9329,6 @@
     return 0; // TODO

 }

 

-VkResult VmaBlockMetadata_Buddy::CheckCorruption(const void* pBlockData)

-{

-    return VK_SUCCESS; // TODO

-}

-

 void VmaBlockMetadata_Buddy::Alloc(

     const VmaAllocationRequest& request,

     VmaSuballocationType type,

@@ -9514,6 +9385,8 @@
     // Convert to allocation node.

     currNode->type = Node::TYPE_ALLOCATION;

     currNode->allocation.alloc = hAllocation;

+

+    ++m_AllocationCount;

 }

 

 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)

@@ -9527,56 +9400,36 @@
     delete node;

 }

 

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

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

 {

-    if(curr->parent != parent)

-    {

-        return false;

-    }

-    if((curr->buddy == VMA_NULL) != (parent == VMA_NULL))

-    {

-        return false;

-    }

-    if(curr->buddy != VMA_NULL && curr->buddy->buddy != curr)

-    {

-        return false;

-    }

+    VMA_VALIDATE(curr->parent == parent);

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

+    VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);

     switch(curr->type)

     {

     case Node::TYPE_FREE:

         // curr->free.prev, next are validated separately.

         break;

     case Node::TYPE_ALLOCATION:

-        if(curr->allocation.alloc == VK_NULL_HANDLE)

-        {

-            return false;

-        }

+        ++ctx.calculatedAllocationCount;

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

         break;

     case Node::TYPE_SPLIT:

         {

             const uint32_t childrenLevel = level + 1;

             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;

             const Node* const leftChild = curr->split.leftChild;

-            if(leftChild == VMA_NULL)

+            VMA_VALIDATE(leftChild != VMA_NULL);

+            VMA_VALIDATE(leftChild->offset == curr->offset);

+            if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))

             {

-                return false;

-            }

-            if(leftChild->offset != curr->offset)

-            {

-                return false;

-            }

-            if(!ValidateNode(curr, leftChild, childrenLevel, childrenLevelNodeSize))

-            {

-                return false;

+                VMA_VALIDATE(false && "ValidateNode for left child failed.");

             }

             const Node* const rightChild = leftChild->buddy;

-            if(rightChild->offset != curr->offset + levelNodeSize)

+            VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);

+            if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))

             {

-                return false;

-            }

-            if(!ValidateNode(curr, rightChild, childrenLevel, childrenLevelNodeSize))

-            {

-                return false;

+                VMA_VALIDATE(false && "ValidateNode for right child failed.");

             }

         }

         break;

@@ -9639,6 +9492,8 @@
     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);

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

 

+    --m_AllocationCount;

+

     node->type = Node::TYPE_FREE;

 

     // Join free nodes if possible.

@@ -9828,11 +9683,8 @@
 

 bool VmaDeviceMemoryBlock::Validate() const

 {

-    if((m_hMemory == VK_NULL_HANDLE) ||

-        (m_pMetadata->GetSize() == 0))

-    {

-        return false;

-    }

+    VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&

+        (m_pMetadata->GetSize() != 0));

     

     return m_pMetadata->Validate();

 }