Added functions vmaAllocateMemoryPages, vmaFreeMemoryPages to VmaRecorder and VmaReplay. Bumped recording file format version to 1.5.

Support for sparse binding is now finished and ready!
diff --git a/docs/Recording file format.md b/docs/Recording file format.md
index 875c638..b56413a 100644
--- a/docs/Recording file format.md
+++ b/docs/Recording file format.md
@@ -23,7 +23,7 @@
 VmaReplay application supports all older versions.

 Current version is:

 

-    1,4

+    1,5

 

 # Configuration

 

@@ -152,6 +152,10 @@
 

 - allocation : pointer

 

+**vmaFreeMemoryPages** (min format version: 1.5)

+

+- allocations : list of pointers

+

 **vmaCreateLostAllocation** (min format version 1.2)

 

 - allocation (output) : pointer

@@ -170,6 +174,20 @@
 - allocation (output) : pointer

 - allocationCreateInfo.pUserData : string (may contain additional commas)

 

+**vmaAllocateMemoryPages** (min format version 1.5)

+

+- vkMemoryRequirements.size : uint64

+- vkMemoryRequirements.alignment : uint64

+- vkMemoryRequirements.memoryTypeBits : uint32

+- allocationCreateInfo.flags : uint32

+- allocationCreateInfo.usage : uint32

+- allocationCreateInfo.requiredFlags : uint32

+- allocationCreateInfo.preferredFlags : uint32

+- allocationCreateInfo.memoryTypeBits : uint32

+- allocationCreateInfo.pool : pointer

+- allocations (output) : list of pointers

+- allocationCreateInfo.pUserData : string (may contain additional commas)

+

 **vmaAllocateMemoryForBuffer, vmaAllocateMemoryForImage** (min format version 1.2)

 

 - vkMemoryRequirements.size : uint64

@@ -230,10 +248,14 @@
 It may contain additional commas.

 It should not contain end-of-line characters - results are then undefined.

 

+**list of (...)** (min format version: 1.5)

+

+An ordered sequence of values of some type, separated by single space.

+

 # Example file

 

     Vulkan Memory Allocator,Calls recording

-    1,4

+    1,5

     Config,Begin

     PhysicalDevice,apiVersion,4198477

     PhysicalDevice,driverVersion,8388653

diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp
index 515c1ce..a7c723f 100644
--- a/src/VmaReplay/Common.cpp
+++ b/src/VmaReplay/Common.cpp
@@ -1,5 +1,29 @@
 #include "Common.h"

 

+bool StrRangeToPtrList(const StrRange& s, std::vector<uint64_t>& out)

+{

+    out.clear();

+    StrRange currRange = { s.beg, nullptr };

+    while(currRange.beg < s.end)

+    {

+        currRange.end = currRange.beg;

+        while(currRange.end < s.end && *currRange.end != ' ')

+        {

+            ++currRange.end;

+        }

+

+        uint64_t ptr = 0;

+        if(!StrRangeToPtr(currRange, ptr))

+        {

+            return false;

+        }

+        out.push_back(ptr);

+

+        currRange.beg = currRange.end + 1;

+    }

+    return true;

+}

+

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

 // LineSplit class

 

diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h
index 28b5bbc..a78524b 100644
--- a/src/VmaReplay/Common.h
+++ b/src/VmaReplay/Common.h
@@ -111,6 +111,7 @@
 

     return true;

 }

+bool StrRangeToPtrList(const StrRange& s, std::vector<uint64_t>& out);

 

 class LineSplit

 {

diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp
index a9acfa5..79d9bde 100644
--- a/src/VmaReplay/VmaReplay.cpp
+++ b/src/VmaReplay/VmaReplay.cpp
@@ -71,8 +71,10 @@
     CreateImage,

     DestroyImage,

     FreeMemory,

+    FreeMemoryPages,

     CreateLostAllocation,

     AllocateMemory,

+    AllocateMemoryPages,

     AllocateMemoryForBuffer,

     AllocateMemoryForImage,

     MapMemory,

@@ -94,8 +96,10 @@
     "vmaCreateImage",

     "vmaDestroyImage",

     "vmaFreeMemory",

+    "vmaFreeMemoryPages",

     "vmaCreateLostAllocation",

     "vmaAllocateMemory",

+    "vmaAllocateMemoryPages",

     "vmaAllocateMemoryForBuffer",

     "vmaAllocateMemoryForImage",

     "vmaMapMemory",

@@ -145,7 +149,7 @@
 static bool ValidateFileVersion()

 {

     if(GetVersionMajor(g_FileVersion) == 1 &&

-        GetVersionMinor(g_FileVersion) <= 4)

+        GetVersionMinor(g_FileVersion) <= 5)

     {

         return true;

     }

@@ -195,7 +199,7 @@
     void RegisterCreateImage(uint32_t usage, uint32_t tiling);

     void RegisterCreateBuffer(uint32_t usage);

     void RegisterCreatePool();

-    void RegisterCreateAllocation();

+    void RegisterCreateAllocation(size_t allocCount = 1);

 

     void UpdateMemStats(const VmaStats& currStats);

 

@@ -364,9 +368,9 @@
     ++m_PoolCreationCount;

 }

 

