Added function vmaImportVulkanFunctionsFromVolk (#486)

Also added Cmake option VMA_VOLK_HEADER_PATH.
Also updated the documentation.
This commit is contained in:
Adam Sawicki 2025-04-11 17:29:35 +02:00 committed by GitHub
parent 1dea379ae3
commit b5f203256a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 231 additions and 7 deletions

View file

@ -2469,7 +2469,8 @@ PREDEFINED = VMA_CALL_PRE= \
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

View file

@ -1669,6 +1669,62 @@ typedef struct VmaVirtualAllocationInfo
@{
*/
#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 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
#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 @@ See code sample below.
\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 @@ You may need to configure importing Vulkan functions. There are 3 ways to do thi
- 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

View file

@ -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 @@ target_sources(VmaSample PRIVATE
Tests.h
VmaUsage.cpp
VmaUsage.h
VolkUsage.cpp
VulkanSample.cpp
../include/vk_mem_alloc.h
)
@ -78,6 +80,15 @@ target_sources(VmaSample PRIVATE vk_mem_alloc.natvis)
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)

View file

@ -3902,6 +3902,11 @@ void TestHeapSizeLimit()
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;

View file

@ -96,10 +96,14 @@ include all public interface declarations. Example:
#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"

28
src/VolkUsage.cpp Normal file
View file

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

View file

@ -426,6 +426,10 @@ void VulkanUsage::Init()
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 @@ void VulkanUsage::Init()
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 @@ void SetAllocatorCreateInfo(VmaAllocatorCreateInfo& outInfo)
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 @@ static void InitializeApplication()
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");