Added function vmaImportVulkanFunctionsFromVolk (#486)
Also added Cmake option VMA_VOLK_HEADER_PATH.
Also updated the documentation.
diff --git a/Doxyfile b/Doxyfile
index 2b6bcc9..346537c 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -2469,7 +2469,8 @@
VMA_EXTERNAL_MEMORY_WIN32=1 \
VMA_EXTERNAL_MEMORY=1 \
VMA_EXTENDS_VK_STRUCT= \
- VMA_STATS_STRING_ENABLED=1
+ VMA_STATS_STRING_ENABLED=1 \
+ VOLK_HEADER_VERSION=304
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index 9e135ec..b63c732 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -1669,6 +1669,62 @@
@{
*/
+#ifdef VOLK_HEADER_VERSION
+/** \brief Fully initializes `pDstVulkanFunctions` structure with Vulkan functions needed by VMA
+using [volk library](https://github.com/zeux/volk).
+
+This function is defined in VMA header only if "volk.h" was included before it.
+
+To use this function properly:
+
+-# Initialize volk and Vulkan:
+ -# Call `volkInitialize()`
+ -# Create `VkInstance` object
+ -# Call `volkLoadInstance()`
+ -# Create `VkDevice` object
+ -# Call `volkLoadDevice()`
+-# Fill in structure #VmaAllocatorCreateInfo, especially members:
+ - VmaAllocatorCreateInfo::device
+ - VmaAllocatorCreateInfo::vulkanApiVersion
+ - VmaAllocatorCreateInfo::flags - set appropriate flags for the Vulkan extensions you enabled
+-# Create an instance of the #VmaVulkanFunctions structure.
+-# Call vmaImportVulkanFunctionsFromVolk().
+ Parameter `pAllocatorCreateInfo` is read to find out which functions should be fetched for
+ appropriate Vulkan version and extensions.
+ Parameter `pDstVulkanFunctions` is filled with those function pointers, or null if not applicable.
+-# Attach the #VmaVulkanFunctions structure to VmaAllocatorCreateInfo::pVulkanFunctions.
+-# Call vmaCreateAllocator() to create the #VmaAllocator object.
+
+Example:
+
+\code
+VmaAllocatorCreateInfo allocatorCreateInfo = {};
+allocatorCreateInfo.physicalDevice = myPhysicalDevice;
+allocatorCreateInfo.device = myDevice;
+allocatorCreateInfo.instance = myInstance;
+allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;
+allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT |
+ VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT |
+ VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT;
+
+VmaVulkanFunctions vulkanFunctions;
+VkResult res = vmaImportVulkanFunctionsFromVolk(&allocatorCreateInfo, &vulkanFunctions);
+// Check res...
+allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
+
+VmaAllocator allocator;
+res = vmaCreateAllocator(&allocatorCreateInfo, &allocator);
+// Check res...
+\endcode
+
+Internally in this function, pointers to functions related to the entire Vulkan instance are fetched using global function definitions,
+while pointers to functions related to the Vulkan device are fetched using `volkLoadDeviceTable()` for given `pAllocatorCreateInfo->device`.
+ */
+VMA_CALL_PRE VkResult VMA_CALL_POST vmaImportVulkanFunctionsFromVolk(
+ const VmaAllocatorCreateInfo* VMA_NOT_NULL pAllocatorCreateInfo,
+ VmaVulkanFunctions* VMA_NOT_NULL pDstVulkanFunctions);
+#endif
+
/// Creates #VmaAllocator object.
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
@@ -15067,6 +15123,103 @@
#ifndef _VMA_PUBLIC_INTERFACE
+
+#ifdef VOLK_HEADER_VERSION
+
+VMA_CALL_PRE VkResult VMA_CALL_POST vmaImportVulkanFunctionsFromVolk(
+ const VmaAllocatorCreateInfo* VMA_NOT_NULL pAllocatorCreateInfo,
+ VmaVulkanFunctions* VMA_NOT_NULL pDstVulkanFunctions)
+{
+ VMA_ASSERT(pAllocatorCreateInfo != VMA_NULL);
+ VMA_ASSERT(pAllocatorCreateInfo->instance != VK_NULL_HANDLE);
+ VMA_ASSERT(pAllocatorCreateInfo->device != VK_NULL_HANDLE);
+
+ memset(pDstVulkanFunctions, 0, sizeof(*pDstVulkanFunctions));
+
+ VolkDeviceTable src = {};
+ volkLoadDeviceTable(&src, pAllocatorCreateInfo->device);
+
+#define COPY_GLOBAL_TO_VMA_FUNC(volkName, vmaName) if(!pDstVulkanFunctions->vmaName) pDstVulkanFunctions->vmaName = volkName;
+#define COPY_DEVICE_TO_VMA_FUNC(volkName, vmaName) if(!pDstVulkanFunctions->vmaName) pDstVulkanFunctions->vmaName = src.volkName;
+
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetInstanceProcAddr, vkGetInstanceProcAddr)
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetDeviceProcAddr, vkGetDeviceProcAddr)
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceProperties, vkGetPhysicalDeviceProperties)
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties, vkGetPhysicalDeviceMemoryProperties)
+ COPY_DEVICE_TO_VMA_FUNC(vkAllocateMemory, vkAllocateMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkFreeMemory, vkFreeMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkMapMemory, vkMapMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkUnmapMemory, vkUnmapMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkFlushMappedMemoryRanges, vkFlushMappedMemoryRanges)
+ COPY_DEVICE_TO_VMA_FUNC(vkInvalidateMappedMemoryRanges, vkInvalidateMappedMemoryRanges)
+ COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory, vkBindBufferMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory, vkBindImageMemory)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements, vkGetBufferMemoryRequirements)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements, vkGetImageMemoryRequirements)
+ COPY_DEVICE_TO_VMA_FUNC(vkCreateBuffer, vkCreateBuffer)
+ COPY_DEVICE_TO_VMA_FUNC(vkDestroyBuffer, vkDestroyBuffer)
+ COPY_DEVICE_TO_VMA_FUNC(vkCreateImage, vkCreateImage)
+ COPY_DEVICE_TO_VMA_FUNC(vkDestroyImage, vkDestroyImage)
+ COPY_DEVICE_TO_VMA_FUNC(vkCmdCopyBuffer, vkCmdCopyBuffer)
+#if VMA_VULKAN_VERSION >= 1001000
+ if (pAllocatorCreateInfo->vulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
+ {
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties2, vkGetPhysicalDeviceMemoryProperties2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements2, vkGetBufferMemoryRequirements2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements2, vkGetImageMemoryRequirements2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory2, vkBindBufferMemory2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory2, vkBindImageMemory2KHR)
+ }
+#endif
+#if VMA_VULKAN_VERSION >= 1003000
+ if (pAllocatorCreateInfo->vulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
+ {
+ COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceBufferMemoryRequirements, vkGetDeviceBufferMemoryRequirements)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceImageMemoryRequirements, vkGetDeviceImageMemoryRequirements)
+ }
+#endif
+#if VMA_KHR_MAINTENANCE4
+ if((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0)
+ {
+ COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceBufferMemoryRequirementsKHR, vkGetDeviceBufferMemoryRequirements)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceImageMemoryRequirementsKHR, vkGetDeviceImageMemoryRequirements)
+ }
+#endif
+#if VMA_DEDICATED_ALLOCATION
+ if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
+ {
+ COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements2KHR, vkGetBufferMemoryRequirements2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements2KHR, vkGetImageMemoryRequirements2KHR)
+ }
+#endif
+#if VMA_BIND_MEMORY2
+ if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
+ {
+ COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory2KHR, vkBindBufferMemory2KHR)
+ COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory2KHR, vkBindImageMemory2KHR)
+ }
+#endif
+#if VMA_MEMORY_BUDGET
+ if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
+ {
+ COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, vkGetPhysicalDeviceMemoryProperties2KHR)
+ }
+#endif
+#if VMA_EXTERNAL_MEMORY_WIN32
+ if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0)
+ {
+ COPY_DEVICE_TO_VMA_FUNC(vkGetMemoryWin32HandleKHR, vkGetMemoryWin32HandleKHR)
+ }
+#endif
+
+#undef COPY_DEVICE_TO_VMA_FUNC
+#undef COPY_GLOBAL_TO_VMA_FUNC
+
+ return VK_SUCCESS;
+}
+
+#endif // #ifdef VOLK_HEADER_VERSION
+
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
const VmaAllocatorCreateInfo* pCreateInfo,
VmaAllocator* pAllocator)
@@ -16781,7 +16934,7 @@
\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions
-You may need to configure importing Vulkan functions. There are 3 ways to do this:
+You may need to configure importing Vulkan functions. There are 4 ways to do this:
-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):
- You don't need to do anything.
@@ -16792,10 +16945,13 @@
- Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,
VmaVulkanFunctions::vkGetDeviceProcAddr.
- The library will fetch pointers to all other functions it needs internally.
--# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like
- [Volk](https://github.com/zeux/volk):
+-# **If you fetch pointers to all Vulkan functions in a custom way**:
- Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.
- Pass these pointers via structure #VmaVulkanFunctions.
+-# **If you use [volk library](https://github.com/zeux/volk)**:
+ - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.
+ - Use function vmaImportVulkanFunctionsFromVolk() to fill in the structure #VmaVulkanFunctions.
+ For more information, see the description of this function.
\subsection quick_start_initialization_enabling_extensions Enabling extensions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c2be024..47165b0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -22,6 +22,7 @@
option(VMA_STATIC_VULKAN_FUNCTIONS "Link statically with Vulkan API" ON)
option(VMA_DYNAMIC_VULKAN_FUNCTIONS "Fetch pointers to Vulkan functions internally (no static linking)" OFF)
+set(VMA_VOLK_HEADER_PATH "" CACHE STRING "Path to volk.h file from the volk library (optional)")
option(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY "Every allocation will have its own memory block" OFF)
option(VMA_DEBUG_INITIALIZE_ALLOCATIONS "Automatically fill new allocations and destroyed allocations with some bit pattern" OFF)
option(VMA_DEBUG_GLOBAL_MUTEX "Enable single mutex protecting all entry calls to the library" OFF)
@@ -50,6 +51,7 @@
Tests.h
VmaUsage.cpp
VmaUsage.h
+ VolkUsage.cpp
VulkanSample.cpp
../include/vk_mem_alloc.h
)
@@ -78,6 +80,15 @@
add_subdirectory(Shaders)
add_dependencies(VmaSample VmaSampleShaders)
+if(NOT "${VMA_VOLK_HEADER_PATH}" STREQUAL "")
+ if(EXISTS "${VMA_VOLK_HEADER_PATH}")
+ message(STATUS "File volk.h found and used from path: ${VMA_VOLK_HEADER_PATH}")
+ target_compile_definitions(VmaSample PRIVATE VMA_VOLK_HEADER_PATH="${VMA_VOLK_HEADER_PATH}")
+ else()
+ message(FATAL_ERROR "File volk.h not found in path: ${VMA_VOLK_HEADER_PATH}")
+ endif()
+endif()
+
# Use Unicode instead of multibyte set
add_compile_definitions(UNICODE _UNICODE)
diff --git a/src/Tests.cpp b/src/Tests.cpp
index b10ee6d..71ed3af 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -3902,6 +3902,11 @@
allocatorCreateInfo.device = g_hDevice;
allocatorCreateInfo.instance = g_hVulkanInstance;
allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
+#ifdef VOLK_HEADER_VERSION
+ VmaVulkanFunctions vulkanFunctions = {};
+ vmaImportVulkanFunctionsFromVolk(&allocatorCreateInfo, &vulkanFunctions);
+ allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
+#endif
#if VMA_DYNAMIC_VULKAN_FUNCTIONS
VmaVulkanFunctions vulkanFunctions = {};
vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
diff --git a/src/VmaUsage.h b/src/VmaUsage.h
index 906d6b9..c018cb7 100644
--- a/src/VmaUsage.h
+++ b/src/VmaUsage.h
@@ -96,10 +96,14 @@
#pragma clang diagnostic ignored "-Wnullability-completeness"
#endif
-#include <vulkan/vulkan.h>
+#ifdef VMA_VOLK_HEADER_PATH
+ #include VMA_VOLK_HEADER_PATH
+#else
+ #include <vulkan/vulkan.h>
+#endif
#ifdef _WIN32
-#include <vulkan/vulkan_win32.h>
+ #include <vulkan/vulkan_win32.h>
#endif // #ifdef _WIN32
#include "vk_mem_alloc.h"
diff --git a/src/VolkUsage.cpp b/src/VolkUsage.cpp
new file mode 100644
index 0000000..7dfe561
--- /dev/null
+++ b/src/VolkUsage.cpp
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#ifdef VMA_VOLK_HEADER_PATH
+
+#define VOLK_IMPLEMENTATION
+#include "VmaUsage.h"
+
+#endif // #ifdef VMA_VOLK_HEADER_PATH
diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp
index a8d1f3e..c77e058 100644
--- a/src/VulkanSample.cpp
+++ b/src/VulkanSample.cpp
@@ -426,6 +426,10 @@
g_Allocs = &g_CpuAllocationCallbacks;
}
+#ifdef VOLK_HEADER_VERSION
+ ERR_GUARD_VULKAN(volkInitialize());
+#endif
+
uint32_t instanceLayerPropCount = 0;
ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) );
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
@@ -513,6 +517,10 @@
ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, g_Allocs, &g_hVulkanInstance) );
+#ifdef VOLK_HEADER_VERSION
+ volkLoadInstance(g_hVulkanInstance);
+#endif
+
if(VK_EXT_debug_utils_enabled)
{
RegisterDebugCallbacks();
@@ -1511,12 +1519,18 @@
outInfo.pAllocationCallbacks = &g_CpuAllocationCallbacks;
}
+#ifdef VOLK_HEADER_VERSION
+ static VmaVulkanFunctions vulkanFunctions = {};
+ vmaImportVulkanFunctionsFromVolk(&outInfo, &vulkanFunctions);
+ outInfo.pVulkanFunctions = &vulkanFunctions;
+#endif // #ifdef VOLK_HEADER_VERSION
+
#if VMA_DYNAMIC_VULKAN_FUNCTIONS
static VmaVulkanFunctions vulkanFunctions = {};
vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
vulkanFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
outInfo.pVulkanFunctions = &vulkanFunctions;
-#endif
+#endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS
// Uncomment to enable HeapSizeLimit.
/*
@@ -2083,6 +2097,11 @@
deviceCreateInfo.pQueueCreateInfos = queueCreateInfo;
ERR_GUARD_VULKAN( vkCreateDevice(g_hPhysicalDevice, &deviceCreateInfo, g_Allocs, &g_hDevice) );
+
+#ifdef VOLK_HEADER_VERSION
+ volkLoadDevice(g_hDevice);
+#endif
+
SetDebugUtilsObjectName(VK_OBJECT_TYPE_DEVICE, reinterpret_cast<std::uint64_t>(g_hDevice), "g_hDevice");
// Only now that SetDebugUtilsObjectName is loaded, we can assign a name to g_hVulkanInstance as well
SetDebugUtilsObjectName(VK_OBJECT_TYPE_INSTANCE, reinterpret_cast<std::uint64_t>(g_hVulkanInstance), "g_hVulkanInstance");