VulkanMemoryAllocator 0.6.0.1 → 0.7
raw patch · 6 files changed
+263/−17 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ VulkanMemoryAllocator: [$sel:typeExternalMemoryHandleTypes:AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr ExternalMemoryHandleTypeFlagsKHR
+ VulkanMemoryAllocator: createBufferWithAlignment :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> ("minAlignment" ::: DeviceSize) -> io (Buffer, Allocation, AllocationInfo)
- VulkanMemoryAllocator: AllocatorCreateInfo :: AllocatorCreateFlags -> Ptr PhysicalDevice_T -> Ptr Device_T -> DeviceSize -> Maybe AllocationCallbacks -> Maybe DeviceMemoryCallbacks -> Word32 -> Ptr DeviceSize -> Maybe VulkanFunctions -> Maybe RecordSettings -> Ptr Instance_T -> Word32 -> AllocatorCreateInfo
+ VulkanMemoryAllocator: AllocatorCreateInfo :: AllocatorCreateFlags -> Ptr PhysicalDevice_T -> Ptr Device_T -> DeviceSize -> Maybe AllocationCallbacks -> Maybe DeviceMemoryCallbacks -> Word32 -> Ptr DeviceSize -> Maybe VulkanFunctions -> Maybe RecordSettings -> Ptr Instance_T -> Word32 -> Ptr ExternalMemoryHandleTypeFlagsKHR -> AllocatorCreateInfo
Files
- VulkanMemoryAllocator.cabal +1/−1
- VulkanMemoryAllocator/include/vk_mem_alloc.h +192/−12
- changelog.md +3/−0
- package.yaml +1/−1
- src/VulkanMemoryAllocator.hs +58/−3
- src/lib.cpp +8/−0
VulkanMemoryAllocator.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: VulkanMemoryAllocator-version: 0.6.0.1+version: 0.7 synopsis: Bindings to the VulkanMemoryAllocator library category: Graphics homepage: https://github.com/expipiplus1/vulkan#readme
VulkanMemoryAllocator/include/vk_mem_alloc.h view
@@ -2121,6 +2121,15 @@ #endif #endif +// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers. +#if !defined(VMA_EXTERNAL_MEMORY) + #if VK_KHR_external_memory + #define VMA_EXTERNAL_MEMORY 1 + #else + #define VMA_EXTERNAL_MEMORY 0 + #endif +#endif + // Define these macros to decorate all public functions with additional code, // before and after returned type, appropriately. This may be useful for // exporting the functions when compiling VMA as a separate library. Example: @@ -2494,6 +2503,18 @@ Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. */ uint32_t vulkanApiVersion; +#if VMA_EXTERNAL_MEMORY + /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type. + + If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` + elements, defining external memory handle types of particular Vulkan memory type, + to be passed using `VkExportMemoryAllocateInfoKHR`. + + Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. + This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. + */ + const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes; +#endif // #if VMA_EXTERNAL_MEMORY } VmaAllocatorCreateInfo; /// Creates Allocator object. @@ -3966,6 +3987,21 @@ VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation, VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); +/** \brief Creates a buffer with additional minimum alignment. + +Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, +minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. +for interop with OpenGL. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + /** \brief Destroys Vulkan buffer and frees allocated memory. This is just a convenience function equivalent to: @@ -4146,18 +4182,21 @@ static void* vma_aligned_alloc(size_t alignment, size_t size) { -#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 - // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only - // with the MacOSX11.0 SDK in Xcode 12 (which is what adds - // MAC_OS_X_VERSION_10_16), even though the function is marked - // availabe for 10.15. That's why the preprocessor checks for 10.16 but - // the __builtin_available checks for 10.15. - // People who use C++17 could call aligned_alloc with the 10.15 SDK already. - if (__builtin_available(macOS 10.15, iOS 13, *)) - return aligned_alloc(alignment, size); -#endif -#endif + // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4) + // Therefore, for now disable this specific exception until a proper solution is found. + //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) + //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 + // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only + // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds + // // MAC_OS_X_VERSION_10_16), even though the function is marked + // // availabe for 10.15. That's why the preprocessor checks for 10.16 but + // // the __builtin_available checks for 10.15. + // // People who use C++17 could call aligned_alloc with the 10.15 SDK already. + // if (__builtin_available(macOS 10.15, iOS 13, *)) + // return aligned_alloc(alignment, size); + //#endif + //#endif + // alignment must be >= sizeof(void*) if(alignment < sizeof(void*)) { @@ -8395,6 +8434,12 @@ */ uint32_t GetGpuDefragmentationMemoryTypeBits(); +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const + { + return m_TypeExternalMemoryHandleTypes[memTypeIndex]; + } +#endif // #if VMA_EXTERNAL_MEMORY private: VkDeviceSize m_PreferredLargeHeapBlockSize; @@ -8402,6 +8447,9 @@ VkPhysicalDevice m_PhysicalDevice; VMA_ATOMIC_UINT32 m_CurrentFrameIndex; VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES]; +#endif // #if VMA_EXTERNAL_MEMORY VMA_RW_MUTEX m_PoolsMutex; typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList; @@ -13664,6 +13712,16 @@ } #endif // #if VMA_MEMORY_PRIORITY +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + VkDeviceMemory mem = VK_NULL_HANDLE; VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); if(res < 0) @@ -16108,6 +16166,9 @@ memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); +#if VMA_EXTERNAL_MEMORY + memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes)); +#endif // #if VMA_EXTERNAL_MEMORY if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) { @@ -16131,6 +16192,13 @@ m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits(); +#if VMA_EXTERNAL_MEMORY + if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL) + { + memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes, + sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount()); + } +#endif // #if VMA_EXTERNAL_MEMORY if(pCreateInfo->pHeapSizeLimit != VMA_NULL) { @@ -16664,6 +16732,16 @@ } #endif // #if VMA_MEMORY_PRIORITY +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + size_t allocIndex; VkResult res = VK_SUCCESS; for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) @@ -19396,6 +19474,108 @@ *pBufferCreateInfo, *pAllocationCreateInfo, *pAllocation); + } +#endif + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_VALIDATION_FAILED_EXT; + } + + VMA_DEBUG_LOG("vmaCreateBufferWithAlignment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 2a. Include minAlignment + vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + pBufferCreateInfo->usage, // dedicatedBufferUsage + VK_NULL_HANDLE, // dedicatedImage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + VMA_ASSERT(0 && "Not implemented."); } #endif
changelog.md view
@@ -2,6 +2,9 @@ ## WIP +## [0.7] - 2021-07-23+- Bump VMA, adds vkaCreateBufferWithAlignment and fixes aligned_alloc on OS X.+ ## [0.6.0.1] - 2021-06-26 - Bump VMA, no functional change - Use allocaBytes over allocaBytesAligned where possible
package.yaml view
@@ -1,5 +1,5 @@ name: VulkanMemoryAllocator-version: "0.6.0.1"+version: "0.7" synopsis: Bindings to the VulkanMemoryAllocator library category: Graphics maintainer: Joe Hermaszewski <live.long.and.prosper@monoid.al>
src/VulkanMemoryAllocator.hs view
@@ -59,6 +59,7 @@ , bindImageMemory2 , createBuffer , withBuffer+ , createBufferWithAlignment , destroyBuffer , createImage , withImage@@ -155,6 +156,7 @@ import Vulkan (DeviceMemory) import Vulkan (DeviceSize) import Vulkan (Device_T)+import Vulkan (ExternalMemoryHandleTypeFlagsKHR) import Vulkan (Flags) import Vulkan (Image) import Vulkan (ImageCreateInfo)@@ -2265,6 +2267,44 @@ #if !defined(SAFE_FOREIGN_CALLS) unsafe #endif+ "vmaCreateBufferWithAlignment" ffiVmaCreateBufferWithAlignment+ :: Allocator -> Ptr (SomeStruct BufferCreateInfo) -> Ptr AllocationCreateInfo -> DeviceSize -> Ptr Buffer -> Ptr Allocation -> Ptr AllocationInfo -> IO Result++-- | Creates a buffer with additional minimum alignment.+--+-- Similar to 'createBuffer' but provides additional parameter+-- @minAlignment@ which allows to specify custom, minimum alignment to be+-- used when placing the buffer inside a larger memory block, which may be+-- needed e.g. for interop with OpenGL.+createBufferWithAlignment :: forall a io+ . (Extendss BufferCreateInfo a, PokeChain a, MonadIO io)+ => -- No documentation found for Nested "vmaCreateBufferWithAlignment" "allocator"+ Allocator+ -> -- No documentation found for Nested "vmaCreateBufferWithAlignment" "pBufferCreateInfo"+ (BufferCreateInfo a)+ -> -- No documentation found for Nested "vmaCreateBufferWithAlignment" "pAllocationCreateInfo"+ AllocationCreateInfo+ -> -- No documentation found for Nested "vmaCreateBufferWithAlignment" "minAlignment"+ ("minAlignment" ::: DeviceSize)+ -> io (Buffer, Allocation, AllocationInfo)+createBufferWithAlignment allocator bufferCreateInfo allocationCreateInfo minAlignment = liftIO . evalContT $ do+ pBufferCreateInfo <- ContT $ withCStruct (bufferCreateInfo)+ pAllocationCreateInfo <- ContT $ withCStruct (allocationCreateInfo)+ pPBuffer <- ContT $ bracket (callocBytes @Buffer 8) free+ pPAllocation <- ContT $ bracket (callocBytes @Allocation 8) free+ pPAllocationInfo <- ContT (withZeroCStruct @AllocationInfo)+ r <- lift $ traceAroundEvent "vmaCreateBufferWithAlignment" ((ffiVmaCreateBufferWithAlignment) (allocator) (forgetExtensions pBufferCreateInfo) pAllocationCreateInfo (minAlignment) (pPBuffer) (pPAllocation) (pPAllocationInfo))+ lift $ when (r < SUCCESS) (throwIO (VulkanException r))+ pBuffer <- lift $ peek @Buffer pPBuffer+ pAllocation <- lift $ peek @Allocation pPAllocation+ pAllocationInfo <- lift $ peekCStruct @AllocationInfo pPAllocationInfo+ pure $ (pBuffer, pAllocation, pAllocationInfo)+++foreign import ccall+#if !defined(SAFE_FOREIGN_CALLS)+ unsafe+#endif "vmaDestroyBuffer" ffiVmaDestroyBuffer :: Allocator -> Buffer -> Allocation -> IO () @@ -3052,6 +3092,18 @@ -- supported by the current implementation. Leaving it initialized to zero -- is equivalent to @VK_API_VERSION_1_0@. vulkanApiVersion :: Word32+ , -- | Either null or a pointer to an array of external memory handle types for+ -- each Vulkan memory type.+ --+ -- If not NULL, it must be a pointer to an array of+ -- @VkPhysicalDeviceMemoryProperties::memoryTypeCount@ elements, defining+ -- external memory handle types of particular Vulkan memory type, to be+ -- passed using @VkExportMemoryAllocateInfoKHR@.+ --+ -- Any of the elements may be equal to 0, which means not to use+ -- @VkExportMemoryAllocateInfoKHR@ on this memory type. This is also the+ -- default in case of @pTypeExternalMemoryHandleTypes@ = NULL.+ typeExternalMemoryHandleTypes :: Ptr ExternalMemoryHandleTypeFlagsKHR } deriving (Typeable) #if defined(GENERIC_INSTANCES)@@ -3060,7 +3112,7 @@ deriving instance Show AllocatorCreateInfo instance ToCStruct AllocatorCreateInfo where- withCStruct x f = allocaBytes 96 $ \p -> pokeCStruct p x (f p)+ withCStruct x f = allocaBytes 104 $ \p -> pokeCStruct p x (f p) pokeCStruct p AllocatorCreateInfo{..} f = evalContT $ do lift $ poke ((p `plusPtr` 0 :: Ptr AllocatorCreateFlags)) (flags) lift $ poke ((p `plusPtr` 8 :: Ptr (Ptr PhysicalDevice_T))) (physicalDevice)@@ -3086,8 +3138,9 @@ lift $ poke ((p `plusPtr` 72 :: Ptr (Ptr RecordSettings))) pRecordSettings'' lift $ poke ((p `plusPtr` 80 :: Ptr (Ptr Instance_T))) (instance') lift $ poke ((p `plusPtr` 88 :: Ptr Word32)) (vulkanApiVersion)+ lift $ poke ((p `plusPtr` 96 :: Ptr (Ptr ExternalMemoryHandleTypeFlagsKHR))) (typeExternalMemoryHandleTypes) lift $ f- cStructSize = 96+ cStructSize = 104 cStructAlignment = 8 pokeZeroCStruct p f = do poke ((p `plusPtr` 0 :: Ptr AllocatorCreateFlags)) (zero)@@ -3117,8 +3170,9 @@ pRecordSettings' <- maybePeek (\j -> peekCStruct @RecordSettings (j)) pRecordSettings instance' <- peek @(Ptr Instance_T) ((p `plusPtr` 80 :: Ptr (Ptr Instance_T))) vulkanApiVersion <- peek @Word32 ((p `plusPtr` 88 :: Ptr Word32))+ pTypeExternalMemoryHandleTypes <- peek @(Ptr ExternalMemoryHandleTypeFlagsKHR) ((p `plusPtr` 96 :: Ptr (Ptr ExternalMemoryHandleTypeFlagsKHR))) pure $ AllocatorCreateInfo- flags physicalDevice device preferredLargeHeapBlockSize pAllocationCallbacks' pDeviceMemoryCallbacks' frameInUseCount pHeapSizeLimit pVulkanFunctions' pRecordSettings' instance' vulkanApiVersion+ flags physicalDevice device preferredLargeHeapBlockSize pAllocationCallbacks' pDeviceMemoryCallbacks' frameInUseCount pHeapSizeLimit pVulkanFunctions' pRecordSettings' instance' vulkanApiVersion pTypeExternalMemoryHandleTypes instance Zero AllocatorCreateInfo where zero = AllocatorCreateInfo@@ -3132,6 +3186,7 @@ zero Nothing Nothing+ zero zero zero
src/lib.cpp view
@@ -6,6 +6,14 @@ #pragma clang diagnostic ignored "-Wnullability-completeness" #endif +// To match the vulkan headers this is generated against+#define VMA_DEDICATED_ALLOCATION 1+#define VMA_BIND_MEMORY2 1+#define VMA_MEMORY_BUDGET 1+#define VMA_BUFFER_DEVICE_ADDRESS 1+#define VMA_MEMORY_PRIORITY 1+#define VMA_EXTERNAL_MEMORY 1+ #include <vk_mem_alloc.h> #ifdef __clang__