Skip to content

Commit 4ac8ff8

Browse files
Multiple fixes in budget management
Including correct handling of lost allocation.
1 parent a63e37c commit 4ac8ff8

5 files changed

Lines changed: 60 additions & 48 deletions

File tree

src/Common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ typedef std::chrono::high_resolution_clock::duration duration;
6666

6767
extern VkPhysicalDevice g_hPhysicalDevice;
6868
extern VkDevice g_hDevice;
69+
extern VkInstance g_hVulkanInstance;
6970
extern VmaAllocator g_hAllocator;
7071
extern bool g_MemoryAliasingWarningEnabled;
7172

src/Tests.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,7 @@ void TestHeapSizeLimit()
20002000
VmaAllocatorCreateInfo allocatorCreateInfo = {};
20012001
allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
20022002
allocatorCreateInfo.device = g_hDevice;
2003+
allocatorCreateInfo.instance = g_hVulkanInstance;
20032004
allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
20042005

20052006
VmaAllocator hAllocator;
@@ -2016,8 +2017,8 @@ void TestHeapSizeLimit()
20162017
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
20172018
bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
20182019

2019-
// 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
2020-
VmaAllocationInfo ownAllocInfo;
2020+
// 1. Allocate two blocks of dedicated memory, half the size of BLOCK_SIZE.
2021+
VmaAllocationInfo dedicatedAllocInfo;
20212022
{
20222023
VmaAllocationCreateInfo allocCreateInfo = {};
20232024
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
@@ -2028,15 +2029,15 @@ void TestHeapSizeLimit()
20282029
for(size_t i = 0; i < 2; ++i)
20292030
{
20302031
Item item;
2031-
res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
2032+
res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &dedicatedAllocInfo);
20322033
TEST(res == VK_SUCCESS);
20332034
items.push_back(item);
20342035
}
20352036
}
20362037

20372038
// Create pool to make sure allocations must be out of this memory type.
20382039
VmaPoolCreateInfo poolCreateInfo = {};
2039-
poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
2040+
poolCreateInfo.memoryTypeIndex = dedicatedAllocInfo.memoryType;
20402041
poolCreateInfo.blockSize = BLOCK_SIZE;
20412042

20422043
VmaPool hPool;
@@ -3873,8 +3874,6 @@ static void TestBudget()
38733874
{
38743875
wprintf(L"Testing budget...\n");
38753876

3876-
uint32_t memTypeIndex = UINT32_MAX;
3877-
38783877
static const VkDeviceSize BUF_SIZE = 100ull * 1024 * 1024;
38793878
static const uint32_t BUF_COUNT = 4;
38803879

@@ -3885,6 +3884,11 @@ static void TestBudget()
38853884
VmaBudget budgetBeg[VK_MAX_MEMORY_HEAPS] = {};
38863885
vmaGetBudget(g_hAllocator, budgetBeg);
38873886

3887+
for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
3888+
{
3889+
TEST(budgetBeg[i].allocationBytes <= budgetBeg[i].blockBytes);
3890+
}
3891+
38883892
VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
38893893
bufInfo.size = BUF_SIZE;
38903894
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
@@ -5234,7 +5238,7 @@ void Test()
52345238
//PerformCustomPoolTest(file);
52355239

52365240
fclose(file);
5237-
5241+
52385242
wprintf(L"Done.\n");
52395243
}
52405244

