3rdparty: Sync vkmemoryallocator to commit 5a53a198945ba8260fbc58fadb788745ce6aa263

This commit is contained in:
JordanTheToaster 2024-12-08 23:16:54 +00:00 committed by Ty
parent 19882dc160
commit 4b8890c438
2 changed files with 958 additions and 88 deletions

View File

@ -95,6 +95,7 @@ See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-me
- \subpage enabling_buffer_device_address - \subpage enabling_buffer_device_address
- \subpage vk_ext_memory_priority - \subpage vk_ext_memory_priority
- \subpage vk_amd_device_coherent_memory - \subpage vk_amd_device_coherent_memory
- \subpage vk_khr_external_memory_win32
- \subpage general_considerations - \subpage general_considerations
- [Thread safety](@ref general_considerations_thread_safety) - [Thread safety](@ref general_considerations_thread_safety)
- [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
@ -127,7 +128,9 @@ See documentation chapter: \ref statistics.
extern "C" { extern "C" {
#endif #endif
#if !defined(VULKAN_H_)
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#endif
#if !defined(VMA_VULKAN_VERSION) #if !defined(VMA_VULKAN_VERSION)
#if defined(VK_VERSION_1_3) #if defined(VK_VERSION_1_3)
@ -240,6 +243,15 @@ extern "C" {
#endif #endif
#endif #endif
// Defined to 1 when VK_KHR_external_memory_win32 device extension is defined in Vulkan headers.
#if !defined(VMA_EXTERNAL_MEMORY_WIN32)
#if VK_KHR_external_memory_win32
#define VMA_EXTERNAL_MEMORY_WIN32 1
#else
#define VMA_EXTERNAL_MEMORY_WIN32 0
#endif
#endif
// Define these macros to decorate all public functions with additional code, // Define these macros to decorate all public functions with additional code,
// before and after returned type, appropriately. This may be useful for // before and after returned type, appropriately. This may be useful for
// exporting the functions when compiling VMA as a separate library. Example: // exporting the functions when compiling VMA as a separate library. Example:
@ -459,6 +471,15 @@ typedef enum VmaAllocatorCreateFlagBits
*/ */
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100, VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,
/**
Enables usage of VK_KHR_external_memory_win32 extension in the library.
You should set this flag if you found available and enabled this device extension,
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
For more information, see \ref vk_khr_external_memory_win32.
*/
VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT = 0x00000200,
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VmaAllocatorCreateFlagBits; } VmaAllocatorCreateFlagBits;
/// See #VmaAllocatorCreateFlagBits. /// See #VmaAllocatorCreateFlagBits.
@ -1033,6 +1054,11 @@ typedef struct VmaVulkanFunctions
/// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements; PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
PFN_vkGetMemoryWin32HandleKHR VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#else
void* VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#endif
} VmaVulkanFunctions; } VmaVulkanFunctions;
/// Description of a Allocator to be created. /// Description of a Allocator to be created.
@ -1810,6 +1836,9 @@ VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
\param allocator Allocator object. \param allocator Allocator object.
\param pool Pool object. \param pool Pool object.
\param[out] pPoolStats Statistics of specified pool. \param[out] pPoolStats Statistics of specified pool.
Note that when using the pool from multiple threads, returned information may immediately
become outdated.
*/ */
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
VmaAllocator VMA_NOT_NULL allocator, VmaAllocator VMA_NOT_NULL allocator,
@ -2050,6 +2079,40 @@ VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
VmaAllocation VMA_NOT_NULL allocation, VmaAllocation VMA_NOT_NULL allocation,
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
#if VMA_EXTERNAL_MEMORY_WIN32
/**
\brief Given an allocation, returns Win32 handle that may be imported by other processes or APIs.
\param hTargetProcess Must be a valid handle to target process or null. If it's null, the function returns
handle for the current process.
\param[out] pHandle Output parameter that returns the handle.
The function fills `pHandle` with handle that can be used in target process.
The handle is fetched using function `vkGetMemoryWin32HandleKHR`.
When no longer needed, you must close it using:
\code
CloseHandle(handle);
\endcode
You can close it any time, before or after destroying the allocation object.
It is reference-counted internally by Windows.
Note the handle is returned for the entire `VkDeviceMemory` block that the allocation belongs to.
If the allocation is sub-allocated from a larger block, you may need to consider the offset of the allocation
(VmaAllocationInfo::offset).
If the function fails with `VK_ERROR_FEATURE_NOT_PRESENT` error code, please double-check
that VmaVulkanFunctions::vkGetMemoryWin32HandleKHR function pointer is set, e.g. either by using `VMA_DYNAMIC_VULKAN_FUNCTIONS`
or by manually passing it through VmaAllocatorCreateInfo::pVulkanFunctions.
For more information, see chapter \ref vk_khr_external_memory_win32.
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle);
#endif // VMA_EXTERNAL_MEMORY_WIN32
/** \brief Maps memory represented by given allocation and returns pointer to it. /** \brief Maps memory represented by given allocation and returns pointer to it.
Maps memory represented by given allocation to make it accessible to CPU code. Maps memory represented by given allocation to make it accessible to CPU code.
@ -3097,7 +3160,7 @@ static void vma_aligned_free(void* VMA_NULLABLE ptr)
std::shared_mutex m_Mutex; std::shared_mutex m_Mutex;
}; };
#define VMA_RW_MUTEX VmaRWMutex #define VMA_RW_MUTEX VmaRWMutex
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 #elif defined(_WIN32) && defined(WINVER) && defined(SRWLOCK_INIT) && WINVER >= 0x0600
// Use SRWLOCK from WinAPI. // Use SRWLOCK from WinAPI.
// Minimum supported client = Windows Vista, server = Windows Server 2008. // Minimum supported client = Windows Vista, server = Windows Server 2008.
class VmaRWMutex class VmaRWMutex
@ -3838,12 +3901,6 @@ struct VmaBufferImageUsage
const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0); const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);
static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept
{
using std::swap;
swap(lhs.Value, rhs.Value);
}
VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,
bool useKhrMaintenance5) bool useKhrMaintenance5)
{ {
@ -6073,6 +6130,84 @@ private:
#endif // _VMA_MAPPING_HYSTERESIS #endif // _VMA_MAPPING_HYSTERESIS
#if VMA_EXTERNAL_MEMORY_WIN32
class VmaWin32Handle
{
public:
VmaWin32Handle() noexcept : m_hHandle(VMA_NULL) { }
explicit VmaWin32Handle(HANDLE hHandle) noexcept : m_hHandle(hHandle) { }
~VmaWin32Handle() noexcept { if (m_hHandle != VMA_NULL) { ::CloseHandle(m_hHandle); } }
VMA_CLASS_NO_COPY_NO_MOVE(VmaWin32Handle)
public:
// Strengthened
VkResult GetHandle(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, bool useMutex, HANDLE* pHandle) noexcept
{
*pHandle = VMA_NULL;
// Try to get handle first.
if (m_hHandle != VMA_NULL)
{
*pHandle = Duplicate(hTargetProcess);
return VK_SUCCESS;
}
VkResult res = VK_SUCCESS;
// If failed, try to create it.
{
VmaMutexLockWrite lock(m_Mutex, useMutex);
if (m_hHandle == VMA_NULL)
{
res = Create(device, memory, pvkGetMemoryWin32HandleKHR, &m_hHandle);
}
}
*pHandle = Duplicate(hTargetProcess);
return res;
}
operator bool() const noexcept { return m_hHandle != VMA_NULL; }
private:
// Not atomic
static VkResult Create(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE* pHandle) noexcept
{
VkResult res = VK_ERROR_FEATURE_NOT_PRESENT;
if (pvkGetMemoryWin32HandleKHR != VMA_NULL)
{
VkMemoryGetWin32HandleInfoKHR handleInfo{ };
handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
handleInfo.memory = memory;
handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
res = pvkGetMemoryWin32HandleKHR(device, &handleInfo, pHandle);
}
return res;
}
HANDLE Duplicate(HANDLE hTargetProcess = VMA_NULL) const noexcept
{
if (!m_hHandle)
return m_hHandle;
HANDLE hCurrentProcess = ::GetCurrentProcess();
HANDLE hDupHandle = VMA_NULL;
if (!::DuplicateHandle(hCurrentProcess, m_hHandle, hTargetProcess ? hTargetProcess : hCurrentProcess, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
VMA_ASSERT(0 && "Failed to duplicate handle.");
}
return hDupHandle;
}
private:
HANDLE m_hHandle;
VMA_RW_MUTEX m_Mutex; // Protects access m_Handle
};
#else
class VmaWin32Handle
{
// ABI compatibility
void* placeholder = VMA_NULL;
VMA_RW_MUTEX placeholder2;
};
#endif // VMA_EXTERNAL_MEMORY_WIN32
#ifndef _VMA_DEVICE_MEMORY_BLOCK #ifndef _VMA_DEVICE_MEMORY_BLOCK
/* /*
Represents a single block of device memory (`VkDeviceMemory`) with all the Represents a single block of device memory (`VkDeviceMemory`) with all the
@ -6139,7 +6274,13 @@ public:
VkDeviceSize allocationLocalOffset, VkDeviceSize allocationLocalOffset,
VkImage hImage, VkImage hImage,
const void* pNext); const void* pNext);
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult CreateWin32Handle(
const VmaAllocator hAllocator,
PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR,
HANDLE hTargetProcess,
HANDLE* pHandle)noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private: private:
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
uint32_t m_MemoryTypeIndex; uint32_t m_MemoryTypeIndex;
@ -6155,10 +6296,18 @@ private:
VmaMappingHysteresis m_MappingHysteresis; VmaMappingHysteresis m_MappingHysteresis;
uint32_t m_MapCount; uint32_t m_MapCount;
void* m_pMappedData; void* m_pMappedData;
VmaWin32Handle m_Handle;
}; };
#endif // _VMA_DEVICE_MEMORY_BLOCK #endif // _VMA_DEVICE_MEMORY_BLOCK
#ifndef _VMA_ALLOCATION_T #ifndef _VMA_ALLOCATION_T
struct VmaAllocationExtraData
{
void* m_pMappedData = VMA_NULL; // Not null means memory is mapped.
VmaWin32Handle m_Handle;
};
struct VmaAllocation_T struct VmaAllocation_T
{ {
friend struct VmaDedicatedAllocationListItemTraits; friend struct VmaDedicatedAllocationListItemTraits;
@ -6191,12 +6340,14 @@ public:
bool mapped); bool mapped);
// pMappedData not null means allocation is created with MAPPED flag. // pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation( void InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool, VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
VmaSuballocationType suballocationType, VmaSuballocationType suballocationType,
void* pMappedData, void* pMappedData,
VkDeviceSize size); VkDeviceSize size);
void Destroy(VmaAllocator allocator);
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
VkDeviceSize GetAlignment() const { return m_Alignment; } VkDeviceSize GetAlignment() const { return m_Alignment; }
@ -6240,6 +6391,10 @@ public:
void PrintParameters(class VmaJsonWriter& json) const; void PrintParameters(class VmaJsonWriter& json) const;
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* hHandle) noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private: private:
// Allocation out of VmaDeviceMemoryBlock. // Allocation out of VmaDeviceMemoryBlock.
struct BlockAllocation struct BlockAllocation
@ -6252,7 +6407,7 @@ private:
{ {
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
VkDeviceMemory m_hMemory; VkDeviceMemory m_hMemory;
void* m_pMappedData; // Not null means memory is mapped. VmaAllocationExtraData* m_ExtraData;
VmaAllocation_T* m_Prev; VmaAllocation_T* m_Prev;
VmaAllocation_T* m_Next; VmaAllocation_T* m_Next;
}; };
@ -6277,6 +6432,8 @@ private:
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown. VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.
#endif #endif
void EnsureExtraData(VmaAllocator hAllocator);
}; };
#endif // _VMA_ALLOCATION_T #endif // _VMA_ALLOCATION_T
@ -10075,6 +10232,7 @@ public:
bool m_UseExtMemoryPriority; bool m_UseExtMemoryPriority;
bool m_UseKhrMaintenance4; bool m_UseKhrMaintenance4;
bool m_UseKhrMaintenance5; bool m_UseKhrMaintenance5;
bool m_UseKhrExternalMemoryWin32;
const VkDevice m_hDevice; const VkDevice m_hDevice;
const VkInstance m_hInstance; const VkInstance m_hInstance;
const bool m_AllocationCallbacksSpecified; const bool m_AllocationCallbacksSpecified;
@ -10438,7 +10596,7 @@ VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
m_Id(0), m_Id(0),
m_hMemory(VK_NULL_HANDLE), m_hMemory(VK_NULL_HANDLE),
m_MapCount(0), m_MapCount(0),
m_pMappedData(VMA_NULL) {} m_pMappedData(VMA_NULL){}
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
{ {
@ -10681,6 +10839,14 @@ VkResult VmaDeviceMemoryBlock::BindImageMemory(
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaDeviceMemoryBlock::CreateWin32Handle(const VmaAllocator hAllocator, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
VMA_ASSERT(pHandle);
return m_Handle.GetHandle(hAllocator->m_hDevice, m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS #endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
#ifndef _VMA_ALLOCATION_T_FUNCTIONS #ifndef _VMA_ALLOCATION_T_FUNCTIONS
@ -10733,6 +10899,7 @@ void VmaAllocation_T::InitBlockAllocation(
} }
void VmaAllocation_T::InitDedicatedAllocation( void VmaAllocation_T::InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool, VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
@ -10747,16 +10914,29 @@ void VmaAllocation_T::InitDedicatedAllocation(
m_Size = size; m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex; m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType; m_SuballocationType = (uint8_t)suballocationType;
if(pMappedData != VMA_NULL) m_DedicatedAllocation.m_ExtraData = VMA_NULL;
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
if (pMappedData != VMA_NULL)
{ {
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
EnsureExtraData(allocator);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = pMappedData;
}
}
void VmaAllocation_T::Destroy(VmaAllocator allocator)
{
FreeName(allocator);
if (GetType() == ALLOCATION_TYPE_DEDICATED)
{
vma_delete(allocator, m_DedicatedAllocation.m_ExtraData);
} }
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
} }
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName) void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
@ -10861,8 +11041,9 @@ void* VmaAllocation_T::GetMappedData() const
} }
break; break;
case ALLOCATION_TYPE_DEDICATED: case ALLOCATION_TYPE_DEDICATED:
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap())); VMA_ASSERT((m_DedicatedAllocation.m_ExtraData != VMA_NULL && m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL) ==
return m_DedicatedAllocation.m_pMappedData; (m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_ExtraData != VMA_NULL ? m_DedicatedAllocation.m_ExtraData->m_pMappedData : VMA_NULL;
default: default:
VMA_ASSERT(0); VMA_ASSERT(0);
return VMA_NULL; return VMA_NULL;
@ -10903,12 +11084,14 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
EnsureExtraData(hAllocator);
if (m_MapCount != 0 || IsPersistentMap()) if (m_MapCount != 0 || IsPersistentMap())
{ {
if (m_MapCount < 0xFF) if (m_MapCount < 0xFF)
{ {
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); VMA_ASSERT(m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_pMappedData; *ppData = m_DedicatedAllocation.m_ExtraData->m_pMappedData;
++m_MapCount; ++m_MapCount;
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -10929,7 +11112,7 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
ppData); ppData);
if (result == VK_SUCCESS) if (result == VK_SUCCESS)
{ {
m_DedicatedAllocation.m_pMappedData = *ppData; m_DedicatedAllocation.m_ExtraData->m_pMappedData = *ppData;
m_MapCount = 1; m_MapCount = 1;
} }
return result; return result;
@ -10945,7 +11128,8 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
--m_MapCount; --m_MapCount;
if (m_MapCount == 0 && !IsPersistentMap()) if (m_MapCount == 0 && !IsPersistentMap())
{ {
m_DedicatedAllocation.m_pMappedData = VMA_NULL; VMA_ASSERT(m_DedicatedAllocation.m_ExtraData != VMA_NULL);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)( (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
hAllocator->m_hDevice, hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory); m_DedicatedAllocation.m_hMemory);
@ -10981,8 +11165,33 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString(m_pName); json.WriteString(m_pName);
} }
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaAllocation_T::GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
auto pvkGetMemoryWin32HandleKHR = hAllocator->GetVulkanFunctions().vkGetMemoryWin32HandleKHR;
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->CreateWin32Handle(hAllocator, pvkGetMemoryWin32HandleKHR, hTargetProcess, pHandle);
case ALLOCATION_TYPE_DEDICATED:
EnsureExtraData(hAllocator);
return m_DedicatedAllocation.m_ExtraData->m_Handle.GetHandle(hAllocator->m_hDevice, m_DedicatedAllocation.m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
default:
VMA_ASSERT(0);
return VK_ERROR_FEATURE_NOT_PRESENT;
}
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED #endif // VMA_STATS_STRING_ENABLED
void VmaAllocation_T::EnsureExtraData(VmaAllocator hAllocator)
{
if (m_DedicatedAllocation.m_ExtraData == VMA_NULL)
{
m_DedicatedAllocation.m_ExtraData = vma_new(hAllocator, VmaAllocationExtraData)();
}
}
void VmaAllocation_T::FreeName(VmaAllocator hAllocator) void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
{ {
if(m_pName) if(m_pName)
@ -11399,6 +11608,10 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
} }
IncrementallySortBlocks(); IncrementallySortBlocks();
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
hAllocation->Destroy(m_hAllocator);
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
} }
// Destruction of a free block. Deferred until this point, outside of mutex // Destruction of a free block. Deferred until this point, outside of mutex
@ -11409,9 +11622,6 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
pBlockToDelete->Destroy(m_hAllocator); pBlockToDelete->Destroy(m_hAllocator);
vma_delete(m_hAllocator, pBlockToDelete); vma_delete(m_hAllocator, pBlockToDelete);
} }
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
} }
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
@ -12711,6 +12921,7 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0), m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),
m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0), m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),
m_UseKhrExternalMemoryWin32((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0),
m_hDevice(pCreateInfo->device), m_hDevice(pCreateInfo->device),
m_hInstance(pCreateInfo->instance), m_hInstance(pCreateInfo->instance),
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
@ -12802,6 +13013,19 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
} }
#endif #endif
#if !(VMA_KHR_MAINTENANCE5)
if(m_UseKhrMaintenance5)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_EXTERNAL_MEMORY_WIN32)
if(m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
@ -13026,7 +13250,9 @@ void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVul
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements); VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements); VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_COPY_IF_NOT_NULL(vkGetMemoryWin32HandleKHR);
#endif
#undef VMA_COPY_IF_NOT_NULL #undef VMA_COPY_IF_NOT_NULL
} }
@ -13128,7 +13354,12 @@ void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR"); VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");
} }
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_FETCH_DEVICE_FUNC(vkGetMemoryWin32HandleKHR, PFN_vkGetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR");
}
#endif
#undef VMA_FETCH_DEVICE_FUNC #undef VMA_FETCH_DEVICE_FUNC
#undef VMA_FETCH_INSTANCE_FUNC #undef VMA_FETCH_INSTANCE_FUNC
} }
@ -13177,6 +13408,12 @@ void VmaAllocator_T::ValidateVulkanFunctions()
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
} }
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(m_VulkanFunctions.vkGetMemoryWin32HandleKHR != VMA_NULL);
}
#endif
// Not validating these due to suspected driver bugs with these function // Not validating these due to suspected driver bugs with these function
// pointers being null despite correct extension or Vulkan version is enabled. // pointers being null despite correct extension or Vulkan version is enabled.
@ -13527,7 +13764,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
} }
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed); *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); (*pAllocation)->InitDedicatedAllocation(this, pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
if (isUserDataString) if (isUserDataString)
(*pAllocation)->SetName(this, (const char*)pUserData); (*pAllocation)->SetName(this, (const char*)pUserData);
else else
@ -13863,8 +14100,6 @@ void VmaAllocator_T::FreeMemory(
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
} }
allocation->FreeName(this);
switch(allocation->GetType()) switch(allocation->GetType())
{ {
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
@ -14335,7 +14570,6 @@ VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
} }
return res; return res;
} }
VMA_FALLTHROUGH; // Fallthrough
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return hAllocation->DedicatedAllocMap(this, ppData); return hAllocation->DedicatedAllocMap(this, ppData);
default: default:
@ -14549,6 +14783,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
allocation->Destroy(this);
m_AllocationObjectAllocator.Free(allocation); m_AllocationObjectAllocator.Free(allocation);
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex); VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);
@ -16169,7 +16404,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
pImageCreateInfo, pImageCreateInfo,
allocator->GetAllocationCallbacks(), allocator->GetAllocationCallbacks(),
pImage); pImage);
if(res >= 0) if(res == VK_SUCCESS)
{ {
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
@ -16194,14 +16429,14 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
1, // allocationCount 1, // allocationCount
pAllocation); pAllocation);
if(res >= 0) if(res == VK_SUCCESS)
{ {
// 3. Bind image with memory. // 3. Bind image with memory.
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{ {
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
} }
if(res >= 0) if(res == VK_SUCCESS)
{ {
// All steps succeeded. // All steps succeeded.
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
@ -16434,6 +16669,15 @@ VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock V
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
} }
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle)
{
VMA_ASSERT(allocator && allocation && pHandle);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return allocation->GetWin32Handle(allocator, hTargetProcess, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED #endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_PUBLIC_INTERFACE #endif // _VMA_PUBLIC_INTERFACE
#endif // VMA_IMPLEMENTATION #endif // VMA_IMPLEMENTATION
@ -16567,6 +16811,7 @@ VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
VK_KHR_external_memory_win32 | #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
Example with fetching pointers to Vulkan functions dynamically: Example with fetching pointers to Vulkan functions dynamically:
@ -17053,7 +17298,7 @@ implementation whether the allocation succeeds or fails. You can change this beh
by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
not made if it would exceed the budget or if the budget is already exceeded. not made if it would exceed the budget or if the budget is already exceeded.
VMA then tries to make the allocation from the next eligible Vulkan memory type. VMA then tries to make the allocation from the next eligible Vulkan memory type.
The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. If all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
when creating resources that are not essential for the application (e.g. the texture when creating resources that are not essential for the application (e.g. the texture
of a specific object) and not to pass it when creating critically important resources of a specific object) and not to pass it when creating critically important resources
@ -18193,7 +18438,8 @@ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VkBuffer buf; VkBuffer buf;
VmaAllocation alloc; VmaAllocation alloc;
VmaAllocationInfo allocInfo; VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); VkResult result = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
// Check result...
VkMemoryPropertyFlags memPropFlags; VkMemoryPropertyFlags memPropFlags;
vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags); vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
@ -18204,10 +18450,24 @@ if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
// [Executed in runtime]: // [Executed in runtime]:
memcpy(allocInfo.pMappedData, myData, myDataSize); memcpy(allocInfo.pMappedData, myData, myDataSize);
result = vmaFlushAllocation(allocator, alloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = buf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
} }
else else
{ {
// Allocation ended up in a non-mappable memory - need to transfer. // Allocation ended up in a non-mappable memory - a transfer using a staging buffer is required.
VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
stagingBufCreateInfo.size = 65536; stagingBufCreateInfo.size = 65536;
stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
@ -18220,18 +18480,46 @@ else
VkBuffer stagingBuf; VkBuffer stagingBuf;
VmaAllocation stagingAlloc; VmaAllocation stagingAlloc;
VmaAllocationInfo stagingAllocInfo; VmaAllocationInfo stagingAllocInfo;
vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo, result = vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, stagingAllocInfo); &stagingBuf, &stagingAlloc, &stagingAllocInfo);
// Check result...
// [Executed in runtime]: // [Executed in runtime]:
memcpy(stagingAllocInfo.pMappedData, myData, myDataSize); memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE); result = vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
//vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT // Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = stagingBuf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
VkBufferCopy bufCopy = { VkBufferCopy bufCopy = {
0, // srcOffset 0, // srcOffset
0, // dstOffset, 0, // dstOffset,
myDataSize); // size myDataSize, // size
};
vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy); vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
VkBufferMemoryBarrier bufMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // We created a uniform buffer
bufMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.buffer = buf;
bufMemBarrier2.offset = 0;
bufMemBarrier2.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier2, 0, nullptr);
} }
\endcode \endcode
@ -18264,14 +18552,22 @@ Please check "CONFIGURATION SECTION" in the code to find macros that you can def
before each include of this file or change directly in this file to provide before each include of this file or change directly in this file to provide
your own implementation of basic facilities like assert, `min()` and `max()` functions, your own implementation of basic facilities like assert, `min()` and `max()` functions,
mutex, atomic etc. mutex, atomic etc.
The library uses its own implementation of containers by default, but you can switch to using
STL containers instead.
For example, define `VMA_ASSERT(expr)` before including the library to provide For example, define `VMA_ASSERT(expr)` before including the library to provide
custom implementation of the assertion, compatible with your project. custom implementation of the assertion, compatible with your project.
By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
and empty otherwise. and empty otherwise.
Similarly, you can define `VMA_LEAK_LOG_FORMAT` macro to enable printing of leaked (unfreed) allocations,
including their names and other parameters. Example:
\code
#define VMA_LEAK_LOG_FORMAT(format, ...) do { \
printf((format), __VA_ARGS__); \
printf("\n"); \
} while(false)
\endcode
\section config_Vulkan_functions Pointers to Vulkan functions \section config_Vulkan_functions Pointers to Vulkan functions
There are multiple ways to import pointers to Vulkan functions in the library. There are multiple ways to import pointers to Vulkan functions in the library.
@ -18526,6 +18822,145 @@ Example use of this extension can be found in the code of the sample and test su
accompanying this library. accompanying this library.
\page vk_khr_external_memory_win32 VK_KHR_external_memory_win32
On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 `HANDLE`
of a `VkDeviceMemory` block, to be able to reference the memory on other Vulkan logical devices or instances,
in multiple processes, and/or in multiple APIs.
VMA offers support for it.
\section vk_khr_external_memory_win32_initialization Initialization
1) Make sure the extension is defined in the code by including following header before including VMA:
\code
#include <vulkan/vulkan_win32.h>
\endcode
2) Check if "VK_KHR_external_memory_win32" is available among device extensions.
Enable it when creating the `VkDevice` object.
3) Enable the usage of this extension in VMA by setting flag #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
when calling vmaCreateAllocator().
4) Make sure that VMA has access to the `vkGetMemoryWin32HandleKHR` function by either enabling `VMA_DYNAMIC_VULKAN_FUNCTIONS` macro
or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly.
For more information, see \ref quick_start_initialization_importing_vulkan_functions.
\section vk_khr_external_memory_win32_preparations Preparations
You can find example usage among tests, in file "Tests.cpp", function `TestWin32Handles()`.
To use the extenion, buffers need to be created with `VkExternalMemoryBufferCreateInfoKHR` attached to their `pNext` chain,
and memory allocations need to be made with `VkExportMemoryAllocateInfoKHR` attached to their `pNext` chain.
To make use of them, you need to use \ref custom_memory_pools. Example:
\code
// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo exampleAllocCreateInfo = {};
exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Find memory type index to use for the custom pool.
uint32_t memTypeIndex;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,
&exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
// Check res...
// Create a custom pool.
constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {
VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.memoryTypeIndex = memTypeIndex;
poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;
VmaPool pool;
res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyPool(g_Allocator, pool);
\endcode
Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged
for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block.
No copy is made internally. This is why variable `exportMemAllocInfo` is defined as `static`.
\section vk_khr_external_memory_win32_memory_allocation Memory allocation
Finally, you can create a buffer with an allocation out of the custom pool.
The buffer should use same flags as the sample buffer used to find the memory type.
It should also specify `VkExternalMemoryBufferCreateInfoKHR` in its `pNext` chain.
\code
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = // Your desired buffer size.
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool; // It is enough to set this one member.
VkBuffer buf;
VmaAllocation alloc;
res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyBuffer(g_Allocator, buf, alloc);
\endcode
If you need each allocation to have its own device memory block and start at offset 0, you can still do
by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.
\section vk_khr_external_memory_win32_exporting_win32_handle Exporting Win32 handle
After the allocation is created, you can acquire a Win32 `HANDLE` to the `VkDeviceMemory` block it belongs to.
VMA function vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.
\code
HANDLE handle;
res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
\endcode
Documentation of the VK_KHR_external_memory_win32 extension states that:
> If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.
This is ensured automatically inside VMA.
The library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed.
Every time you call vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.
For further information, please check documentation of the vmaGetMemoryWin32Handle() function.
\page enabling_buffer_device_address Enabling buffer device address \page enabling_buffer_device_address Enabling buffer device address
Device extension VK_KHR_buffer_device_address Device extension VK_KHR_buffer_device_address

View File

@ -95,6 +95,7 @@ See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-me
- \subpage enabling_buffer_device_address - \subpage enabling_buffer_device_address
- \subpage vk_ext_memory_priority - \subpage vk_ext_memory_priority
- \subpage vk_amd_device_coherent_memory - \subpage vk_amd_device_coherent_memory
- \subpage vk_khr_external_memory_win32
- \subpage general_considerations - \subpage general_considerations
- [Thread safety](@ref general_considerations_thread_safety) - [Thread safety](@ref general_considerations_thread_safety)
- [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
@ -127,7 +128,9 @@ See documentation chapter: \ref statistics.
extern "C" { extern "C" {
#endif #endif
#if !defined(VULKAN_H_)
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#endif
#if !defined(VMA_VULKAN_VERSION) #if !defined(VMA_VULKAN_VERSION)
#if defined(VK_VERSION_1_3) #if defined(VK_VERSION_1_3)
@ -240,6 +243,15 @@ extern "C" {
#endif #endif
#endif #endif
// Defined to 1 when VK_KHR_external_memory_win32 device extension is defined in Vulkan headers.
#if !defined(VMA_EXTERNAL_MEMORY_WIN32)
#if VK_KHR_external_memory_win32
#define VMA_EXTERNAL_MEMORY_WIN32 1
#else
#define VMA_EXTERNAL_MEMORY_WIN32 0
#endif
#endif
// Define these macros to decorate all public functions with additional code, // Define these macros to decorate all public functions with additional code,
// before and after returned type, appropriately. This may be useful for // before and after returned type, appropriately. This may be useful for
// exporting the functions when compiling VMA as a separate library. Example: // exporting the functions when compiling VMA as a separate library. Example:
@ -459,6 +471,15 @@ typedef enum VmaAllocatorCreateFlagBits
*/ */
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100, VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,
/**
Enables usage of VK_KHR_external_memory_win32 extension in the library.
You should set this flag if you found available and enabled this device extension,
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
For more information, see \ref vk_khr_external_memory_win32.
*/
VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT = 0x00000200,
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VmaAllocatorCreateFlagBits; } VmaAllocatorCreateFlagBits;
/// See #VmaAllocatorCreateFlagBits. /// See #VmaAllocatorCreateFlagBits.
@ -1033,6 +1054,11 @@ typedef struct VmaVulkanFunctions
/// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements; PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
PFN_vkGetMemoryWin32HandleKHR VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#else
void* VMA_NULLABLE vkGetMemoryWin32HandleKHR;
#endif
} VmaVulkanFunctions; } VmaVulkanFunctions;
/// Description of a Allocator to be created. /// Description of a Allocator to be created.
@ -1810,6 +1836,9 @@ VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
\param allocator Allocator object. \param allocator Allocator object.
\param pool Pool object. \param pool Pool object.
\param[out] pPoolStats Statistics of specified pool. \param[out] pPoolStats Statistics of specified pool.
Note that when using the pool from multiple threads, returned information may immediately
become outdated.
*/ */
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
VmaAllocator VMA_NOT_NULL allocator, VmaAllocator VMA_NOT_NULL allocator,
@ -2050,6 +2079,40 @@ VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
VmaAllocation VMA_NOT_NULL allocation, VmaAllocation VMA_NOT_NULL allocation,
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
#if VMA_EXTERNAL_MEMORY_WIN32
/**
\brief Given an allocation, returns Win32 handle that may be imported by other processes or APIs.
\param hTargetProcess Must be a valid handle to target process or null. If it's null, the function returns
handle for the current process.
\param[out] pHandle Output parameter that returns the handle.
The function fills `pHandle` with handle that can be used in target process.
The handle is fetched using function `vkGetMemoryWin32HandleKHR`.
When no longer needed, you must close it using:
\code
CloseHandle(handle);
\endcode
You can close it any time, before or after destroying the allocation object.
It is reference-counted internally by Windows.
Note the handle is returned for the entire `VkDeviceMemory` block that the allocation belongs to.
If the allocation is sub-allocated from a larger block, you may need to consider the offset of the allocation
(VmaAllocationInfo::offset).
If the function fails with `VK_ERROR_FEATURE_NOT_PRESENT` error code, please double-check
that VmaVulkanFunctions::vkGetMemoryWin32HandleKHR function pointer is set, e.g. either by using `VMA_DYNAMIC_VULKAN_FUNCTIONS`
or by manually passing it through VmaAllocatorCreateInfo::pVulkanFunctions.
For more information, see chapter \ref vk_khr_external_memory_win32.
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle);
#endif // VMA_EXTERNAL_MEMORY_WIN32
/** \brief Maps memory represented by given allocation and returns pointer to it. /** \brief Maps memory represented by given allocation and returns pointer to it.
Maps memory represented by given allocation to make it accessible to CPU code. Maps memory represented by given allocation to make it accessible to CPU code.
@ -3097,7 +3160,7 @@ static void vma_aligned_free(void* VMA_NULLABLE ptr)
std::shared_mutex m_Mutex; std::shared_mutex m_Mutex;
}; };
#define VMA_RW_MUTEX VmaRWMutex #define VMA_RW_MUTEX VmaRWMutex
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 #elif defined(_WIN32) && defined(WINVER) && defined(SRWLOCK_INIT) && WINVER >= 0x0600
// Use SRWLOCK from WinAPI. // Use SRWLOCK from WinAPI.
// Minimum supported client = Windows Vista, server = Windows Server 2008. // Minimum supported client = Windows Vista, server = Windows Server 2008.
class VmaRWMutex class VmaRWMutex
@ -3838,12 +3901,6 @@ struct VmaBufferImageUsage
const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0); const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);
static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept
{
using std::swap;
swap(lhs.Value, rhs.Value);
}
VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,
bool useKhrMaintenance5) bool useKhrMaintenance5)
{ {
@ -6073,6 +6130,84 @@ private:
#endif // _VMA_MAPPING_HYSTERESIS #endif // _VMA_MAPPING_HYSTERESIS
#if VMA_EXTERNAL_MEMORY_WIN32
class VmaWin32Handle
{
public:
VmaWin32Handle() noexcept : m_hHandle(VMA_NULL) { }
explicit VmaWin32Handle(HANDLE hHandle) noexcept : m_hHandle(hHandle) { }
~VmaWin32Handle() noexcept { if (m_hHandle != VMA_NULL) { ::CloseHandle(m_hHandle); } }
VMA_CLASS_NO_COPY_NO_MOVE(VmaWin32Handle)
public:
// Strengthened
VkResult GetHandle(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, bool useMutex, HANDLE* pHandle) noexcept
{
*pHandle = VMA_NULL;
// Try to get handle first.
if (m_hHandle != VMA_NULL)
{
*pHandle = Duplicate(hTargetProcess);
return VK_SUCCESS;
}
VkResult res = VK_SUCCESS;
// If failed, try to create it.
{
VmaMutexLockWrite lock(m_Mutex, useMutex);
if (m_hHandle == VMA_NULL)
{
res = Create(device, memory, pvkGetMemoryWin32HandleKHR, &m_hHandle);
}
}
*pHandle = Duplicate(hTargetProcess);
return res;
}
operator bool() const noexcept { return m_hHandle != VMA_NULL; }
private:
// Not atomic
static VkResult Create(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE* pHandle) noexcept
{
VkResult res = VK_ERROR_FEATURE_NOT_PRESENT;
if (pvkGetMemoryWin32HandleKHR != VMA_NULL)
{
VkMemoryGetWin32HandleInfoKHR handleInfo{ };
handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
handleInfo.memory = memory;
handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
res = pvkGetMemoryWin32HandleKHR(device, &handleInfo, pHandle);
}
return res;
}
HANDLE Duplicate(HANDLE hTargetProcess = VMA_NULL) const noexcept
{
if (!m_hHandle)
return m_hHandle;
HANDLE hCurrentProcess = ::GetCurrentProcess();
HANDLE hDupHandle = VMA_NULL;
if (!::DuplicateHandle(hCurrentProcess, m_hHandle, hTargetProcess ? hTargetProcess : hCurrentProcess, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
VMA_ASSERT(0 && "Failed to duplicate handle.");
}
return hDupHandle;
}
private:
HANDLE m_hHandle;
VMA_RW_MUTEX m_Mutex; // Protects access m_Handle
};
#else
class VmaWin32Handle
{
// ABI compatibility
void* placeholder = VMA_NULL;
VMA_RW_MUTEX placeholder2;
};
#endif // VMA_EXTERNAL_MEMORY_WIN32
#ifndef _VMA_DEVICE_MEMORY_BLOCK #ifndef _VMA_DEVICE_MEMORY_BLOCK
/* /*
Represents a single block of device memory (`VkDeviceMemory`) with all the Represents a single block of device memory (`VkDeviceMemory`) with all the
@ -6139,7 +6274,13 @@ public:
VkDeviceSize allocationLocalOffset, VkDeviceSize allocationLocalOffset,
VkImage hImage, VkImage hImage,
const void* pNext); const void* pNext);
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult CreateWin32Handle(
const VmaAllocator hAllocator,
PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR,
HANDLE hTargetProcess,
HANDLE* pHandle)noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private: private:
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
uint32_t m_MemoryTypeIndex; uint32_t m_MemoryTypeIndex;
@ -6155,10 +6296,18 @@ private:
VmaMappingHysteresis m_MappingHysteresis; VmaMappingHysteresis m_MappingHysteresis;
uint32_t m_MapCount; uint32_t m_MapCount;
void* m_pMappedData; void* m_pMappedData;
VmaWin32Handle m_Handle;
}; };
#endif // _VMA_DEVICE_MEMORY_BLOCK #endif // _VMA_DEVICE_MEMORY_BLOCK
#ifndef _VMA_ALLOCATION_T #ifndef _VMA_ALLOCATION_T
struct VmaAllocationExtraData
{
void* m_pMappedData = VMA_NULL; // Not null means memory is mapped.
VmaWin32Handle m_Handle;
};
struct VmaAllocation_T struct VmaAllocation_T
{ {
friend struct VmaDedicatedAllocationListItemTraits; friend struct VmaDedicatedAllocationListItemTraits;
@ -6191,12 +6340,14 @@ public:
bool mapped); bool mapped);
// pMappedData not null means allocation is created with MAPPED flag. // pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation( void InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool, VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
VmaSuballocationType suballocationType, VmaSuballocationType suballocationType,
void* pMappedData, void* pMappedData,
VkDeviceSize size); VkDeviceSize size);
void Destroy(VmaAllocator allocator);
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
VkDeviceSize GetAlignment() const { return m_Alignment; } VkDeviceSize GetAlignment() const { return m_Alignment; }
@ -6240,6 +6391,10 @@ public:
void PrintParameters(class VmaJsonWriter& json) const; void PrintParameters(class VmaJsonWriter& json) const;
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* hHandle) noexcept;
#endif // VMA_EXTERNAL_MEMORY_WIN32
private: private:
// Allocation out of VmaDeviceMemoryBlock. // Allocation out of VmaDeviceMemoryBlock.
struct BlockAllocation struct BlockAllocation
@ -6252,7 +6407,7 @@ private:
{ {
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
VkDeviceMemory m_hMemory; VkDeviceMemory m_hMemory;
void* m_pMappedData; // Not null means memory is mapped. VmaAllocationExtraData* m_ExtraData;
VmaAllocation_T* m_Prev; VmaAllocation_T* m_Prev;
VmaAllocation_T* m_Next; VmaAllocation_T* m_Next;
}; };
@ -6277,6 +6432,8 @@ private:
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown. VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.
#endif #endif
void EnsureExtraData(VmaAllocator hAllocator);
}; };
#endif // _VMA_ALLOCATION_T #endif // _VMA_ALLOCATION_T
@ -10075,6 +10232,7 @@ public:
bool m_UseExtMemoryPriority; bool m_UseExtMemoryPriority;
bool m_UseKhrMaintenance4; bool m_UseKhrMaintenance4;
bool m_UseKhrMaintenance5; bool m_UseKhrMaintenance5;
bool m_UseKhrExternalMemoryWin32;
const VkDevice m_hDevice; const VkDevice m_hDevice;
const VkInstance m_hInstance; const VkInstance m_hInstance;
const bool m_AllocationCallbacksSpecified; const bool m_AllocationCallbacksSpecified;
@ -10438,7 +10596,7 @@ VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
m_Id(0), m_Id(0),
m_hMemory(VK_NULL_HANDLE), m_hMemory(VK_NULL_HANDLE),
m_MapCount(0), m_MapCount(0),
m_pMappedData(VMA_NULL) {} m_pMappedData(VMA_NULL){}
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
{ {
@ -10681,6 +10839,14 @@ VkResult VmaDeviceMemoryBlock::BindImageMemory(
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaDeviceMemoryBlock::CreateWin32Handle(const VmaAllocator hAllocator, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
VMA_ASSERT(pHandle);
return m_Handle.GetHandle(hAllocator->m_hDevice, m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS #endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
#ifndef _VMA_ALLOCATION_T_FUNCTIONS #ifndef _VMA_ALLOCATION_T_FUNCTIONS
@ -10733,6 +10899,7 @@ void VmaAllocation_T::InitBlockAllocation(
} }
void VmaAllocation_T::InitDedicatedAllocation( void VmaAllocation_T::InitDedicatedAllocation(
VmaAllocator allocator,
VmaPool hParentPool, VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
@ -10747,16 +10914,29 @@ void VmaAllocation_T::InitDedicatedAllocation(
m_Size = size; m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex; m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType; m_SuballocationType = (uint8_t)suballocationType;
if(pMappedData != VMA_NULL) m_DedicatedAllocation.m_ExtraData = VMA_NULL;
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
if (pMappedData != VMA_NULL)
{ {
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
EnsureExtraData(allocator);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = pMappedData;
}
}
void VmaAllocation_T::Destroy(VmaAllocator allocator)
{
FreeName(allocator);
if (GetType() == ALLOCATION_TYPE_DEDICATED)
{
vma_delete(allocator, m_DedicatedAllocation.m_ExtraData);
} }
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
} }
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName) void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
@ -10861,8 +11041,9 @@ void* VmaAllocation_T::GetMappedData() const
} }
break; break;
case ALLOCATION_TYPE_DEDICATED: case ALLOCATION_TYPE_DEDICATED:
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap())); VMA_ASSERT((m_DedicatedAllocation.m_ExtraData != VMA_NULL && m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL) ==
return m_DedicatedAllocation.m_pMappedData; (m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_ExtraData != VMA_NULL ? m_DedicatedAllocation.m_ExtraData->m_pMappedData : VMA_NULL;
default: default:
VMA_ASSERT(0); VMA_ASSERT(0);
return VMA_NULL; return VMA_NULL;
@ -10903,12 +11084,14 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
EnsureExtraData(hAllocator);
if (m_MapCount != 0 || IsPersistentMap()) if (m_MapCount != 0 || IsPersistentMap())
{ {
if (m_MapCount < 0xFF) if (m_MapCount < 0xFF)
{ {
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); VMA_ASSERT(m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_pMappedData; *ppData = m_DedicatedAllocation.m_ExtraData->m_pMappedData;
++m_MapCount; ++m_MapCount;
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -10929,7 +11112,7 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa
ppData); ppData);
if (result == VK_SUCCESS) if (result == VK_SUCCESS)
{ {
m_DedicatedAllocation.m_pMappedData = *ppData; m_DedicatedAllocation.m_ExtraData->m_pMappedData = *ppData;
m_MapCount = 1; m_MapCount = 1;
} }
return result; return result;
@ -10945,7 +11128,8 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
--m_MapCount; --m_MapCount;
if (m_MapCount == 0 && !IsPersistentMap()) if (m_MapCount == 0 && !IsPersistentMap())
{ {
m_DedicatedAllocation.m_pMappedData = VMA_NULL; VMA_ASSERT(m_DedicatedAllocation.m_ExtraData != VMA_NULL);
m_DedicatedAllocation.m_ExtraData->m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)( (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
hAllocator->m_hDevice, hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory); m_DedicatedAllocation.m_hMemory);
@ -10981,8 +11165,33 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString(m_pName); json.WriteString(m_pName);
} }
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VkResult VmaAllocation_T::GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* pHandle) noexcept
{
auto pvkGetMemoryWin32HandleKHR = hAllocator->GetVulkanFunctions().vkGetMemoryWin32HandleKHR;
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->CreateWin32Handle(hAllocator, pvkGetMemoryWin32HandleKHR, hTargetProcess, pHandle);
case ALLOCATION_TYPE_DEDICATED:
EnsureExtraData(hAllocator);
return m_DedicatedAllocation.m_ExtraData->m_Handle.GetHandle(hAllocator->m_hDevice, m_DedicatedAllocation.m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);
default:
VMA_ASSERT(0);
return VK_ERROR_FEATURE_NOT_PRESENT;
}
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED #endif // VMA_STATS_STRING_ENABLED
void VmaAllocation_T::EnsureExtraData(VmaAllocator hAllocator)
{
if (m_DedicatedAllocation.m_ExtraData == VMA_NULL)
{
m_DedicatedAllocation.m_ExtraData = vma_new(hAllocator, VmaAllocationExtraData)();
}
}
void VmaAllocation_T::FreeName(VmaAllocator hAllocator) void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
{ {
if(m_pName) if(m_pName)
@ -11399,6 +11608,10 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
} }
IncrementallySortBlocks(); IncrementallySortBlocks();
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
hAllocation->Destroy(m_hAllocator);
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
} }
// Destruction of a free block. Deferred until this point, outside of mutex // Destruction of a free block. Deferred until this point, outside of mutex
@ -11409,9 +11622,6 @@ void VmaBlockVector::Free(const VmaAllocation hAllocation)
pBlockToDelete->Destroy(m_hAllocator); pBlockToDelete->Destroy(m_hAllocator);
vma_delete(m_hAllocator, pBlockToDelete); vma_delete(m_hAllocator, pBlockToDelete);
} }
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
} }
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
@ -12711,6 +12921,7 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0), m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),
m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0), m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),
m_UseKhrExternalMemoryWin32((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0),
m_hDevice(pCreateInfo->device), m_hDevice(pCreateInfo->device),
m_hInstance(pCreateInfo->instance), m_hInstance(pCreateInfo->instance),
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
@ -12802,6 +13013,19 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
} }
#endif #endif
#if !(VMA_KHR_MAINTENANCE5)
if(m_UseKhrMaintenance5)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if !(VMA_EXTERNAL_MEMORY_WIN32)
if(m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
@ -13026,7 +13250,9 @@ void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVul
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements); VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements); VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_COPY_IF_NOT_NULL(vkGetMemoryWin32HandleKHR);
#endif
#undef VMA_COPY_IF_NOT_NULL #undef VMA_COPY_IF_NOT_NULL
} }
@ -13128,7 +13354,12 @@ void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR"); VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");
} }
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_FETCH_DEVICE_FUNC(vkGetMemoryWin32HandleKHR, PFN_vkGetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR");
}
#endif
#undef VMA_FETCH_DEVICE_FUNC #undef VMA_FETCH_DEVICE_FUNC
#undef VMA_FETCH_INSTANCE_FUNC #undef VMA_FETCH_INSTANCE_FUNC
} }
@ -13177,6 +13408,12 @@ void VmaAllocator_T::ValidateVulkanFunctions()
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
} }
#endif #endif
#if VMA_EXTERNAL_MEMORY_WIN32
if (m_UseKhrExternalMemoryWin32)
{
VMA_ASSERT(m_VulkanFunctions.vkGetMemoryWin32HandleKHR != VMA_NULL);
}
#endif
// Not validating these due to suspected driver bugs with these function // Not validating these due to suspected driver bugs with these function
// pointers being null despite correct extension or Vulkan version is enabled. // pointers being null despite correct extension or Vulkan version is enabled.
@ -13527,7 +13764,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
} }
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed); *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); (*pAllocation)->InitDedicatedAllocation(this, pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
if (isUserDataString) if (isUserDataString)
(*pAllocation)->SetName(this, (const char*)pUserData); (*pAllocation)->SetName(this, (const char*)pUserData);
else else
@ -13863,8 +14100,6 @@ void VmaAllocator_T::FreeMemory(
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
} }
allocation->FreeName(this);
switch(allocation->GetType()) switch(allocation->GetType())
{ {
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
@ -14335,7 +14570,6 @@ VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
} }
return res; return res;
} }
VMA_FALLTHROUGH; // Fallthrough
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return hAllocation->DedicatedAllocMap(this, ppData); return hAllocation->DedicatedAllocMap(this, ppData);
default: default:
@ -14549,6 +14783,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
allocation->Destroy(this);
m_AllocationObjectAllocator.Free(allocation); m_AllocationObjectAllocator.Free(allocation);
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex); VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);
@ -16169,7 +16404,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
pImageCreateInfo, pImageCreateInfo,
allocator->GetAllocationCallbacks(), allocator->GetAllocationCallbacks(),
pImage); pImage);
if(res >= 0) if(res == VK_SUCCESS)
{ {
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
@ -16194,14 +16429,14 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
1, // allocationCount 1, // allocationCount
pAllocation); pAllocation);
if(res >= 0) if(res == VK_SUCCESS)
{ {
// 3. Bind image with memory. // 3. Bind image with memory.
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{ {
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
} }
if(res >= 0) if(res == VK_SUCCESS)
{ {
// All steps succeeded. // All steps succeeded.
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
@ -16434,6 +16669,15 @@ VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock V
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
} }
} }
#if VMA_EXTERNAL_MEMORY_WIN32
VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle)
{
VMA_ASSERT(allocator && allocation && pHandle);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return allocation->GetWin32Handle(allocator, hTargetProcess, pHandle);
}
#endif // VMA_EXTERNAL_MEMORY_WIN32
#endif // VMA_STATS_STRING_ENABLED #endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_PUBLIC_INTERFACE #endif // _VMA_PUBLIC_INTERFACE
#endif // VMA_IMPLEMENTATION #endif // VMA_IMPLEMENTATION
@ -16567,6 +16811,7 @@ VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
VK_KHR_external_memory_win32 | #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
Example with fetching pointers to Vulkan functions dynamically: Example with fetching pointers to Vulkan functions dynamically:
@ -17053,7 +17298,7 @@ implementation whether the allocation succeeds or fails. You can change this beh
by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
not made if it would exceed the budget or if the budget is already exceeded. not made if it would exceed the budget or if the budget is already exceeded.
VMA then tries to make the allocation from the next eligible Vulkan memory type. VMA then tries to make the allocation from the next eligible Vulkan memory type.
The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. If all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
when creating resources that are not essential for the application (e.g. the texture when creating resources that are not essential for the application (e.g. the texture
of a specific object) and not to pass it when creating critically important resources of a specific object) and not to pass it when creating critically important resources
@ -18193,7 +18438,8 @@ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VkBuffer buf; VkBuffer buf;
VmaAllocation alloc; VmaAllocation alloc;
VmaAllocationInfo allocInfo; VmaAllocationInfo allocInfo;
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); VkResult result = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
// Check result...
VkMemoryPropertyFlags memPropFlags; VkMemoryPropertyFlags memPropFlags;
vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags); vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
@ -18204,10 +18450,24 @@ if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
// [Executed in runtime]: // [Executed in runtime]:
memcpy(allocInfo.pMappedData, myData, myDataSize); memcpy(allocInfo.pMappedData, myData, myDataSize);
result = vmaFlushAllocation(allocator, alloc, 0, VK_WHOLE_SIZE);
// Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = buf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
} }
else else
{ {
// Allocation ended up in a non-mappable memory - need to transfer. // Allocation ended up in a non-mappable memory - a transfer using a staging buffer is required.
VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
stagingBufCreateInfo.size = 65536; stagingBufCreateInfo.size = 65536;
stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
@ -18220,18 +18480,46 @@ else
VkBuffer stagingBuf; VkBuffer stagingBuf;
VmaAllocation stagingAlloc; VmaAllocation stagingAlloc;
VmaAllocationInfo stagingAllocInfo; VmaAllocationInfo stagingAllocInfo;
vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo, result = vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
&stagingBuf, &stagingAlloc, stagingAllocInfo); &stagingBuf, &stagingAlloc, &stagingAllocInfo);
// Check result...
// [Executed in runtime]: // [Executed in runtime]:
memcpy(stagingAllocInfo.pMappedData, myData, myDataSize); memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE); result = vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
//vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT // Check result...
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier.buffer = stagingBuf;
bufMemBarrier.offset = 0;
bufMemBarrier.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
VkBufferCopy bufCopy = { VkBufferCopy bufCopy = {
0, // srcOffset 0, // srcOffset
0, // dstOffset, 0, // dstOffset,
myDataSize); // size myDataSize, // size
};
vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy); vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
VkBufferMemoryBarrier bufMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
bufMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // We created a uniform buffer
bufMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufMemBarrier2.buffer = buf;
bufMemBarrier2.offset = 0;
bufMemBarrier2.size = VK_WHOLE_SIZE;
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0, 0, nullptr, 1, &bufMemBarrier2, 0, nullptr);
} }
\endcode \endcode
@ -18264,14 +18552,22 @@ Please check "CONFIGURATION SECTION" in the code to find macros that you can def
before each include of this file or change directly in this file to provide before each include of this file or change directly in this file to provide
your own implementation of basic facilities like assert, `min()` and `max()` functions, your own implementation of basic facilities like assert, `min()` and `max()` functions,
mutex, atomic etc. mutex, atomic etc.
The library uses its own implementation of containers by default, but you can switch to using
STL containers instead.
For example, define `VMA_ASSERT(expr)` before including the library to provide For example, define `VMA_ASSERT(expr)` before including the library to provide
custom implementation of the assertion, compatible with your project. custom implementation of the assertion, compatible with your project.
By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
and empty otherwise. and empty otherwise.
Similarly, you can define `VMA_LEAK_LOG_FORMAT` macro to enable printing of leaked (unfreed) allocations,
including their names and other parameters. Example:
\code
#define VMA_LEAK_LOG_FORMAT(format, ...) do { \
printf((format), __VA_ARGS__); \
printf("\n"); \
} while(false)
\endcode
\section config_Vulkan_functions Pointers to Vulkan functions \section config_Vulkan_functions Pointers to Vulkan functions
There are multiple ways to import pointers to Vulkan functions in the library. There are multiple ways to import pointers to Vulkan functions in the library.
@ -18526,6 +18822,145 @@ Example use of this extension can be found in the code of the sample and test su
accompanying this library. accompanying this library.
\page vk_khr_external_memory_win32 VK_KHR_external_memory_win32
On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 `HANDLE`
of a `VkDeviceMemory` block, to be able to reference the memory on other Vulkan logical devices or instances,
in multiple processes, and/or in multiple APIs.
VMA offers support for it.
\section vk_khr_external_memory_win32_initialization Initialization
1) Make sure the extension is defined in the code by including following header before including VMA:
\code
#include <vulkan/vulkan_win32.h>
\endcode
2) Check if "VK_KHR_external_memory_win32" is available among device extensions.
Enable it when creating the `VkDevice` object.
3) Enable the usage of this extension in VMA by setting flag #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT
when calling vmaCreateAllocator().
4) Make sure that VMA has access to the `vkGetMemoryWin32HandleKHR` function by either enabling `VMA_DYNAMIC_VULKAN_FUNCTIONS` macro
or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly.
For more information, see \ref quick_start_initialization_importing_vulkan_functions.
\section vk_khr_external_memory_win32_preparations Preparations
You can find example usage among tests, in file "Tests.cpp", function `TestWin32Handles()`.
To use the extenion, buffers need to be created with `VkExternalMemoryBufferCreateInfoKHR` attached to their `pNext` chain,
and memory allocations need to be made with `VkExportMemoryAllocateInfoKHR` attached to their `pNext` chain.
To make use of them, you need to use \ref custom_memory_pools. Example:
\code
// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo exampleAllocCreateInfo = {};
exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Find memory type index to use for the custom pool.
uint32_t memTypeIndex;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,
&exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
// Check res...
// Create a custom pool.
constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {
VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.memoryTypeIndex = memTypeIndex;
poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;
VmaPool pool;
res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyPool(g_Allocator, pool);
\endcode
Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged
for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block.
No copy is made internally. This is why variable `exportMemAllocInfo` is defined as `static`.
\section vk_khr_external_memory_win32_memory_allocation Memory allocation
Finally, you can create a buffer with an allocation out of the custom pool.
The buffer should use same flags as the sample buffer used to find the memory type.
It should also specify `VkExternalMemoryBufferCreateInfoKHR` in its `pNext` chain.
\code
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = // Your desired buffer size.
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool; // It is enough to set this one member.
VkBuffer buf;
VmaAllocation alloc;
res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyBuffer(g_Allocator, buf, alloc);
\endcode
If you need each allocation to have its own device memory block and start at offset 0, you can still do
by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.
\section vk_khr_external_memory_win32_exporting_win32_handle Exporting Win32 handle
After the allocation is created, you can acquire a Win32 `HANDLE` to the `VkDeviceMemory` block it belongs to.
VMA function vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.
\code
HANDLE handle;
res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
\endcode
Documentation of the VK_KHR_external_memory_win32 extension states that:
> If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.
This is ensured automatically inside VMA.
The library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed.
Every time you call vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.
For further information, please check documentation of the vmaGetMemoryWin32Handle() function.
\page enabling_buffer_device_address Enabling buffer device address \page enabling_buffer_device_address Enabling buffer device address
Device extension VK_KHR_buffer_device_address Device extension VK_KHR_buffer_device_address