-void Statistics::RegisterCreateAllocation()

+void Statistics::RegisterCreateAllocation(size_t allocCount)

 {

-    ++m_AllocationCreationCount;

+    m_AllocationCreationCount += allocCount;

 }

 

 void Statistics::UpdateMemStats(const VmaStats& currStats)

@@ -955,10 +959,10 @@
     };

     struct Allocation

     {

-        uint32_t allocationFlags;

-        VmaAllocation allocation;

-        VkBuffer buffer;

-        VkImage image;

+        uint32_t allocationFlags = 0;

+        VmaAllocation allocation = VK_NULL_HANDLE;

+        VkBuffer buffer = VK_NULL_HANDLE;

+        VkImage image = VK_NULL_HANDLE;

     };

     std::unordered_map<uint64_t, Pool> m_Pools;

     std::unordered_map<uint64_t, Allocation> m_Allocations;

@@ -1003,12 +1007,14 @@
     void ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit);

-    void ExecuteDestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyBuffer); DestroyAllocation(lineNumber, csvSplit); }

+    void ExecuteDestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyBuffer); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyBuffer"); }

     void ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit);

-    void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyImage); DestroyAllocation(lineNumber, csvSplit); }

-    void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemory); DestroyAllocation(lineNumber, csvSplit); }

+    void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyImage); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyImage"); }

+    void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemory); DestroyAllocation(lineNumber, csvSplit, "vmaFreeMemory"); }

+    void ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit);

+    void ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType);

     void ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit);

@@ -1019,7 +1025,7 @@
     void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit);

 

-    void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit);

+    void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName);

 };

 

 Player::Player()

@@ -1121,45 +1127,49 @@
                 // Nothing.

             }

         }

-        else if(StrRangeEq(functionName, "vmaCreatePool"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreatePool]))

             ExecuteCreatePool(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaDestroyPool"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyPool]))

             ExecuteDestroyPool(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaSetAllocationUserData"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::SetAllocationUserData]))

             ExecuteSetAllocationUserData(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaCreateBuffer"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateBuffer]))

             ExecuteCreateBuffer(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaDestroyBuffer"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyBuffer]))

             ExecuteDestroyBuffer(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaCreateImage"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateImage]))

             ExecuteCreateImage(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaDestroyImage"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyImage]))

             ExecuteDestroyImage(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaFreeMemory"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemory]))

             ExecuteFreeMemory(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaCreateLostAllocation"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemoryPages]))

+            ExecuteFreeMemoryPages(lineNumber, csvSplit);

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateLostAllocation]))

             ExecuteCreateLostAllocation(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaAllocateMemory"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemory]))

             ExecuteAllocateMemory(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaAllocateMemoryForBuffer"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryPages]))

+            ExecuteAllocateMemoryPages(lineNumber, csvSplit);

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForBuffer]))

             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::BUFFER);

-        else if(StrRangeEq(functionName, "vmaAllocateMemoryForImage"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForImage]))

             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::IMAGE);

-        else if(StrRangeEq(functionName, "vmaMapMemory"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MapMemory]))

             ExecuteMapMemory(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaUnmapMemory"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::UnmapMemory]))

             ExecuteUnmapMemory(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaFlushAllocation"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FlushAllocation]))

             ExecuteFlushAllocation(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaInvalidateAllocation"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::InvalidateAllocation]))

             ExecuteInvalidateAllocation(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaTouchAllocation"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::TouchAllocation]))

             ExecuteTouchAllocation(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaGetAllocationInfo"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::GetAllocationInfo]))

             ExecuteGetAllocationInfo(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaMakePoolAllocationsLost"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MakePoolAllocationsLost]))

             ExecuteMakePoolAllocationsLost(lineNumber, csvSplit);

-        else if(StrRangeEq(functionName, "vmaResizeAllocation"))

+        else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::ResizeAllocation]))

             ExecuteResizeAllocation(lineNumber, csvSplit);

         else

         {

@@ -2029,7 +2039,7 @@
     }

 }

 

-void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit)

+void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName)

 {

     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))

     {

@@ -2059,7 +2069,7 @@
         {

             if(IssueWarning())

             {

-                printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber);

+                printf("Line %zu: Invalid parameters for %s.\n", lineNumber, functionName);

             }

         }

     }