src/VmaUsage.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ include all public interface declarations. Example:
4949
//#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY 256
5050
//#define VMA_USE_STL_SHARED_MUTEX 0
5151
//#define VMA_DEBUG_GLOBAL_MUTEX 1
52+
//#define VMA_MEMORY_BUDGET 0
5253
/*
5354
#define VMA_DEBUG_LOG(format, ...) do { \
5455
printf(format, __VA_ARGS__); \

src/VulkanSample.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static const bool USE_CUSTOM_CPU_ALLOCATION_CALLBACKS = true;
4343
VkPhysicalDevice g_hPhysicalDevice;
4444
VkDevice g_hDevice;
4545
VmaAllocator g_hAllocator;
46+
VkInstance g_hVulkanInstance;
4647
bool g_MemoryAliasingWarningEnabled = true;
4748

4849
static bool g_EnableValidationLayer = true;
@@ -56,7 +57,6 @@ bool g_SparseBindingEnabled = false;
5657
static HINSTANCE g_hAppInstance;
5758
static HWND g_hWnd;
5859
static LONG g_SizeX = 1280, g_SizeY = 720;
59-
static VkInstance g_hVulkanInstance;
6060
static VkSurfaceKHR g_hSurface;
6161
static VkQueue g_hPresentQueue;
6262
static VkSurfaceFormatKHR g_SurfaceFormat;
@@ -1369,10 +1369,12 @@ static void InitializeApplication()
13691369
{
13701370
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT;
13711371
}
1372+
#if !defined(VMA_MEMORY_BUDGET) || VMA_MEMORY_BUDGET == 1
13721373
if(VK_EXT_memory_budget_enabled && VK_KHR_get_physical_device_properties2_enabled)
13731374
{
13741375
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
13751376
}
1377+
#endif
13761378

13771379
if(USE_CUSTOM_CPU_ALLOCATION_CALLBACKS)
13781380
{

src/vk_mem_alloc.h

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,9 +2154,12 @@ typedef struct VmaBudget
21542154

21552155
/** \brief Sum size of all allocations created in particular heap, in bytes.
21562156

2157-
Always less or equal than `blockBytes`.
2157+
Usually less or equal than `blockBytes`.
21582158
Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
21592159
available for new allocations or wasted due to fragmentation.
2160+
2161+
It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2162+
to this value as well.
21602163
*/
21612164
VkDeviceSize allocationBytes;
21622165

@@ -5284,6 +5287,7 @@ struct VmaAllocation_T
52845287
{
52855288
m_Alignment = 1;
52865289
m_Size = 0;
5290+
m_MemoryTypeIndex = 0;
52875291
m_pUserData = VMA_NULL;
52885292
m_LastUseFrameIndex = currentFrameIndex;
52895293
m_Type = (uint8_t)ALLOCATION_TYPE_NONE;
@@ -5310,6 +5314,7 @@ struct VmaAllocation_T
53105314
VkDeviceSize offset,
53115315
VkDeviceSize alignment,
53125316
VkDeviceSize size,
5317+
uint32_t memoryTypeIndex,
53135318
VmaSuballocationType suballocationType,
53145319
bool mapped,
53155320
bool canBecomeLost)
@@ -5319,6 +5324,7 @@ struct VmaAllocation_T
53195324
m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
53205325
m_Alignment = alignment;
53215326
m_Size = size;
5327+
m_MemoryTypeIndex = memoryTypeIndex;
53225328
m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
53235329
m_SuballocationType = (uint8_t)suballocationType;
53245330
m_BlockAllocation.m_Block = block;
@@ -5331,6 +5337,7 @@ struct VmaAllocation_T
53315337
VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
53325338
VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
53335339
m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5340+
m_MemoryTypeIndex = 0;
53345341
m_BlockAllocation.m_Block = VMA_NULL;
53355342
m_BlockAllocation.m_Offset = 0;
53365343
m_BlockAllocation.m_CanBecomeLost = true;
@@ -5356,9 +5363,9 @@ struct VmaAllocation_T
53565363
m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
53575364
m_Alignment = 0;
53585365
m_Size = size;
5366+
m_MemoryTypeIndex = memoryTypeIndex;
53595367
m_SuballocationType = (uint8_t)suballocationType;
53605368
m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5361-
m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
53625369
m_DedicatedAllocation.m_hMemory = hMemory;
53635370
m_DedicatedAllocation.m_pMappedData = pMappedData;
53645371
}
@@ -5378,7 +5385,7 @@ struct VmaAllocation_T
53785385
}
53795386
VkDeviceSize GetOffset() const;
53805387
VkDeviceMemory GetMemory() const;
5381-
uint32_t GetMemoryTypeIndex() const;
5388+
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
53825389
bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
53835390
void* GetMappedData() const;
53845391
bool CanBecomeLost() const;
@@ -5437,6 +5444,7 @@ struct VmaAllocation_T
54375444
VkDeviceSize m_Size;
54385445
void* m_pUserData;
54395446
VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5447+
uint32_t m_MemoryTypeIndex;
54405448
uint8_t m_Type; // ALLOCATION_TYPE
54415449
uint8_t m_SuballocationType; // VmaSuballocationType
54425450
// Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
@@ -5455,7 +5463,6 @@ struct VmaAllocation_T
54555463
// Allocation for an object that has its own private VkDeviceMemory.
54565464
struct DedicatedAllocation
54575465
{
5458-
uint32_t m_MemoryTypeIndex;
54595466
VkDeviceMemory m_hMemory;
54605467
void* m_pMappedData; // Not null means memory is mapped.
54615468
};
@@ -6964,6 +6971,23 @@ struct VmaCurrentBudgetData
69646971

