Added function vmaResizeAllocation.

Added tests: function TestResize. Bumped CSV recording file format version to 1.4.
This commit is contained in:
Adam Sawicki 2018-11-13 16:17:38 +01:00
parent 1146b45c63
commit b0c363693f
11 changed files with 530 additions and 17 deletions

View file

@ -2374,6 +2374,31 @@ void vmaFreeMemory(
VmaAllocator allocator,
VmaAllocation allocation);
/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
Tries to change allocation's size without moving or reallocating it.
You can both shrink and grow allocation size.
When growing, it succeeds only when the allocation belongs to a memory block with enough
free space after it.
Returns `VK_SUCCESS` if allocation's size has been successfully changed.
Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
After successful call to this function, VmaAllocationInfo::size of this allocation changes.
All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
- Resizing dedicated allocations, as well as allocations created in pools that use linear
or buddy algorithm, is not supported.
The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
Support may be added in the future.
*/
VkResult vmaResizeAllocation(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize newSize);
/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
Current paramters of given allocation are returned in `pAllocationInfo`.
@ -4504,7 +4529,9 @@ public:
void ChangeBlockAllocation(
VmaAllocator hAllocator,
VmaDeviceMemoryBlock* block,
VkDeviceSize offset);
VkDeviceSize offset);
void ChangeSize(VkDeviceSize newSize);
// pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation(
@ -4766,6 +4793,9 @@ public:
virtual void Free(const VmaAllocation allocation) = 0;
virtual void FreeAtOffset(VkDeviceSize offset) = 0;
// Tries to resize (grow or shrink) space for given allocation, in place.
virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
protected:
const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
@ -4845,6 +4875,8 @@ public:
virtual void Free(const VmaAllocation allocation);
virtual void FreeAtOffset(VkDeviceSize offset);
virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
private:
uint32_t m_FreeCount;
VkDeviceSize m_SumFreeSize;
@ -5597,6 +5629,10 @@ public:
VmaAllocation allocation);
void RecordFreeMemory(uint32_t frameIndex,
VmaAllocation allocation);
void RecordResizeAllocation(
uint32_t frameIndex,
VmaAllocation allocation,
VkDeviceSize newSize);
void RecordSetAllocationUserData(uint32_t frameIndex,
VmaAllocation allocation,
const void* pUserData);
@ -5763,6 +5799,10 @@ public:
// Main deallocation function.
void FreeMemory(const VmaAllocation allocation);
VkResult ResizeAllocation(
const VmaAllocation alloc,
VkDeviceSize newSize);
void CalculateStats(VmaStats* pStats);
#if VMA_STATS_STRING_ENABLED
@ -6296,6 +6336,12 @@ void VmaAllocation_T::ChangeBlockAllocation(
m_BlockAllocation.m_Offset = offset;
}
void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
{
VMA_ASSERT(newSize > 0);
m_Size = newSize;
}
VkDeviceSize VmaAllocation_T::GetOffset() const
{
switch(m_Type)
@ -7222,6 +7268,133 @@ void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
VMA_ASSERT(0 && "Not found!");
}
bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
{
typedef VmaSuballocationList::iterator iter_type;
for(iter_type suballocItem = m_Suballocations.begin();
suballocItem != m_Suballocations.end();
++suballocItem)
{
VmaSuballocation& suballoc = *suballocItem;
if(suballoc.hAllocation == alloc)
{
iter_type nextItem = suballocItem;
++nextItem;
// Should have been ensured on higher level.
VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
// Shrinking.
if(newSize < alloc->GetSize())
{
const VkDeviceSize sizeDiff = suballoc.size - newSize;
// There is next item.
if(nextItem != m_Suballocations.end())
{
// Next item is free.
if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
{
// Grow this next item backward.
UnregisterFreeSuballocation(nextItem);
nextItem->offset -= sizeDiff;
nextItem->size += sizeDiff;
RegisterFreeSuballocation(nextItem);
}
// Next item is not free.
else
{
// Create free item after current one.
VmaSuballocation newFreeSuballoc;
newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
newFreeSuballoc.offset = suballoc.offset + newSize;
newFreeSuballoc.size = sizeDiff;
newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
RegisterFreeSuballocation(newFreeSuballocIt);
++m_FreeCount;
}
}
// This is the last item.
else
{
// Create free item at the end.
VmaSuballocation newFreeSuballoc;
newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
newFreeSuballoc.offset = suballoc.offset + newSize;
newFreeSuballoc.size = sizeDiff;
newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
m_Suballocations.push_back(newFreeSuballoc);
iter_type newFreeSuballocIt = m_Suballocations.end();
RegisterFreeSuballocation(--newFreeSuballocIt);
++m_FreeCount;
}
suballoc.size = newSize;
m_SumFreeSize += sizeDiff;
}
// Growing.
else
{
const VkDeviceSize sizeDiff = newSize - suballoc.size;
// There is next item.
if(nextItem != m_Suballocations.end())
{
// Next item is free.
if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
{
// There is not enough free space, including margin.
if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
{
return false;
}
// There is more free space than required.
if(nextItem->size > sizeDiff)
{
// Move and shrink this next item.
UnregisterFreeSuballocation(nextItem);
nextItem->offset += sizeDiff;
nextItem->size -= sizeDiff;
RegisterFreeSuballocation(nextItem);
}
// There is exactly the amount of free space required.
else
{
// Remove this next free item.
UnregisterFreeSuballocation(nextItem);
m_Suballocations.erase(nextItem);
--m_FreeCount;
}
}
// Next item is not free - there is no space to grow.
else
{
return false;
}
}
// This is the last item - there is no space to grow.
else
{
return false;
}
suballoc.size = newSize;
m_SumFreeSize -= sizeDiff;
}
// We cannot call Validate() here because alloc object is updated to new size outside of this call.
return true;
}
}
VMA_ASSERT(0 && "Not found!");
return false;
}
bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
{
VkDeviceSize lastSize = 0;
@ -11368,7 +11541,7 @@ VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
// Write header.
fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
fprintf(m_File, "%s\n", "1,3");
fprintf(m_File, "%s\n", "1,4");
return VK_SUCCESS;
}
@ -11524,6 +11697,20 @@ void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
Flush();
}
void VmaRecorder::RecordResizeAllocation(
uint32_t frameIndex,
VmaAllocation allocation,
VkDeviceSize newSize)
{
CallParams callParams;
GetBasicParams(callParams);
VmaMutexLock lock(m_FileMutex, m_UseMutex);
fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
allocation, newSize);
Flush();
}
void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
VmaAllocation allocation,
const void* pUserData)
@ -12487,6 +12674,40 @@ void VmaAllocator_T::FreeMemory(const VmaAllocation allocation)
vma_delete(this, allocation);
}
VkResult VmaAllocator_T::ResizeAllocation(
const VmaAllocation alloc,
VkDeviceSize newSize)
{
if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
{
return VK_ERROR_VALIDATION_FAILED_EXT;
}
if(newSize == alloc->GetSize())
{
return VK_SUCCESS;
}
switch(alloc->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return VK_ERROR_FEATURE_NOT_PRESENT;
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
{
alloc->ChangeSize(newSize);
VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
return VK_SUCCESS;
}
else
{
return VK_ERROR_OUT_OF_POOL_MEMORY;
}
default:
VMA_ASSERT(0);
return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
void VmaAllocator_T::CalculateStats(VmaStats* pStats)
{
// Initialize.
@ -13889,6 +14110,30 @@ void vmaFreeMemory(
allocator->FreeMemory(allocation);
}
VkResult vmaResizeAllocation(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize newSize)
{
VMA_ASSERT(allocator && allocation);
VMA_DEBUG_LOG("vmaResizeAllocation");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
#if VMA_RECORDING_ENABLED
if(allocator->GetRecorder() != VMA_NULL)
{
allocator->GetRecorder()->RecordResizeAllocation(
allocator->GetCurrentFrameIndex(),
allocation,
newSize);
}
#endif
return allocator->ResizeAllocation(allocation, newSize);
}
void vmaGetAllocationInfo(
VmaAllocator allocator,
VmaAllocation allocation,