@@ -2127,6 +2137,53 @@
     }

 }

 

+void Player::ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)

+{

+    m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemoryPages);

+    

+    if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))

+    {

+        std::vector<uint64_t> origAllocPtrs;

+        if(StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtrs))

+        {

+            const size_t allocCount = origAllocPtrs.size();

+            size_t notNullCount = 0;

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

+            {

+                const uint64_t origAllocPtr = origAllocPtrs[i];

+                if(origAllocPtr != 0)

+                {

+                    const auto it = m_Allocations.find(origAllocPtr);

+                    if(it != m_Allocations.end())

+                    {

+                        Destroy(it->second);

+                        m_Allocations.erase(it);

+                        ++notNullCount;

+                    }

+                    else

+                    {

+                        if(IssueWarning())

+                        {

+                            printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);

+                        }

+                    }

+                }

+            }

+            if(notNullCount)

+            {

+                UpdateMemStats();

+            }

+        }

+        else

+        {

+            if(IssueWarning())

+            {

+                printf("Line %zu: Invalid parameters for vmaFreeMemoryPages.\n", lineNumber);

+            }

+        }

+    }

+}

+

 void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit)

 {

     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateLostAllocation);

@@ -2206,6 +2263,68 @@
     }

 }

 

+void Player::ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)

+{

+    m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryPages);

+

+    if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true))

+    {

+        VkMemoryRequirements memReq = {};

+        VmaAllocationCreateInfo allocCreateInfo = {};

+        uint64_t origPool = 0;

+        std::vector<uint64_t> origPtrs;

+

+        if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) &&

+            StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) &&

+            StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtrs))

+        {

+            const size_t allocCount = origPtrs.size();

+            if(allocCount > 0)

+            {

+                FindPool(lineNumber, origPool, allocCreateInfo.pool);

+

+                if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10)

+                {

+                    PrepareUserData(

+                        lineNumber,

+                        allocCreateInfo.flags,

+                        csvSplit.GetRange(FIRST_PARAM_INDEX + 10),

+                        csvSplit.GetLine(),

+                        allocCreateInfo.pUserData);

+                }

+

+                UpdateMemStats();

+                m_Stats.RegisterCreateAllocation(allocCount);

+

+                std::vector<VmaAllocation> allocations(allocCount);

+

+                VkResult res = vmaAllocateMemoryPages(m_Allocator, &memReq, &allocCreateInfo, allocCount, allocations.data(), nullptr);

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

+                {

+                    Allocation allocDesc = {};

+                    allocDesc.allocationFlags = allocCreateInfo.flags;

+                    allocDesc.allocation = allocations[i];

+                    AddAllocation(lineNumber, origPtrs[i], res, "vmaAllocateMemoryPages", std::move(allocDesc));

+                }

+            }

+        }

+        else

+        {

+            if(IssueWarning())

+            {

+                printf("Line %zu: Invalid parameters for vmaAllocateMemoryPages.\n", lineNumber);

+            }

+        }

+    }

+}

+

 void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType)

 {

     switch(objType)

diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index eebeb0f..36dcae3 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -5676,6 +5676,11 @@
         const VkMemoryRequirements& vkMemReq,

         const VmaAllocationCreateInfo& createInfo,

         VmaAllocation allocation);