69656972
#if VMA_MEMORY_BUDGET
69666973
m_OperationsSinceBudgetFetch = 0;
6974+
#endif
6975+
}
6976+
6977+
void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6978+
{
6979+
m_AllocationBytes[heapIndex] += allocationSize;
6980+
#if VMA_MEMORY_BUDGET
6981+
++m_OperationsSinceBudgetFetch;
6982+
#endif
6983+
}
6984+
6985+
void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6986+
{
6987+
VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
6988+
m_AllocationBytes[heapIndex] -= allocationSize;
6989+
#if VMA_MEMORY_BUDGET
6990+
++m_OperationsSinceBudgetFetch;
69676991
#endif
69686992
}
69696993
};
@@ -7715,20 +7739,6 @@ VkDeviceMemory VmaAllocation_T::GetMemory() const
77157739
}
77167740
}
77177741

7718-
uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7719-
{
7720-
switch(m_Type)
7721-
{
7722-
case ALLOCATION_TYPE_BLOCK:
7723-
return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7724-
case ALLOCATION_TYPE_DEDICATED:
7725-
return m_DedicatedAllocation.m_MemoryTypeIndex;
7726-
default:
7727-
VMA_ASSERT(0);
7728-
return UINT32_MAX;
7729-
}
7730-
}
7731-
77327742
void* VmaAllocation_T::GetMappedData() const
77337743
{
77347744
switch(m_Type)
@@ -11840,10 +11850,11 @@ VkResult VmaBlockVector::AllocatePage(
1184011850
freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
1184111851
}
1184211852

11853+
const bool canFallbackToDedicated = !IsCustomPool();
1184311854
const bool canCreateNewBlock =
1184411855
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
1184511856
(m_Blocks.size() < m_MaxBlockCount) &&
11846-
freeMemory >= size;
11857+
(freeMemory >= size || !canFallbackToDedicated);
1184711858
uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
1184811859

1184911860
// If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
@@ -11995,7 +12006,7 @@ VkResult VmaBlockVector::AllocatePage(
1199512006
}
1199612007

1199712008
size_t newBlockIndex = 0;
11998-
VkResult res = newBlockSize <= freeMemory ?
12009+
VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
1199912010
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
1200012011
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
1200112012
if(!m_ExplicitBlockSize)
@@ -12007,7 +12018,7 @@ VkResult VmaBlockVector::AllocatePage(
1200712018
{
1200812019
newBlockSize = smallerNewBlockSize;
1200912020
++newBlockSizeShift;
12010-
res = newBlockSize <= freeMemory ?
12021+
res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
1201112022
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
1201212023
}
1201312024
else
@@ -12162,14 +12173,14 @@ VkResult VmaBlockVector::AllocatePage(
1216212173
bestRequest.offset,
1216312174
alignment,
1216412175
size,
12176+
m_MemoryTypeIndex,
1216512177
suballocType,
1216612178
mapped,
1216712179
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
1216812180
VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
1216912181
VMA_DEBUG_LOG(" Returned from existing block");
1217012182
(*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12171-
m_hAllocator->m_Budget.m_AllocationBytes[m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex)] += size;
12172-
++m_hAllocator->m_Budget.m_OperationsSinceBudgetFetch;
12183+
m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
1217312184
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
1217412185
{
1217512186
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
@@ -12376,13 +12387,13 @@ VkResult VmaBlockVector::AllocateFromBlock(
1237612387
currRequest.offset,
1237712388
alignment,
1237812389
size,
12390+
m_MemoryTypeIndex,
1237912391
suballocType,
1238012392
mapped,
1238112393
(allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
1238212394
VMA_HEAVY_ASSERT(pBlock->Validate());
1238312395
(*pAllocation)->SetUserData(m_hAllocator, pUserData);
12384-
m_hAllocator->m_Budget.m_AllocationBytes[m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex)] += size;
12385-
++m_hAllocator->m_Budget.m_OperationsSinceBudgetFetch;
12396+
m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
1238612397
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
1238712398
{
1238812399
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
@@ -14987,9 +14998,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
1498714998
*/
1498814999

1498915000
FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14990-
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14991-
m_Budget.m_AllocationBytes[heapIndex] -= currAlloc->GetSize();
14992-
++m_Budget.m_OperationsSinceBudgetFetch;
15001+
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
1499315002
currAlloc->SetUserData(this, VMA_NULL);
1499415003
currAlloc->Dtor();
1499515004
m_AllocationObjectAllocator.Free(currAlloc);
@@ -15041,9 +15050,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
1504115050
(*pAllocation)->Ctor(m_CurrentFrameIndex.load(), isUserDataString);
1504215051
(*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
1504315052
(*pAllocation)->SetUserData(this, pUserData);
15044-
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15045-
m_Budget.m_AllocationBytes[heapIndex] += size;
15046-
++m_Budget.m_OperationsSinceBudgetFetch;
15053+
m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
1504715054
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
1504815055
{
1504915056
FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
@@ -15311,11 +15318,8 @@ void VmaAllocator_T::FreeMemory(
1531115318
}
1531215319
}
1531315320

15314-
if(allocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
15315-
{
15316-
m_Budget.m_AllocationBytes[MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex())] -= allocation->GetSize();
15317-
++m_Budget.m_OperationsSinceBudgetFetch;
15318-
}
15321+
// Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
15322+
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
1531915323
allocation->SetUserData(this, VMA_NULL);
1532015324
allocation->Dtor();
1532115325
m_AllocationObjectAllocator.Free(allocation);
@@ -15748,7 +15752,7 @@ VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAlloc
1574815752
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
1574915753

1575015754
// HeapSizeLimit is in effect for this heap.
15751-
if((m_HeapSizeLimitMask | (1u << heapIndex)) != 0)
15755+
if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
1575215756
{
1575315757
const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
1575415758
VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
@@ -15775,7 +15779,9 @@ VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAlloc
1577515779

1577615780
if(res == VK_SUCCESS)
1577715781
{
15782+
#if VMA_MEMORY_BUDGET
1577815783
++m_Budget.m_OperationsSinceBudgetFetch;
15784+
#endif
1577915785

1578015786
// Informative callback.
1578115787
if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
@@ -15802,9 +15808,7 @@ void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, Vk
1580215808
// VULKAN CALL vkFreeMemory.
1580315809
(*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
1580415810

15805-
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15806-
m_Budget.m_BlockBytes[heapIndex] -= size;
15807-
++m_Budget.m_OperationsSinceBudgetFetch;
15811+
m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
1580815812
}
1580915813

1581015814
VkResult VmaAllocator_T::BindVulkanBuffer(

0 commit comments

Comments
 (0)