VulkanMemoryAllocator 0.4 → 0.5
raw patch · 5 files changed
+390/−162 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- VulkanMemoryAllocator: resizeAllocation :: forall io. MonadIO io => Allocator -> Allocation -> ("newSize" ::: DeviceSize) -> io ()
Files
- VulkanMemoryAllocator.cabal +1/−1
- VulkanMemoryAllocator/src/vk_mem_alloc.h +385/−129
- changelog.md +3/−0
- package.yaml +1/−1
- src/VulkanMemoryAllocator.hs +0/−31
VulkanMemoryAllocator.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: VulkanMemoryAllocator-version: 0.4+version: 0.5 synopsis: Bindings to the VulkanMemoryAllocator library category: Graphics homepage: https://github.com/expipiplus1/vulkan#readme
VulkanMemoryAllocator/src/vk_mem_alloc.h view
@@ -3368,18 +3368,6 @@ size_t allocationCount, const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations); -/** \brief Deprecated. - -\deprecated -In version 2.2.0 it used to try to change allocation's size without moving or reallocating it. -In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size. -Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed. -*/ -VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation( - VmaAllocator VMA_NOT_NULL allocator, - VmaAllocation VMA_NOT_NULL allocation, - VkDeviceSize newSize); - /** \brief Returns current information about specified allocation and atomically marks it as used in current frame. Current paramteres of given allocation are returned in `pAllocationInfo`. @@ -4409,6 +4397,14 @@ #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) #endif +#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + /* + Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount + and return error instead of leaving up to Vulkan implementation what to do in such cases. + */ + #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0) +#endif + #ifndef VMA_SMALL_HEAP_MAX_SIZE /// Maximum size of a memory heap in Vulkan to consider it "small". #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) @@ -6013,6 +6009,222 @@ #endif // #if VMA_USE_STL_LIST //////////////////////////////////////////////////////////////////////////////// +// class VmaIntrusiveLinkedList + +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + typedef MyItem ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template<typename ItemTypeTraits> +class VmaIntrusiveLinkedList +{ +public: + typedef typename ItemTypeTraits::ItemType ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + // Movable, not copyable. + VmaIntrusiveLinkedList() { } + VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete; + VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) : + m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) + { + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + ~VmaIntrusiveLinkedList() + { + VMA_HEAVY_ASSERT(IsEmpty()); + } + VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete; + VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) + { + if(&src != this) + { + VMA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + return *this; + } + void RemoveAll() + { + if(!IsEmpty()) + { + ItemType* item = m_Back; + while(item != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + item = prevItem; + } + m_Front = VMA_NULL; + m_Back = VMA_NULL; + m_Count = 0; + } + } + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + ItemType* Front() { return m_Front; } + const ItemType* Front() const { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Back() const { return m_Back; } + void PushBack(ItemType* item) + { + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if(IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } + } + void PushFront(ItemType* item) + { + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if(IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } + } + ItemType* PopBack() + { + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if(prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = VMA_NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = VMA_NULL; + ItemTypeTraits::AccessNext(backItem) = VMA_NULL; + return backItem; + } + ItemType* PopFront() + { + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if(nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL; + ItemTypeTraits::AccessNext(frontItem) = VMA_NULL; + return frontItem; + } + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem) + { + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if(existingItem != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if(prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); + } + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem) + { + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if(existingItem != VMA_NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if(nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); + } + void Remove(ItemType* item) + { + VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0); + if(ItemTypeTraits::GetPrev(item) != VMA_NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + VMA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if(ItemTypeTraits::GetNext(item) != VMA_NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + VMA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + --m_Count; + } +private: + ItemType* m_Front = VMA_NULL; + ItemType* m_Back = VMA_NULL; + size_t m_Count = 0; +}; + +//////////////////////////////////////////////////////////////////////////////// // class VmaMap // Unused in this version. @@ -6226,6 +6438,8 @@ m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; m_DedicatedAllocation.m_hMemory = hMemory; m_DedicatedAllocation.m_pMappedData = pMappedData; + m_DedicatedAllocation.m_Prev = VMA_NULL; + m_DedicatedAllocation.m_Next = VMA_NULL; } ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } @@ -6323,6 +6537,8 @@ { VkDeviceMemory m_hMemory; void* m_pMappedData; // Not null means memory is mapped. + VmaAllocation_T* m_Prev; + VmaAllocation_T* m_Next; }; union @@ -6339,8 +6555,34 @@ #endif void FreeUserDataString(VmaAllocator hAllocator); + + friend struct VmaDedicatedAllocationListItemTraits; }; +struct VmaDedicatedAllocationListItemTraits +{ + typedef VmaAllocation_T ItemType; + static ItemType* GetPrev(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType* GetNext(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } + static ItemType*& AccessPrev(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType*& AccessNext(ItemType* item){ + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } +}; + /* Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as allocated memory block or free. @@ -7026,14 +7268,6 @@ void* m_pMappedData; }; -struct VmaPointerLess -{ - bool operator()(const void* lhs, const void* rhs) const - { - return lhs < rhs; - } -}; - struct VmaDefragmentationMove { size_t srcBlockIndex; @@ -7237,8 +7471,20 @@ private: uint32_t m_Id; char* m_Name; + VmaPool_T* m_PrevPool = VMA_NULL; + VmaPool_T* m_NextPool = VMA_NULL; + friend struct VmaPoolListItemTraits; }; +struct VmaPoolListItemTraits +{ + typedef VmaPool_T ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; + /* Performs defragmentation: @@ -7913,12 +8159,12 @@ // Default pools. VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; - // Each vector is sorted by memory (handle value). - typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType; - AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES]; + typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList; + DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES]; VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES]; VmaCurrentBudgetData m_Budget; + VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects. VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); @@ -8004,10 +8250,6 @@ size_t allocationCount, const VmaAllocation* pAllocations); - VkResult ResizeAllocation( - const VmaAllocation alloc, - VkDeviceSize newSize); - void CalculateStats(VmaStats* pStats); void GetBudget( @@ -8105,8 +8347,9 @@ VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. VMA_RW_MUTEX m_PoolsMutex; - // Protected by m_PoolsMutex. Sorted by pointer value. - VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools; + typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList; + // Protected by m_PoolsMutex. + PoolList m_Pools; uint32_t m_NextPoolId; VmaVulkanFunctions m_VulkanFunctions; @@ -12614,6 +12857,7 @@ VmaPool_T::~VmaPool_T() { + VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL); } void VmaPool_T::SetName(const char* pName) @@ -12764,9 +13008,13 @@ if(res != VK_SUCCESS) { // Free all already created allocations. + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); while(allocIndex--) { - Free(pAllocations[allocIndex]); + VmaAllocation_T* const alloc = pAllocations[allocIndex]; + const VkDeviceSize allocSize = alloc->GetSize(); + Free(alloc); + m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize); } memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); } @@ -15729,11 +15977,11 @@ *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), m_AllocationObjectAllocator(&m_AllocationCallbacks), m_HeapSizeLimitMask(0), + m_DeviceMemoryCount(0), m_PreferredLargeHeapBlockSize(0), m_PhysicalDevice(pCreateInfo->physicalDevice), m_CurrentFrameIndex(0), m_GpuDefragmentationMemoryTypeBits(UINT32_MAX), - m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())), m_NextPoolId(0), m_GlobalMemoryTypeBits(UINT32_MAX) #if VMA_RECORDING_ENABLED @@ -15805,7 +16053,6 @@ memset(&m_MemProps, 0, sizeof(m_MemProps)); memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); - memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations)); memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) @@ -15864,8 +16111,6 @@ 0.5f); // priority (0.5 is the default per Vulkan spec) // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, // becase minBlockCount is 0. - m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks())); - } } @@ -15918,17 +16163,16 @@ } #endif - VMA_ASSERT(m_Pools.empty()); + VMA_ASSERT(m_Pools.IsEmpty()); - for(size_t i = GetMemoryTypeCount(); i--; ) + for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) { - if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty()) + if(!m_DedicatedAllocations[memTypeIndex].IsEmpty()) { VMA_ASSERT(0 && "Unfreed dedicated allocations found."); } - vma_delete(this, m_pDedicatedAllocations[i]); - vma_delete(this, m_pBlockVectors[i]); + vma_delete(this, m_pBlockVectors[memTypeIndex]); } } @@ -16244,34 +16488,40 @@ { return VK_ERROR_OUT_OF_DEVICE_MEMORY; } + + // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget, + // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above + // 3/4 of the maximum allocation count. + if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + res = AllocateDedicatedMemory( + size, + suballocType, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedBufferUsage, + dedicatedImage, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } else { - res = AllocateDedicatedMemory( - size, - suballocType, - memTypeIndex, - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0, - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, - finalCreateInfo.pUserData, - finalCreateInfo.priority, - dedicatedBuffer, - dedicatedBufferUsage, - dedicatedImage, - allocationCount, - pAllocations); - if(res == VK_SUCCESS) - { - // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. - VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); - return VK_SUCCESS; - } - else - { - // Everything failed: Return error code. - VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); - return res; - } + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; } } } @@ -16378,14 +16628,13 @@ if(res == VK_SUCCESS) { - // Register them in m_pDedicatedAllocations. + // Register them in m_DedicatedAllocations. { VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocations); + DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex]; for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) { - VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]); + dedicatedAllocations.PushBack(pAllocations[allocIndex]); } } @@ -16739,22 +16988,6 @@ } } -VkResult VmaAllocator_T::ResizeAllocation( - const VmaAllocation alloc, - VkDeviceSize newSize) -{ - // This function is deprecated and so it does nothing. It's left for backward compatibility. - if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST) - { - return VK_ERROR_VALIDATION_FAILED_EXT; - } - if(newSize == alloc->GetSize()) - { - return VK_SUCCESS; - } - return VK_ERROR_OUT_OF_POOL_MEMORY; -} - void VmaAllocator_T::CalculateStats(VmaStats* pStats) { // Initialize. @@ -16775,9 +17008,9 @@ // Process custom pools. { VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); - for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) { - m_Pools[poolIndex]->m_BlockVector.AddStats(pStats); + pool->m_BlockVector.AddStats(pStats); } } @@ -16786,12 +17019,12 @@ { const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocVector); - for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex) + DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + for(VmaAllocation alloc = dedicatedAllocList.Front(); + alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc)) { VmaStatInfo allocationStatInfo; - (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo); + alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo); VmaAddStatInfo(pStats->total, allocationStatInfo); VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); @@ -17072,7 +17305,7 @@ { VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); (*pPool)->SetId(m_NextPoolId++); - VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool); + m_Pools.PushBack(*pPool); } return VK_SUCCESS; @@ -17083,8 +17316,7 @@ // Remove from m_Pools. { VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); - bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool); - VMA_ASSERT(success && "Pool not found in Allocator."); + m_Pools.Remove(pool); } vma_delete(this, pool); @@ -17149,11 +17381,11 @@ // Process custom pools. { VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); - for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) { - if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) { - VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption(); + VkResult localRes = pool->m_BlockVector.CheckCorruption(); switch(localRes) { case VK_ERROR_FEATURE_NOT_PRESENT: @@ -17177,8 +17409,42 @@ (*pAllocation)->InitLost(); } +// An object that increments given atomic but decrements it back in the destructor unless Commit() is called. +template<typename T> +struct AtomicTransactionalIncrement +{ +public: + typedef std::atomic<T> AtomicT; + ~AtomicTransactionalIncrement() + { + if(m_Atomic) + --(*m_Atomic); + } + T Increment(AtomicT* atomic) + { + m_Atomic = atomic; + return m_Atomic->fetch_add(1); + } + void Commit() + { + m_Atomic = nullptr; + } + +private: + AtomicT* m_Atomic = nullptr; +}; + VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) { + AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement; + const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount); +#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } +#endif + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); // HeapSizeLimit is in effect for this heap. @@ -17218,6 +17484,8 @@ { (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData); } + + deviceMemoryCountIncrement.Commit(); } else { @@ -17239,6 +17507,8 @@ (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size; + + --m_DeviceMemoryCount; } VkResult VmaAllocator_T::BindVulkanBuffer( @@ -17475,10 +17745,8 @@ const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); { VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocations); - bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation); - VMA_ASSERT(success); + DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex]; + dedicatedAllocations.Remove(allocation); } VkDeviceMemory hMemory = allocation->GetMemory(); @@ -17690,9 +17958,8 @@ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocVector); - if(pDedicatedAllocVector->empty() == false) + DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + if(!dedicatedAllocList.IsEmpty()) { if(dedicatedAllocationsStarted == false) { @@ -17707,11 +17974,11 @@ json.BeginArray(); - for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i) + for(VmaAllocation alloc = dedicatedAllocList.Front(); + alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc)) { json.BeginObject(true); - const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i]; - hAlloc->PrintParameters(json); + alloc->PrintParameters(json); json.EndObject(); } @@ -17752,18 +18019,17 @@ // Custom pools { VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); - const size_t poolCount = m_Pools.size(); - if(poolCount > 0) + if(!m_Pools.IsEmpty()) { json.WriteString("Pools"); json.BeginObject(); - for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) { json.BeginString(); - json.ContinueString(m_Pools[poolIndex]->GetId()); + json.ContinueString(pool->GetId()); json.EndString(); - m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json); + pool->m_BlockVector.PrintDetailedMap(json); } json.EndObject(); } @@ -17957,18 +18223,22 @@ { json.WriteString("LAZILY_ALLOCATED"); } +#if VMA_VULKAN_VERSION >= 1001000 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) { - json.WriteString(" PROTECTED"); + json.WriteString("PROTECTED"); } +#endif // #if VMA_VULKAN_VERSION >= 1001000 +#if VK_AMD_device_coherent_memory if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) { - json.WriteString(" DEVICE_COHERENT"); + json.WriteString("DEVICE_COHERENT"); } if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0) { - json.WriteString(" DEVICE_UNCACHED"); + json.WriteString("DEVICE_UNCACHED"); } +#endif // #if VK_AMD_device_coherent_memory json.EndArray(); if(stats.memoryType[typeIndex].blockCount > 0) @@ -18565,20 +18835,6 @@ #endif allocator->FreeMemory(allocationCount, pAllocations); -} - -VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation( - VmaAllocator allocator, - VmaAllocation allocation, - VkDeviceSize newSize) -{ - VMA_ASSERT(allocator && allocation); - - VMA_DEBUG_LOG("vmaResizeAllocation"); - - VMA_DEBUG_GLOBAL_MUTEX_LOCK - - return allocator->ResizeAllocation(allocation, newSize); } VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
changelog.md view
@@ -2,6 +2,9 @@ ## WIP +## [0.5] - 2021-03-31+- Bump VMA, deprecates resizeAllocation+ ## [0.4] - 2021-02-18 - Bump VMA, support for VK_EXT_memory_priority
package.yaml view
@@ -1,5 +1,5 @@ name: VulkanMemoryAllocator-version: "0.4"+version: "0.5" synopsis: Bindings to the VulkanMemoryAllocator library category: Graphics maintainer: Joe Hermaszewski <live.long.and.prosper@monoid.al>
src/VulkanMemoryAllocator.hs view
@@ -33,7 +33,6 @@ , withMemoryForImage , freeMemory , freeMemoryPages- , resizeAllocation , getAllocationInfo , touchAllocation , setAllocationUserData@@ -1191,36 +1190,6 @@ lift $ Data.Vector.imapM_ (\i e -> poke (pPAllocations `plusPtr` (8 * (i)) :: Ptr Allocation) (e)) (allocations) lift $ traceAroundEvent "vmaFreeMemoryPages" ((ffiVmaFreeMemoryPages) (allocator) ((fromIntegral (Data.Vector.length $ (allocations)) :: CSize)) (pPAllocations)) pure $ ()---foreign import ccall-#if !defined(SAFE_FOREIGN_CALLS)- unsafe-#endif- "vmaResizeAllocation" ffiVmaResizeAllocation- :: Allocator -> Allocation -> DeviceSize -> IO Result---- | Deprecated.------ /Deprecated/------ In version 2.2.0 it used to try to change allocation\'s size without--- moving or reallocating it. In current version it returns @VK_SUCCESS@--- only if @newSize@ equals current allocation\'s size. Otherwise returns--- @VK_ERROR_OUT_OF_POOL_MEMORY@, indicating that allocation\'s size could--- not be changed.-resizeAllocation :: forall io- . (MonadIO io)- => -- No documentation found for Nested "vmaResizeAllocation" "allocator"- Allocator- -> -- No documentation found for Nested "vmaResizeAllocation" "allocation"- Allocation- -> -- No documentation found for Nested "vmaResizeAllocation" "newSize"- ("newSize" ::: DeviceSize)- -> io ()-resizeAllocation allocator allocation newSize = liftIO $ do- r <- traceAroundEvent "vmaResizeAllocation" ((ffiVmaResizeAllocation) (allocator) (allocation) (newSize))- when (r < SUCCESS) (throwIO (VulkanException r)) foreign import ccall