+    void RecordAllocateMemoryPages(uint32_t frameIndex,

+        const VkMemoryRequirements& vkMemReq,

+        const VmaAllocationCreateInfo& createInfo,

+        uint64_t allocationCount,

+        const VmaAllocation* pAllocations);

     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,

         const VkMemoryRequirements& vkMemReq,

         bool requiresDedicatedAllocation,

@@ -5690,6 +5695,9 @@
         VmaAllocation allocation);

     void RecordFreeMemory(uint32_t frameIndex,

         VmaAllocation allocation);

+    void RecordFreeMemoryPages(uint32_t frameIndex,

+        uint64_t allocationCount,

+        const VmaAllocation* pAllocations);

     void RecordResizeAllocation(

         uint32_t frameIndex,

         VmaAllocation allocation,

@@ -5752,6 +5760,7 @@
     int64_t m_StartCounter;

 

     void GetBasicParams(CallParams& outParams);

+    void PrintPointerList(uint64_t count, const VmaAllocation* pItems);

     void Flush();

 };

 

@@ -11661,7 +11670,7 @@
 

     // Write header.

     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");

-    fprintf(m_File, "%s\n", "1,4");

+    fprintf(m_File, "%s\n", "1,5");

 

     return VK_SUCCESS;

 }

@@ -11747,6 +11756,32 @@
     Flush();

 }

 

+void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,

+    const VkMemoryRequirements& vkMemReq,

+    const VmaAllocationCreateInfo& createInfo,

+    uint64_t allocationCount,

+    const VmaAllocation* pAllocations)

+{

+    CallParams callParams;

+    GetBasicParams(callParams);

+

+    VmaMutexLock lock(m_FileMutex, m_UseMutex);

+    UserDataString userDataStr(createInfo.flags, createInfo.pUserData);

+    fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,

+        vkMemReq.size,

+        vkMemReq.alignment,

+        vkMemReq.memoryTypeBits,

+        createInfo.flags,

+        createInfo.usage,

+        createInfo.requiredFlags,

+        createInfo.preferredFlags,

+        createInfo.memoryTypeBits,

+        createInfo.pool);

+    PrintPointerList(allocationCount, pAllocations);

+    fprintf(m_File, ",%s\n", userDataStr.GetString());

+    Flush();

+}

+

 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,

     const VkMemoryRequirements& vkMemReq,

     bool requiresDedicatedAllocation,

@@ -11817,6 +11852,20 @@
     Flush();

 }

 

+void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,

+    uint64_t allocationCount,

+    const VmaAllocation* pAllocations)

+{

+    CallParams callParams;

+    GetBasicParams(callParams);

+

+    VmaMutexLock lock(m_FileMutex, m_UseMutex);

+    fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);

+    PrintPointerList(allocationCount, pAllocations);

+    fprintf(m_File, "\n");

+    Flush();

+}

+

 void VmaRecorder::RecordResizeAllocation(

     uint32_t frameIndex,

     VmaAllocation allocation,

@@ -12108,6 +12157,18 @@
     outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;

 }

 

+void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)

+{

+    if(count)

+    {

+        fprintf(m_File, "%p", pItems[0]);

+        for(uint64_t i = 1; i < count; ++i)

+        {

+            fprintf(m_File, " %p", pItems[i]);

+        }

+    }

+}

+

 void VmaRecorder::Flush()

 {

     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)

@@ -14220,14 +14281,12 @@
 #if VMA_RECORDING_ENABLED

     if(allocator->GetRecorder() != VMA_NULL)

     {

-        // TODO: Extend recording format with this function.

-        /*

         allocator->GetRecorder()->RecordAllocateMemoryPages(

             allocator->GetCurrentFrameIndex(),

             *pVkMemoryRequirements,

             *pCreateInfo,

-            *pAllocation);

-        */

+            (uint64_t)allocationCount,

+            pAllocations);

     }

 #endif

         

@@ -14391,15 +14450,13 @@
     VMA_DEBUG_GLOBAL_MUTEX_LOCK

 

 #if VMA_RECORDING_ENABLED

-    // TODO Add this to recording file format.

-    /*

     if(allocator->GetRecorder() != VMA_NULL)

     {

         allocator->GetRecorder()->RecordFreeMemoryPages(

             allocator->GetCurrentFrameIndex(),

-            allocation);

+            (uint64_t)allocationCount,

+            pAllocations);

     }

-    */

 #endif

     

     allocator->FreeMemory(allocationCount, pAllocations);