packages feed

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 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__