Added debug printing of unfreed allocation

Closes #130

Also made fixes for compilation errors on Android - see #223

Code by @medranSolus
diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index cc0194c..97e321c 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -2387,7 +2387,7 @@
 */

 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(

     VmaVirtualBlock VMA_NOT_NULL virtualBlock,

-    VmaVirtualAllocation VMA_NOT_NULL allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);

+    VmaVirtualAllocation allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);

 

 /** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.

 

@@ -2412,7 +2412,7 @@
 */

 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(

     VmaVirtualBlock VMA_NOT_NULL virtualBlock,

-    VmaVirtualAllocation VMA_NULLABLE allocation);

+    VmaVirtualAllocation allocation);

 

 /** \brief Frees all virtual allocations inside given #VmaVirtualBlock.

 

@@ -2429,7 +2429,7 @@
 */

 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(

     VmaVirtualBlock VMA_NOT_NULL virtualBlock,

-    VmaVirtualAllocation VMA_NOT_NULL allocation,

+    VmaVirtualAllocation allocation,

     void* VMA_NULLABLE pUserData);

 

 /** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.

@@ -5997,12 +5997,14 @@
     virtual void Clear() = 0;

 

     virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;

+    virtual void DebugLogAllAllocations() const = 0;

 

 protected:

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

     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }

     VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; }

 

+    void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;

 #if VMA_STATS_STRING_ENABLED

     void PrintDetailedMap_Begin(class VmaJsonWriter& json,

         VkDeviceSize unusedBytes,

@@ -6031,6 +6033,36 @@
     m_BufferImageGranularity(bufferImageGranularity),

     m_IsVirtual(isVirtual) {}

 

+void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const

+{

+    if (IsVirtual())

+    {

+        VMA_DEBUG_LOG("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData);

+    }

+    else

+    {

+        VMA_ASSERT(userData != VMA_NULL);

+        VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);

+

+        userData = allocation->GetUserData();

+        if (userData != VMA_NULL && allocation->IsUserDataString())

+        {

+            VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %s; Type: %s; Usage: %u",

+                offset, size, reinterpret_cast<const char*>(userData),

+                VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],

+                allocation->GetBufferImageUsage());

+        }

+        else

+        {

+            VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Type: %s; Usage: %u",

+                offset, size, userData,

+                VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],

+                allocation->GetBufferImageUsage());

+        }

+    }

+    

+}

+

 #if VMA_STATS_STRING_ENABLED

 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,

     VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const

@@ -6391,6 +6423,7 @@
     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;

     void Clear() override;

     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;

+    void DebugLogAllAllocations() const override;

 

     // For defragmentation

     bool IsBufferImageGranularityConflictPossible(

@@ -6808,6 +6841,15 @@
     suballoc.userData = userData;

 }

 

+void VmaBlockMetadata_Generic::DebugLogAllAllocations() const

+{

+    for (const auto& suballoc : m_Suballocations)

+    {

+        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)

+            DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData);

+    }

+}

+

 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset)

 {

     VMA_HEAVY_ASSERT(!m_Suballocations.empty());

@@ -7231,6 +7273,7 @@
     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;

     void Clear() override;

     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;

+    void DebugLogAllAllocations() const override;

 

 private:

     /*

@@ -8357,6 +8400,19 @@
     suballoc.userData = userData;

 }

 

+void VmaBlockMetadata_Linear::DebugLogAllAllocations() const

+{

+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();

+    for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)

+        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)

+            DebugLogAllocation(it->offset, it->size, it->userData);

+

+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();

+    for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)

+        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)

+            DebugLogAllocation(it->offset, it->size, it->userData);

+}

+

 VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset)

 {

     SuballocationVectorType& suballocations1st = AccessSuballocations1st();

@@ -8840,6 +8896,7 @@
     bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; }

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

     VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle; };

+    void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); }

 

     void Init(VkDeviceSize size) override;

     bool Validate() const override;

@@ -8952,6 +9009,7 @@
     // node->type must be FREE.

     // node->free.prev, next stay untouched.

     void RemoveFromFreeList(uint32_t level, Node* node);

+    void DebugLogAllAllocationNode(Node* node, uint32_t level) const;

 

 #if VMA_STATS_STRING_ENABLED

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

@@ -9453,6 +9511,25 @@
     }

 }

 

+void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const

+{

+    switch (node->type)

+    {

+    case Node::TYPE_ALLOCATION:

+        DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData);

+        break;

+    case Node::TYPE_SPLIT:

+    {

+        ++level;

+        DebugLogAllAllocationNode(node->split.leftChild, level);

+        DebugLogAllAllocationNode(node->split.leftChild->buddy, level);

+    }

+    break;

+    default:

+        VMA_ASSERT(0);

+    }

+}

+

 #if VMA_STATS_STRING_ENABLED

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

 {

@@ -9525,6 +9602,7 @@
     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;

     void Clear() override;

     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;

+    void DebugLogAllAllocations() const override;

 

 private:

     // According to original paper it should be preferable 4 or 5:

@@ -10086,6 +10164,13 @@
     block->UserData() = userData;

 }

 

+void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const

+{

+    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)

+        if (!block->IsFree())

+            DebugLogAllocation(block->offset, block->size, block->UserData());

+}

+

 uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const

 {

     if (size > SMALL_BUFFER_SIZE)

@@ -10891,7 +10976,10 @@
 

 VmaVirtualBlock_T::~VmaVirtualBlock_T()

 {

-    // This is an important assert!!!

+    // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations

+    if (!m_Metadata->IsEmpty())

+        m_Metadata->DebugLogAllAllocations();

+    // This is the most important assert in the entire library.

     // Hitting it means you have some memory leak - unreleased virtual allocations.

     VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");

 

@@ -11382,6 +11470,9 @@
 

 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)

 {

+    // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations

+    if (!m_pMetadata->IsEmpty())

+        m_pMetadata->DebugLogAllAllocations();

     // This is the most important assert in the entire library.

     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.

     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");

@@ -11681,10 +11772,10 @@
     case ALLOCATION_TYPE_BLOCK:

         return m_BlockAllocation.m_AllocHandle;

     case ALLOCATION_TYPE_DEDICATED:

-        return VMA_NULL;

+        return VK_NULL_HANDLE;

     default:

         VMA_ASSERT(0);

-        return VMA_NULL;

+        return VK_NULL_HANDLE;

     }

 }

 

@@ -17654,7 +17745,7 @@
 }

 

 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,

-    VmaVirtualAllocation VMA_NOT_NULL allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)

+    VmaVirtualAllocation allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)

 {

     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);

     VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");

@@ -17672,7 +17763,7 @@
     return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);

 }

 

-VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE allocation)

+VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation allocation)

 {

     if(virtualBlock != VMA_NULL)

     {

@@ -17692,7 +17783,7 @@
 }

 

 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,

-    VmaVirtualAllocation VMA_NOT_NULL allocation, void* VMA_NULLABLE pUserData)

+    VmaVirtualAllocation allocation, void* VMA_NULLABLE pUserData)

 {

     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);

     VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");