diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index b6f6f47..0026be6 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -2194,6 +2194,30 @@ struct VmaVirtualBlockCreateInfo
     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
 };
 
+/// TODO
+typedef enum VmaVirtualAllocationCreateFlagBits {
+    /** Allocation will be created from upper stack in a double stack pool.
+
+    This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
+    */
+    VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
+    /** Allocation strategy that tries to minimize memory usage.
+    */
+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
+    /** Allocation strategy that tries to minimize allocation time.
+    */
+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
+    /** Allocation strategy that tries to minimize memory fragmentation.
+    */
+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT,
+    /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
+    */
+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
+
+    VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaVirtualAllocationCreateFlagBits;
+typedef VkFlags VmaVirtualAllocationCreateFlags;
+
 /// Parameters of created virtual allocation to be passed to vma TODO.
 struct VmaVirtualAllocationCreateInfo
 {
@@ -2207,6 +2231,10 @@ struct VmaVirtualAllocationCreateInfo
     Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
     */
     VkDeviceSize alignment;
+    /** \brief TODO
+    
+    */
+    VmaVirtualAllocationCreateFlags flags;
     /** \brief Custom pointer to be associated with the allocation.
 
     It can be fetched or changed later.
@@ -6831,22 +6859,43 @@ public:
     const VkAllocationCallbacks m_AllocationCallbacks;
 
     VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
-    ~VmaVirtualBlock_T();
-    VkResult Init();
+    ~VmaVirtualBlock_T()
+    {
+        vma_delete(GetAllocationCallbacks(), m_Metadata);
+    }
+    VkResult Init()
+    {
+        return VK_SUCCESS;
+    }
 
     const VkAllocationCallbacks* GetAllocationCallbacks() const
     {
         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
     }
-    bool IsEmpty() const;
-    void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const;
-
+    bool IsEmpty() const
+    {
+        return m_Metadata->IsEmpty();
+    }
+    void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const
+    {
+        m_Metadata->GetAllocationInfo(offset, outInfo);
+    }
     VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset);
-    void Free(VkDeviceSize offset);
-    void Clear();
-    void SetAllocationUserData(VkDeviceSize offset, void* userData);
+    void Free(VkDeviceSize offset)
+    {
+        m_Metadata->FreeAtOffset(offset);
+    }
+    void Clear()
+    {
+        m_Metadata->Clear();
+    }
+    void SetAllocationUserData(VkDeviceSize offset, void* userData)
+    {
+        m_Metadata->SetAllocationUserData(offset, userData);
+    }
 
 private:
+    VmaBlockMetadata* m_Metadata;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -16596,48 +16645,49 @@ VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo
     m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
     m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
 {
-    //TODO
-}
+    const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
+    switch(algorithm)
+    {
+    case 0:
+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Generic)(VK_NULL_HANDLE, true);
+        break;
+    case VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT:
+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Buddy)(VK_NULL_HANDLE, true);
+        break;
+    case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, true);
+        break;
+    default:
+        VMA_ASSERT(0);
+    }
 
-VmaVirtualBlock_T::~VmaVirtualBlock_T()
-{
-    //TODO
-}
-
-VkResult VmaVirtualBlock_T::Init()
-{
-    //TODO
-    return VK_SUCCESS;
-}
-
-bool VmaVirtualBlock_T::IsEmpty() const
-{
-    return true;//TODO
-}
-
-void VmaVirtualBlock_T::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) const
-{
-    //TODO
+    m_Metadata->Init(createInfo.size);
 }
 
 VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset)
 {
-    return VK_ERROR_DEVICE_LOST;//TODO
-}
-
-void VmaVirtualBlock_T::Free(VkDeviceSize offset)
-{
-    //TODO
-}
-
-void VmaVirtualBlock_T::Clear()
-{
-    //TODO
-}
-
-void VmaVirtualBlock_T::SetAllocationUserData(VkDeviceSize offset, void* userData)
-{
-    //TODO
+    outOffset = VK_WHOLE_SIZE;
+    VmaAllocationRequest request = {};
+    if(m_Metadata->CreateAllocationRequest(
+        0, // currentFrameIndex - unimportant
+        0, // frameInUseCount - unimportant
+        1, // bufferImageGranularity
+        createInfo.size, // allocSize
+        VMA_MAX(createInfo.alignment, 1llu), // allocAlignment
+        (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
+        VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
+        false, // canMakeOthersLost
+        createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
+        &request))
+    {
+        m_Metadata->Alloc(request,
+            VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
+            createInfo.size, // allocSize
+            createInfo.pUserData);
+        outOffset = request.offset;
+        return VK_SUCCESS;
+    }
+    return VK_ERROR_OUT_OF_DEVICE_MEMORY;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/Tests.cpp b/src/Tests.cpp
index e32009e..c4afc89 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -31,6 +31,7 @@
 #ifdef _WIN32
 
 static const char* CODE_DESCRIPTION = "Foo";
+static constexpr VkDeviceSize MEGABYTE = 1024 * 1024;
 
 extern VkCommandBuffer g_hTemporaryCommandBuffer;
 extern const VkAllocationCallbacks* g_Allocs;
@@ -2686,6 +2687,126 @@ static void TestBasics()
     TestInvalidAllocations();
 }
 
+static void TestVirtualBlocks()
+{
+    wprintf(L"Test virtual blocks\n");
+
+    const VkDeviceSize blockSize = 16 * MEGABYTE;
+    const VkDeviceSize alignment = 256;
+
+    // # Create block 16 MB
+
+    VmaVirtualBlockCreateInfo blockCreateInfo = {};
+    blockCreateInfo.pAllocationCallbacks = g_Allocs;
+    blockCreateInfo.size = blockSize;
+    VmaVirtualBlock block;
+    TEST(vmaCreateVirtualBlock(&blockCreateInfo, &block) == VK_SUCCESS && block);
+
+    // # Allocate 8 MB
+
+    VmaVirtualAllocationCreateInfo allocCreateInfo = {};
+    allocCreateInfo.alignment = alignment;
+    allocCreateInfo.pUserData = (void*)(uintptr_t)1;
+    allocCreateInfo.size = 8 * MEGABYTE;
+    VkDeviceSize alloc0Offset;
+    TEST(vmaVirtualAllocate(block, &allocCreateInfo, &alloc0Offset) == VK_SUCCESS && alloc0Offset < blockSize);
+
+#if 0
+    // # Validate the allocation
+  
+    VIRTUAL_ALLOCATION_INFO allocInfo = {};
+    block->GetAllocationInfo(alloc0Offset, &allocInfo);
+    CHECK_BOOL( allocInfo.size == allocDesc.Size );
+    CHECK_BOOL( allocInfo.pUserData == allocDesc.pUserData );
+
+    // # Check SetUserData
+
+    block->SetAllocationUserData(alloc0Offset, (void*)(uintptr_t)2);
+    block->GetAllocationInfo(alloc0Offset, &allocInfo);
+    CHECK_BOOL( allocInfo.pUserData == (void*)(uintptr_t)2 );
+
+    // # Allocate 4 MB
+
+    allocDesc.Size = 4 * MEGABYTE;
+    allocDesc.Alignment = alignment;
+    UINT64 alloc1Offset;
+    CHECK_HR( block->Allocate(&allocDesc, &alloc1Offset) );
+    CHECK_BOOL( alloc1Offset < blockSize );
+    CHECK_BOOL( alloc1Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc1Offset ); // Check if they don't overlap.
+
+    // # Allocate another 8 MB - it should fail
+
+    allocDesc.Size = 8 * MEGABYTE;
+    allocDesc.Alignment = alignment;
+    UINT64 alloc2Offset;
+    CHECK_BOOL( FAILED(block->Allocate(&allocDesc, &alloc2Offset)) );
+    CHECK_BOOL( alloc2Offset == UINT64_MAX );
+
+    // # Free the 4 MB block. Now allocation of 8 MB should succeed.
+
+    block->FreeAllocation(alloc1Offset);
+    CHECK_HR( block->Allocate(&allocDesc, &alloc2Offset) );
+    CHECK_BOOL( alloc2Offset < blockSize );
+    CHECK_BOOL( alloc2Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc2Offset ); // Check if they don't overlap.
+
+    // # Calculate statistics
+
+    StatInfo statInfo = {};
+    block->CalculateStats(&statInfo);
+    CHECK_BOOL(statInfo.AllocationCount == 2);
+    CHECK_BOOL(statInfo.BlockCount == 1);
+    CHECK_BOOL(statInfo.UsedBytes == blockSize);
+    CHECK_BOOL(statInfo.UnusedBytes + statInfo.UsedBytes == blockSize);
+
+    // # Generate JSON dump
+
+    WCHAR* json = nullptr;
+    block->BuildStatsString(&json);
+    {
+        std::wstring str(json);
+        CHECK_BOOL( str.find(L"\"UserData\": 1") != std::wstring::npos );
+        CHECK_BOOL( str.find(L"\"UserData\": 2") != std::wstring::npos );
+    }
+    block->FreeStatsString(json);
+#endif
+
+    // # Free alloc0, leave alloc2 unfreed.
+
+    vmaVirtualFree(block, alloc0Offset);
+
+#if 0
+    // # Test alignment
+
+    {
+        constexpr size_t allocCount = 10;
+        UINT64 allocOffset[allocCount] = {};
+        for(size_t i = 0; i < allocCount; ++i)
+        {
+            const bool alignment0 = i == allocCount - 1;
+            allocDesc.Size = i * 3 + 15;
+            allocDesc.Alignment = alignment0 ? 0 : 8;
+            CHECK_HR(block->Allocate(&allocDesc, &allocOffset[i]));
+            if(!alignment0)
+            {
+                CHECK_BOOL(allocOffset[i] % allocDesc.Alignment == 0);
+            }
+        }
+
+        for(size_t i = allocCount; i--; )
+        {
+            block->FreeAllocation(allocOffset[i]);
+        }
+    }
+
+    // # Final cleanup
+
+    block->FreeAllocation(alloc2Offset);
+#endif
+
+    //vmaClearVirtualBlock(block);
+    vmaDestroyVirtualBlock(block);
+}
+
 static void TestAllocationVersusResourceSize()
 {
     wprintf(L"Test allocation versus resource size\n");
@@ -6561,16 +6682,18 @@ void Test()
 {
     wprintf(L"TESTING:\n");
 
-    if(false)
+    if(true)
     {
         ////////////////////////////////////////////////////////////////////////////////
         // Temporarily insert custom tests here:
+        TestVirtualBlocks();
         return;
     }
 
     // # Simple tests
 
     TestBasics();
+    TestVirtualBlocks();
     TestAllocationVersusResourceSize();
     //TestGpuData(); // Not calling this because it's just testing the testing environment.
 #if VMA_DEBUG_MARGIN