1 //
2 // Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25
26 /** \mainpage Vulkan Memory Allocator
27
28 <b>Version 3.0.0-development</b> (2021-06-21)
29
30 Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32
33 Documentation of all members: vk_mem_alloc.h
34
35 \section main_table_of_contents Table of contents
36
37 - <b>User guide</b>
38 - \subpage quick_start
39 - [Project setup](@ref quick_start_project_setup)
40 - [Initialization](@ref quick_start_initialization)
41 - [Resource allocation](@ref quick_start_resource_allocation)
42 - \subpage choosing_memory_type
43 - [Usage](@ref choosing_memory_type_usage)
44 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
45 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
46 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
47 - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
48 - \subpage memory_mapping
49 - [Mapping functions](@ref memory_mapping_mapping_functions)
50 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
51 - [Cache flush and invalidate](@ref memory_mapping_cache_control)
52 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
53 - \subpage staying_within_budget
54 - [Querying for budget](@ref staying_within_budget_querying_for_budget)
55 - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
56 - \subpage resource_aliasing
57 - \subpage custom_memory_pools
58 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
59 - [Linear allocation algorithm](@ref linear_algorithm)
60 - [Free-at-once](@ref linear_algorithm_free_at_once)
61 - [Stack](@ref linear_algorithm_stack)
62 - [Double stack](@ref linear_algorithm_double_stack)
63 - [Ring buffer](@ref linear_algorithm_ring_buffer)
64 - [Buddy allocation algorithm](@ref buddy_algorithm)
65 - \subpage defragmentation
66 - [Defragmenting CPU memory](@ref defragmentation_cpu)
67 - [Defragmenting GPU memory](@ref defragmentation_gpu)
68 - [Additional notes](@ref defragmentation_additional_notes)
69 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
70 - \subpage lost_allocations
71 - \subpage statistics
72 - [Numeric statistics](@ref statistics_numeric_statistics)
73 - [JSON dump](@ref statistics_json_dump)
74 - \subpage allocation_annotation
75 - [Allocation user data](@ref allocation_user_data)
76 - [Allocation names](@ref allocation_names)
77 - \subpage virtual_allocator
78 - \subpage debugging_memory_usage
79 - [Memory initialization](@ref debugging_memory_usage_initialization)
80 - [Margins](@ref debugging_memory_usage_margins)
81 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
82 - \subpage record_and_replay
83 - \subpage opengl_interop
84 - \subpage usage_patterns
85 - [Common mistakes](@ref usage_patterns_common_mistakes)
86 - [Simple patterns](@ref usage_patterns_simple)
87 - [Advanced patterns](@ref usage_patterns_advanced)
88 - \subpage configuration
89 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
90 - [Custom host memory allocator](@ref custom_memory_allocator)
91 - [Device memory allocation callbacks](@ref allocation_callbacks)
92 - [Device heap memory limit](@ref heap_memory_limit)
93 - \subpage vk_khr_dedicated_allocation
94 - \subpage enabling_buffer_device_address
95 - \subpage vk_amd_device_coherent_memory
96 - \subpage general_considerations
97 - [Thread safety](@ref general_considerations_thread_safety)
98 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
99 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
100 - [Features not supported](@ref general_considerations_features_not_supported)
101
102 \section main_see_also See also
103
104 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
105 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
106 */
107
108 #ifdef __cplusplus
109 extern "C" {
110 #endif
111
112 /*
113 Define this macro to 0/1 to disable/enable support for recording functionality,
114 available through VmaAllocatorCreateInfo::pRecordSettings.
115 */
116 #ifndef VMA_RECORDING_ENABLED
117 #define VMA_RECORDING_ENABLED 0
118 #endif
119
120 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
121 extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
122 extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
123 extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
124 extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
125 extern PFN_vkAllocateMemory vkAllocateMemory;
126 extern PFN_vkFreeMemory vkFreeMemory;
127 extern PFN_vkMapMemory vkMapMemory;
128 extern PFN_vkUnmapMemory vkUnmapMemory;
129 extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
130 extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
131 extern PFN_vkBindBufferMemory vkBindBufferMemory;
132 extern PFN_vkBindImageMemory vkBindImageMemory;
133 extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
134 extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
135 extern PFN_vkCreateBuffer vkCreateBuffer;
136 extern PFN_vkDestroyBuffer vkDestroyBuffer;
137 extern PFN_vkCreateImage vkCreateImage;
138 extern PFN_vkDestroyImage vkDestroyImage;
139 extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
140 #if VMA_VULKAN_VERSION >= 1001000
141 extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
142 extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
143 extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
144 extern PFN_vkBindImageMemory2 vkBindImageMemory2;
145 extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
146 #endif // #if VMA_VULKAN_VERSION >= 1001000
147 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
148
149 #ifndef VULKAN_H_
150 #include <vulkan/vulkan.h>
151 #endif
152
153 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
154 // where AAA = major, BBB = minor, CCC = patch.
155 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
156 #if !defined(VMA_VULKAN_VERSION)
157 #if defined(VK_VERSION_1_2)
158 #define VMA_VULKAN_VERSION 1002000
159 #elif defined(VK_VERSION_1_1)
160 #define VMA_VULKAN_VERSION 1001000
161 #else
162 #define VMA_VULKAN_VERSION 1000000
163 #endif
164 #endif
165
166 #if !defined(VMA_DEDICATED_ALLOCATION)
167 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
168 #define VMA_DEDICATED_ALLOCATION 1
169 #else
170 #define VMA_DEDICATED_ALLOCATION 0
171 #endif
172 #endif
173
174 #if !defined(VMA_BIND_MEMORY2)
175 #if VK_KHR_bind_memory2
176 #define VMA_BIND_MEMORY2 1
177 #else
178 #define VMA_BIND_MEMORY2 0
179 #endif
180 #endif
181
182 #if !defined(VMA_MEMORY_BUDGET)
183 #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
184 #define VMA_MEMORY_BUDGET 1
185 #else
186 #define VMA_MEMORY_BUDGET 0
187 #endif
188 #endif
189
190 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
191 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
192 #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
193 #define VMA_BUFFER_DEVICE_ADDRESS 1
194 #else
195 #define VMA_BUFFER_DEVICE_ADDRESS 0
196 #endif
197 #endif
198
199 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
200 #if !defined(VMA_MEMORY_PRIORITY)
201 #if VK_EXT_memory_priority
202 #define VMA_MEMORY_PRIORITY 1
203 #else
204 #define VMA_MEMORY_PRIORITY 0
205 #endif
206 #endif
207
208 // Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.
209 #if !defined(VMA_EXTERNAL_MEMORY)
210 #if VK_KHR_external_memory
211 #define VMA_EXTERNAL_MEMORY 1
212 #else
213 #define VMA_EXTERNAL_MEMORY 0
214 #endif
215 #endif
216
217 // Define these macros to decorate all public functions with additional code,
218 // before and after returned type, appropriately. This may be useful for
219 // exporting the functions when compiling VMA as a separate library. Example:
220 // #define VMA_CALL_PRE __declspec(dllexport)
221 // #define VMA_CALL_POST __cdecl
222 #ifndef VMA_CALL_PRE
223 #define VMA_CALL_PRE
224 #endif
225 #ifndef VMA_CALL_POST
226 #define VMA_CALL_POST
227 #endif
228
229 // Define this macro to decorate pointers with an attribute specifying the
230 // length of the array they point to if they are not null.
231 //
232 // The length may be one of
233 // - The name of another parameter in the argument list where the pointer is declared
234 // - The name of another member in the struct where the pointer is declared
235 // - The name of a member of a struct type, meaning the value of that member in
236 // the context of the call. For example
237 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
238 // this means the number of memory heaps available in the device associated
239 // with the VmaAllocator being dealt with.
240 #ifndef VMA_LEN_IF_NOT_NULL
241 #define VMA_LEN_IF_NOT_NULL(len)
242 #endif
243
244 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
245 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
246 #ifndef VMA_NULLABLE
247 #ifdef __clang__
248 #define VMA_NULLABLE _Nullable
249 #else
250 #define VMA_NULLABLE
251 #endif
252 #endif
253
254 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
255 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
256 #ifndef VMA_NOT_NULL
257 #ifdef __clang__
258 #define VMA_NOT_NULL _Nonnull
259 #else
260 #define VMA_NOT_NULL
261 #endif
262 #endif
263
264 // If non-dispatchable handles are represented as pointers then we can give
265 // then nullability annotations
266 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
267 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
268 #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
269 #else
270 #define VMA_NOT_NULL_NON_DISPATCHABLE
271 #endif
272 #endif
273
274 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
275 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
276 #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
277 #else
278 #define VMA_NULLABLE_NON_DISPATCHABLE
279 #endif
280 #endif
281
282 // Used to silence warnings for implicit fallthrough.
283 #if __has_cpp_attribute(clang::fallthrough)
284 #define VMA_FALLTHROUGH [[clang::fallthrough]];
285 #else
286 #define VMA_FALLTHROUGH
287 #endif
288
289 /** \struct VmaAllocator
290 \brief Represents main object of this library initialized.
291
292 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
293 Call function vmaDestroyAllocator() to destroy it.
294
295 It is recommended to create just one object of this type per `VkDevice` object,
296 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
297 */
298 VK_DEFINE_HANDLE(VmaAllocator)
299
300 /// Callback function called after successful vkAllocateMemory.
301 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
302 VmaAllocator VMA_NOT_NULL allocator,
303 uint32_t memoryType,
304 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
305 VkDeviceSize size,
306 void* VMA_NULLABLE pUserData);
307 /// Callback function called before vkFreeMemory.
308 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
309 VmaAllocator VMA_NOT_NULL allocator,
310 uint32_t memoryType,
311 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
312 VkDeviceSize size,
313 void* VMA_NULLABLE pUserData);
314
315 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
316
317 Provided for informative purpose, e.g. to gather statistics about number of
318 allocations or total amount of memory allocated in Vulkan.
319
320 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
321 */
322 typedef struct VmaDeviceMemoryCallbacks {
323 /// Optional, can be null.
324 PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
325 /// Optional, can be null.
326 PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
327 /// Optional, can be null.
328 void* VMA_NULLABLE pUserData;
329 } VmaDeviceMemoryCallbacks;
330
331 /// Flags for created #VmaAllocator.
332 typedef enum VmaAllocatorCreateFlagBits {
333 /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you.
334
335 Using this flag may increase performance because internal mutexes are not used.
336 */
337 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
338 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
339
340 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
341 When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
342
343 Using this extension will automatically allocate dedicated blocks of memory for
344 some buffers and images instead of suballocating place for them out of bigger
345 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
346 flag) when it is recommended by the driver. It may improve performance on some
347 GPUs.
348
349 You may set this flag only if you found out that following device extensions are
350 supported, you enabled them while creating Vulkan device passed as
351 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
352 library:
353
354 - VK_KHR_get_memory_requirements2 (device extension)
355 - VK_KHR_dedicated_allocation (device extension)
356
357 When this flag is set, you can experience following warnings reported by Vulkan
358 validation layer. You can ignore them.
359
360 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
361 */
362 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
363 /**
364 Enables usage of VK_KHR_bind_memory2 extension.
365
366 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
367 When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
368
369 You may set this flag only if you found out that this device extension is supported,
370 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
371 and you want it to be used internally by this library.
372
373 The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
374 which allow to pass a chain of `pNext` structures while binding.
375 This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
376 */
377 VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
378 /**
379 Enables usage of VK_EXT_memory_budget extension.
380
381 You may set this flag only if you found out that this device extension is supported,
382 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
383 and you want it to be used internally by this library, along with another instance extension
384 VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
385
386 The extension provides query for current memory usage and budget, which will probably
387 be more accurate than an estimation used by the library otherwise.
388 */
389 VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
390 /**
391 Enables usage of VK_AMD_device_coherent_memory extension.
392
393 You may set this flag only if you:
394
395 - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
396 - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
397 - want it to be used internally by this library.
398
399 The extension and accompanying device feature provide access to memory types with
400 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
401 They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
402
403 When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
404 To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,
405 returning `VK_ERROR_FEATURE_NOT_PRESENT`.
406 */
407 VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
408 /**
409 Enables usage of "buffer device address" feature, which allows you to use function
410 `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
411
412 You may set this flag only if you:
413
414 1. (For Vulkan version < 1.2) Found as available and enabled device extension
415 VK_KHR_buffer_device_address.
416 This extension is promoted to core Vulkan 1.2.
417 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.
418
419 When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.
420 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to
421 allocated memory blocks wherever it might be needed.
422
423 For more information, see documentation chapter \ref enabling_buffer_device_address.
424 */
425 VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
426 /**
427 Enables usage of VK_EXT_memory_priority extension in the library.
428
429 You may set this flag only if you found available and enabled this device extension,
430 along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,
431 while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
432
433 When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority
434 are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.
435
436 A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
437 Larger values are higher priority. The granularity of the priorities is implementation-dependent.
438 It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.
439 The value to be used for default priority is 0.5.
440 For more details, see the documentation of the VK_EXT_memory_priority extension.
441 */
442 VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,
443
444 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
445 } VmaAllocatorCreateFlagBits;
446 typedef VkFlags VmaAllocatorCreateFlags;
447
448 /** \brief Pointers to some Vulkan functions - a subset used by the library.
449
450 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
451 */
452 typedef struct VmaVulkanFunctions {
453 PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
454 PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
455 PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
456 PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
457 PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
458 PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
459 PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
460 PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
461 PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
462 PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
463 PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
464 PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
465 PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
466 PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
467 PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
468 PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
469 PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
470 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
471 /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
472 PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
473 /// Fetch "vkGetImageMemoryRequirements 2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
474 PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
475 #endif
476 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
477 /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.
478 PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
479 /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.
480 PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
481 #endif
482 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
483 PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
484 #endif
485 } VmaVulkanFunctions;
486
487 /// Flags to be used in VmaRecordSettings::flags.
488 typedef enum VmaRecordFlagBits {
489 /** \brief Enables flush after recording every function call.
490
491 Enable it if you expect your application to crash, which may leave recording file truncated.
492 It may degrade performance though.
493 */
494 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
495
496 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
497 } VmaRecordFlagBits;
498 typedef VkFlags VmaRecordFlags;
499
500 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
501 typedef struct VmaRecordSettings
502 {
503 /// Flags for recording. Use #VmaRecordFlagBits enum.
504 VmaRecordFlags flags;
505 /** \brief Path to the file that should be written by the recording.
506
507 Suggested extension: "csv".
508 If the file already exists, it will be overwritten.
509 It will be opened for the whole time #VmaAllocator object is alive.
510 If opening this file fails, creation of the whole allocator object fails.
511 */
512 const char* VMA_NOT_NULL pFilePath;
513 } VmaRecordSettings;
514
515 /// Description of a Allocator to be created.
516 typedef struct VmaAllocatorCreateInfo
517 {
518 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
519 VmaAllocatorCreateFlags flags;
520 /// Vulkan physical device.
521 /** It must be valid throughout whole lifetime of created allocator. */
522 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
523 /// Vulkan device.
524 /** It must be valid throughout whole lifetime of created allocator. */
525 VkDevice VMA_NOT_NULL device;
526 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
527 /** Set to 0 to use default, which is currently 256 MiB. */
528 VkDeviceSize preferredLargeHeapBlockSize;
529 /// Custom CPU memory allocation callbacks. Optional.
530 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
531 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
532 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
533 /** Optional, can be null. */
534 const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
535 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
536
537 This value is used only when you make allocations with
538 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
539 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
540
541 For example, if you double-buffer your command buffers, so resources used for
542 rendering in previous frame may still be in use by the GPU at the moment you
543 allocate resources needed for the current frame, set this value to 1.
544
545 If you want to allow any allocations other than used in the current frame to
546 become lost, set this value to 0.
547 */
548 uint32_t frameInUseCount;
549 /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap.
550
551 If not NULL, it must be a pointer to an array of
552 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
553 maximum number of bytes that can be allocated out of particular Vulkan memory
554 heap.
555
556 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
557 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
558
559 If there is a limit defined for a heap:
560
561 - If user tries to allocate more memory from that heap using this allocator,
562 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
563 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
564 value of this limit will be reported instead when using vmaGetMemoryProperties().
565
566 Warning! Using this feature may not be equivalent to installing a GPU with
567 smaller amount of memory, because graphics driver doesn't necessary fail new
568 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
569 exceeded. It may return success and just silently migrate some device memory
570 blocks to system RAM. This driver behavior can also be controlled using
571 VK_AMD_memory_overallocation_behavior extension.
572 */
573 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
574
575 /** \brief Pointers to Vulkan functions. Can be null.
576
577 For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
578 */
579 const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
580 /** \brief Parameters for recording of VMA calls. Can be null.
581
582 If not null, it enables recording of calls to VMA functions to a file.
583 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
584 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
585 */
586 const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
587 /** \brief Handle to Vulkan instance object.
588
589 Starting from version 3.0.0 this member is no longer optional, it must be set!
590 */
591 VkInstance VMA_NOT_NULL instance;
592 /** \brief Optional. The highest version of Vulkan that the application is designed to use.
593
594 It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`.
595 The patch version number specified is ignored. Only the major and minor versions are considered.
596 It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
597 Only versions 1.0, 1.1, 1.2 are supported by the current implementation.
598 Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
599 */
600 uint32_t vulkanApiVersion;
601 #if VMA_EXTERNAL_MEMORY
602 /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.
603
604 If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`
605 elements, defining external memory handle types of particular Vulkan memory type,
606 to be passed using `VkExportMemoryAllocateInfoKHR`.
607
608 Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.
609 This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.
610 */
611 const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;
612 #endif // #if VMA_EXTERNAL_MEMORY
613 } VmaAllocatorCreateInfo;
614
615 /// Creates Allocator object.
616 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
617 const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
618 VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
619
620 /// Destroys allocator object.
621 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
622 VmaAllocator VMA_NULLABLE allocator);
623
624 /** \brief Information about existing #VmaAllocator object.
625 */
626 typedef struct VmaAllocatorInfo
627 {
628 /** \brief Handle to Vulkan instance object.
629
630 This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
631 */
632 VkInstance VMA_NOT_NULL instance;
633 /** \brief Handle to Vulkan physical device object.
634
635 This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
636 */
637 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
638 /** \brief Handle to Vulkan device object.
639
640 This is the same value as has been passed through VmaAllocatorCreateInfo::device.
641 */
642 VkDevice VMA_NOT_NULL device;
643 } VmaAllocatorInfo;
644
645 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
646
647 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
648 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
649 */
650 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
651
652 /**
653 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
654 You can access it here, without fetching it again on your own.
655 */
656 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
657 VmaAllocator VMA_NOT_NULL allocator,
658 const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
659
660 /**
661 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
662 You can access it here, without fetching it again on your own.
663 */
664 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
665 VmaAllocator VMA_NOT_NULL allocator,
666 const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
667
668 /**
669 \brief Given Memory Type Index, returns Property Flags of this memory type.
670
671 This is just a convenience function. Same information can be obtained using
672 vmaGetMemoryProperties().
673 */
674 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
675 VmaAllocator VMA_NOT_NULL allocator,
676 uint32_t memoryTypeIndex,
677 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
678
679 /** \brief Sets index of the current frame.
680
681 This function must be used if you make allocations with
682 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
683 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
684 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
685 become lost in the current frame.
686 */
687 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
688 VmaAllocator VMA_NOT_NULL allocator,
689 uint32_t frameIndex);
690
691 /** \brief Calculated statistics of memory usage in entire allocator.
692 */
693 typedef struct VmaStatInfo
694 {
695 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
696 uint32_t blockCount;
697 /// Number of #VmaAllocation allocation objects allocated.
698 uint32_t allocationCount;
699 /// Number of free ranges of memory between allocations.
700 uint32_t unusedRangeCount;
701 /// Total number of bytes occupied by all allocations.
702 VkDeviceSize usedBytes;
703 /// Total number of bytes occupied by unused ranges.
704 VkDeviceSize unusedBytes;
705 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
706 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
707 } VmaStatInfo;
708
709 /// General statistics from current state of Allocator.
710 typedef struct VmaStats
711 {
712 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
713 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
714 VmaStatInfo total;
715 } VmaStats;
716
717 /** \brief Retrieves statistics from current state of the Allocator.
718
719 This function is called "calculate" not "get" because it has to traverse all
720 internal data structures, so it may be quite slow. For faster but more brief statistics
721 suitable to be called every frame or every allocation, use vmaGetBudget().
722
723 Note that when using allocator from multiple threads, returned information may immediately
724 become outdated.
725 */
726 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
727 VmaAllocator VMA_NOT_NULL allocator,
728 VmaStats* VMA_NOT_NULL pStats);
729
730 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
731 */
732 typedef struct VmaBudget
733 {
734 /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
735 */
736 VkDeviceSize blockBytes;
737
738 /** \brief Sum size of all allocations created in particular heap, in bytes.
739
740 Usually less or equal than `blockBytes`.
741 Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
742 available for new allocations or wasted due to fragmentation.
743
744 It might be greater than `blockBytes` if there are some allocations in lost state, as they account
745 to this value as well.
746 */
747 VkDeviceSize allocationBytes;
748
749 /** \brief Estimated current memory usage of the program, in bytes.
750
751 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
752
753 It might be different than `blockBytes` (usually higher) due to additional implicit objects
754 also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
755 `VkDeviceMemory` blocks allocated outside of this library, if any.
756 */
757 VkDeviceSize usage;
758
759 /** \brief Estimated amount of memory available to the program, in bytes.
760
761 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
762
763 It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
764 external to the program, like other programs also consuming system resources.
765 Difference `budget - usage` is the amount of additional memory that can probably
766 be allocated without problems. Exceeding the budget may result in various problems.
767 */
768 VkDeviceSize budget;
769 } VmaBudget;
770
771 /** \brief Retrieves information about current memory budget for all memory heaps.
772
773 \param allocator
774 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
775
776 This function is called "get" not "calculate" because it is very fast, suitable to be called
777 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
778
779 Note that when using allocator from multiple threads, returned information may immediately
780 become outdated.
781 */
782 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
783 VmaAllocator VMA_NOT_NULL allocator,
784 VmaBudget* VMA_NOT_NULL pBudget);
785
786 #ifndef VMA_STATS_STRING_ENABLED
787 #define VMA_STATS_STRING_ENABLED 1
788 #endif
789
790 #if VMA_STATS_STRING_ENABLED
791
792 /// Builds and returns statistics as a null-terminated string in JSON format.
793 /**
794 @param allocator
795 @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
796 @param detailedMap
797 */
798 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
799 VmaAllocator VMA_NOT_NULL allocator,
800 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
801 VkBool32 detailedMap);
802
803 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
804 VmaAllocator VMA_NOT_NULL allocator,
805 char* VMA_NULLABLE pStatsString);
806
807 #endif // #if VMA_STATS_STRING_ENABLED
808
809 /** \struct VmaPool
810 \brief Represents custom memory pool
811
812 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
813 Call function vmaDestroyPool() to destroy it.
814
815 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
816 */
817 VK_DEFINE_HANDLE(VmaPool)
818
819 typedef enum VmaMemoryUsage
820 {
821 /** No intended memory usage specified.
822 Use other members of VmaAllocationCreateInfo to specify your requirements.
823 */
824 VMA_MEMORY_USAGE_UNKNOWN = 0,
825 /** Memory will be used on device only, so fast access from the device is preferred.
826 It usually means device-local GPU (video) memory.
827 No need to be mappable on host.
828 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
829
830 Usage:
831
832 - Resources written and read by device, e.g. images used as attachments.
833 - Resources transferred from host once (immutable) or infrequently and read by
834 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
835 (constant) buffers, and majority of other types of resources used on GPU.
836
837 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
838 In such case, you are free to map it.
839 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
840 */
841 VMA_MEMORY_USAGE_GPU_ONLY = 1,
842 /** Memory will be mappable on host.
843 It usually means CPU (system) memory.
844 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
845 CPU access is typically uncached. Writes may be write-combined.
846 Resources created in this pool may still be accessible to the device, but access to them can be slow.
847 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
848
849 Usage: Staging copy of resources used as transfer source.
850 */
851 VMA_MEMORY_USAGE_CPU_ONLY = 2,
852 /**
853 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
854 CPU access is typically uncached. Writes may be write-combined.
855
856 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call.
857 */
858 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
859 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
860 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
861
862 Usage:
863
864 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
865 - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
866 */
867 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
868 /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
869
870 Usage: Staging copy of resources moved from GPU memory to CPU memory as part
871 of custom paging/residency mechanism, to be moved back to GPU memory when needed.
872 */
873 VMA_MEMORY_USAGE_CPU_COPY = 5,
874 /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
875 Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
876
877 Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
878
879 Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
880 */
881 VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
882
883 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
884 } VmaMemoryUsage;
885
886 /// Flags to be passed as VmaAllocationCreateInfo::flags.
887 typedef enum VmaAllocationCreateFlagBits {
888 /** \brief Set this flag if the allocation should have its own memory block.
889
890 Use it for special, big resources, like fullscreen images used as attachments.
891
892 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
893 */
894 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
895
896 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
897
898 If new allocation cannot be placed in any of the existing blocks, allocation
899 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
900
901 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
902 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
903
904 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
905 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
906 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
907
908 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
909
910 It is valid to use this flag for allocation made from memory type that is not
911 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
912 useful if you need an allocation that is efficient to use on GPU
913 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
914 support it (e.g. Intel GPU).
915
916 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
917 */
918 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
919 /** Allocation created with this flag can become lost as a result of another
920 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
921 must check it before use.
922
923 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
924 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
925
926 For details about supporting lost allocations, see Lost Allocations
927 chapter of User Guide on Main Page.
928
929 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
930 */
931 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
932 /** While creating allocation using this flag, other allocations that were
933 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
934
935 For details about supporting lost allocations, see Lost Allocations
936 chapter of User Guide on Main Page.
937 */
938 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
939 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
940 null-terminated string. Instead of copying pointer value, a local copy of the
941 string is made and stored in allocation's `pUserData`. The string is automatically
942 freed together with the allocation. It is also used in vmaBuildStatsString().
943 */
944 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
945 /** Allocation will be created from upper stack in a double stack pool.
946
947 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
948 */
949 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
950 /** Create both buffer/image and allocation, but don't bind them together.
951 It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
952 The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
953 Otherwise it is ignored.
954 */
955 VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
956 /** Create allocation only if additional device memory required for it, if any, won't exceed
957 memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
958 */
959 VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
960
961 /** Allocation strategy that chooses smallest possible free range for the
962 allocation.
963 */
964 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
965 /** Allocation strategy that chooses biggest possible free range for the
966 allocation.
967 */
968 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
969 /** Allocation strategy that chooses first suitable free range for the
970 allocation.
971
972 "First" doesn't necessarily means the one with smallest offset in memory,
973 but rather the one that is easiest and fastest to find.
974 */
975 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
976
977 /** Allocation strategy that tries to minimize memory usage.
978 */
979 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
980 /** Allocation strategy that tries to minimize allocation time.
981 */
982 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
983 /** Allocation strategy that tries to minimize memory fragmentation.
984 */
985 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
986
987 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
988 */
989 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
990 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
991 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
992 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
993
994 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
995 } VmaAllocationCreateFlagBits;
996 typedef VkFlags VmaAllocationCreateFlags;
997
998 typedef struct VmaAllocationCreateInfo
999 {
1000 /// Use #VmaAllocationCreateFlagBits enum.
1001 VmaAllocationCreateFlags flags;
1002 /** \brief Intended usage of memory.
1003
1004 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1005 If `pool` is not null, this member is ignored.
1006 */
1007 VmaMemoryUsage usage;
1008 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1009
1010 Leave 0 if you specify memory requirements in other way. \n
1011 If `pool` is not null, this member is ignored.*/
1012 VkMemoryPropertyFlags requiredFlags;
1013 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1014
1015 Set to 0 if no additional flags are preferred. \n
1016 If `pool` is not null, this member is ignored. */
1017 VkMemoryPropertyFlags preferredFlags;
1018 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1019
1020 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1021 it meets other requirements specified by this structure, with no further
1022 restrictions on memory type index. \n
1023 If `pool` is not null, this member is ignored.
1024 */
1025 uint32_t memoryTypeBits;
1026 /** \brief Pool that this allocation should be created in.
1027
1028 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1029 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1030 */
1031 VmaPool VMA_NULLABLE pool;
1032 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1033
1034 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1035 null or pointer to a null-terminated string. The string will be then copied to
1036 internal buffer, so it doesn't need to be valid after allocation call.
1037 */
1038 void* VMA_NULLABLE pUserData;
1039 /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
1040
1041 It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object
1042 and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1043 Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.
1044 */
1045 float priority;
1046 } VmaAllocationCreateInfo;
1047
1048 /**
1049 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1050
1051 This algorithm tries to find a memory type that:
1052
1053 - Is allowed by memoryTypeBits.
1054 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1055 - Matches intended usage.
1056 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1057
1058 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1059 from this function or any other allocating function probably means that your
1060 device doesn't support any memory type with requested features for the specific
1061 type of resource you want to use it for. Please check parameters of your
1062 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1063 */
1064 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1065 VmaAllocator VMA_NOT_NULL allocator,
1066 uint32_t memoryTypeBits,
1067 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1068 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1069
1070 /**
1071 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1072
1073 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1074 It internally creates a temporary, dummy buffer that never has memory bound.
1075 It is just a convenience function, equivalent to calling:
1076
1077 - `vkCreateBuffer`
1078 - `vkGetBufferMemoryRequirements`
1079 - `vmaFindMemoryTypeIndex`
1080 - `vkDestroyBuffer`
1081 */
1082 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
1083 VmaAllocator VMA_NOT_NULL allocator,
1084 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
1085 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1086 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1087
1088 /**
1089 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1090
1091 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1092 It internally creates a temporary, dummy image that never has memory bound.
1093 It is just a convenience function, equivalent to calling:
1094
1095 - `vkCreateImage`
1096 - `vkGetImageMemoryRequirements`
1097 - `vmaFindMemoryTypeIndex`
1098 - `vkDestroyImage`
1099 */
1100 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
1101 VmaAllocator VMA_NOT_NULL allocator,
1102 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
1103 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1104 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1105
1106 /// Flags to be passed as VmaPoolCreateInfo::flags.
1107 typedef enum VmaPoolCreateFlagBits {
1108 /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored.
1109
1110 This is an optional optimization flag.
1111
1112 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
1113 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
1114 knows exact type of your allocations so it can handle Buffer-Image Granularity
1115 in the optimal way.
1116
1117 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
1118 exact type of such allocations is not known, so allocator must be conservative
1119 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
1120 (wasted memory). In that case, if you can make sure you always allocate only
1121 buffers and linear images or only optimal images out of this pool, use this flag
1122 to make allocator disregard Buffer-Image Granularity and so make allocations
1123 faster and more optimal.
1124 */
1125 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
1126
1127 /** \brief Enables alternative, linear allocation algorithm in this pool.
1128
1129 Specify this flag to enable linear allocation algorithm, which always creates
1130 new allocations after last one and doesn't reuse space from allocations freed in
1131 between. It trades memory consumption for simplified algorithm and data
1132 structure, which has better performance and uses less memory for metadata.
1133
1134 By using this flag, you can achieve behavior of free-at-once, stack,
1135 ring buffer, and double stack.
1136 For details, see documentation chapter \ref linear_algorithm.
1137
1138 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
1139 */
1140 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
1141
1142 /** \brief Enables alternative, buddy allocation algorithm in this pool.
1143
1144 It operates on a tree of blocks, each having size that is a power of two and
1145 a half of its parent's size. Comparing to default algorithm, this one provides
1146 faster allocation and deallocation and decreased external fragmentation,
1147 at the expense of more memory wasted (internal fragmentation).
1148 For details, see documentation chapter \ref buddy_algorithm.
1149 */
1150 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
1151
1152 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
1153 */
1154 VMA_POOL_CREATE_ALGORITHM_MASK =
1155 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
1156 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
1157
1158 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1159 } VmaPoolCreateFlagBits;
1160 /// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.
1161 typedef VkFlags VmaPoolCreateFlags;
1162
1163 /** \brief Describes parameter of created #VmaPool.
1164 */
1165 typedef struct VmaPoolCreateInfo {
1166 /** \brief Vulkan memory type index to allocate this pool from.
1167 */
1168 uint32_t memoryTypeIndex;
1169 /** \brief Use combination of #VmaPoolCreateFlagBits.
1170 */
1171 VmaPoolCreateFlags flags;
1172 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
1173
1174 Specify nonzero to set explicit, constant size of memory blocks used by this
1175 pool.
1176
1177 Leave 0 to use default and let the library manage block sizes automatically.
1178 Sizes of particular blocks may vary.
1179 */
1180 VkDeviceSize blockSize;
1181 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1182
1183 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
1184 */
1185 size_t minBlockCount;
1186 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1187
1188 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
1189
1190 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
1191 throughout whole lifetime of this pool.
1192 */
1193 size_t maxBlockCount;
1194 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1195
1196 This value is used only when you make allocations with
1197 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1198 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1199
1200 For example, if you double-buffer your command buffers, so resources used for
1201 rendering in previous frame may still be in use by the GPU at the moment you
1202 allocate resources needed for the current frame, set this value to 1.
1203
1204 If you want to allow any allocations other than used in the current frame to
1205 become lost, set this value to 0.
1206 */
1207 uint32_t frameInUseCount;
1208 /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.
1209
1210 It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.
1211 Otherwise, this variable is ignored.
1212 */
1213 float priority;
1214 /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
1215
1216 Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
1217 It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,
1218 e.g. when doing interop with OpenGL.
1219 */
1220 VkDeviceSize minAllocationAlignment;
1221 /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.
1222
1223 Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.
1224 It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.
1225 Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.
1226
1227 Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,
1228 can be attached automatically by this library when using other, more convenient of its features.
1229 */
1230 void* VMA_NULLABLE pMemoryAllocateNext;
1231 } VmaPoolCreateInfo;
1232
1233 /** \brief Describes parameter of existing #VmaPool.
1234 */
1235 typedef struct VmaPoolStats {
1236 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
1237 */
1238 VkDeviceSize size;
1239 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
1240 */
1241 VkDeviceSize unusedSize;
1242 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
1243 */
1244 size_t allocationCount;
1245 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
1246 */
1247 size_t unusedRangeCount;
1248 /** \brief Size of the largest continuous free memory region available for new allocation.
1249
1250 Making a new allocation of that size is not guaranteed to succeed because of
1251 possible additional margin required to respect alignment and buffer/image
1252 granularity.
1253 */
1254 VkDeviceSize unusedRangeSizeMax;
1255 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
1256 */
1257 size_t blockCount;
1258 } VmaPoolStats;
1259
1260 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1261
1262 @param allocator Allocator object.
1263 @param pCreateInfo Parameters of pool to create.
1264 @param[out] pPool Handle to created pool.
1265 */
1266 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
1267 VmaAllocator VMA_NOT_NULL allocator,
1268 const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
1269 VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
1270
1271 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1272 */
1273 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
1274 VmaAllocator VMA_NOT_NULL allocator,
1275 VmaPool VMA_NULLABLE pool);
1276
1277 /** \brief Retrieves statistics of existing #VmaPool object.
1278
1279 @param allocator Allocator object.
1280 @param pool Pool object.
1281 @param[out] pPoolStats Statistics of specified pool.
1282 */
1283 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
1284 VmaAllocator VMA_NOT_NULL allocator,
1285 VmaPool VMA_NOT_NULL pool,
1286 VmaPoolStats* VMA_NOT_NULL pPoolStats);
1287
1288 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
1289
1290 @param allocator Allocator object.
1291 @param pool Pool.
1292 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
1293 */
1294 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
1295 VmaAllocator VMA_NOT_NULL allocator,
1296 VmaPool VMA_NOT_NULL pool,
1297 size_t* VMA_NULLABLE pLostAllocationCount);
1298
1299 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
1300
1301 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1302 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
1303 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1304
1305 Possible return values:
1306
1307 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
1308 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1309 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1310 `VMA_ASSERT` is also fired in that case.
1311 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1312 */
1313 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
1314
1315 /** \brief Retrieves name of a custom pool.
1316
1317 After the call `ppName` is either null or points to an internally-owned null-terminated string
1318 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
1319 destroyed or its name is changed using vmaSetPoolName().
1320 */
1321 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
1322 VmaAllocator VMA_NOT_NULL allocator,
1323 VmaPool VMA_NOT_NULL pool,
1324 const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
1325
1326 /** \brief Sets name of a custom pool.
1327
1328 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
1329 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
1330 */
1331 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
1332 VmaAllocator VMA_NOT_NULL allocator,
1333 VmaPool VMA_NOT_NULL pool,
1334 const char* VMA_NULLABLE pName);
1335
1336 /** \struct VmaAllocation
1337 \brief Represents single memory allocation.
1338
1339 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
1340 plus unique offset.
1341
1342 There are multiple ways to create such object.
1343 You need to fill structure VmaAllocationCreateInfo.
1344 For more information see [Choosing memory type](@ref choosing_memory_type).
1345
1346 Although the library provides convenience functions that create Vulkan buffer or image,
1347 allocate memory for it and bind them together,
1348 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
1349 Allocation object can exist without buffer/image bound,
1350 binding can be done manually by the user, and destruction of it can be done
1351 independently of destruction of the allocation.
1352
1353 The object also remembers its size and some other information.
1354 To retrieve this information, use function vmaGetAllocationInfo() and inspect
1355 returned structure VmaAllocationInfo.
1356
1357 Some kinds allocations can be in lost state.
1358 For more information, see [Lost allocations](@ref lost_allocations).
1359 */
1360 VK_DEFINE_HANDLE(VmaAllocation)
1361
1362 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1363 */
1364 typedef struct VmaAllocationInfo {
1365 /** \brief Memory type index that this allocation was allocated from.
1366
1367 It never changes.
1368 */
1369 uint32_t memoryType;
1370 /** \brief Handle to Vulkan memory object.
1371
1372 Same memory object can be shared by multiple allocations.
1373
1374 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1375
1376 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
1377 */
1378 VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
1379 /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.
1380
1381 You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function
1382 vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,
1383 not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation
1384 and apply this offset automatically.
1385
1386 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1387 */
1388 VkDeviceSize offset;
1389 /** \brief Size of this allocation, in bytes.
1390
1391 It never changes, unless allocation is lost.
1392
1393 \note Allocation size returned in this variable may be greater than the size
1394 requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the
1395 allocation is accessible for operations on memory e.g. using a pointer after
1396 mapping with vmaMapMemory(), but operations on the resource e.g. using
1397 `vkCmdCopyBuffer` must be limited to the size of the resource.
1398 */
1399 VkDeviceSize size;
1400 /** \brief Pointer to the beginning of this allocation as mapped data.
1401
1402 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1403 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
1404
1405 It can change after call to vmaMapMemory(), vmaUnmapMemory().
1406 It can also change after call to vmaDefragment() if this allocation is passed to the function.
1407 */
1408 void* VMA_NULLABLE pMappedData;
1409 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1410
1411 It can change after call to vmaSetAllocationUserData() for this allocation.
1412 */
1413 void* VMA_NULLABLE pUserData;
1414 } VmaAllocationInfo;
1415
1416 /** \brief General purpose memory allocation.
1417
1418 @param allocator
1419 @param pVkMemoryRequirements
1420 @param pCreateInfo
1421 @param[out] pAllocation Handle to allocated memory.
1422 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1423
1424 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1425
1426 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1427 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1428 */
1429 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
1430 VmaAllocator VMA_NOT_NULL allocator,
1431 const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
1432 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1433 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1434 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1435
1436 /** \brief General purpose memory allocation for multiple allocation objects at once.
1437
1438 @param allocator Allocator object.
1439 @param pVkMemoryRequirements Memory requirements for each allocation.
1440 @param pCreateInfo Creation parameters for each alloction.
1441 @param allocationCount Number of allocations to make.
1442 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
1443 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
1444
1445 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1446
1447 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
1448 It is just a general purpose allocation function able to make multiple allocations at once.
1449 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
1450
1451 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
1452 If any allocation fails, all allocations already made within this function call are also freed, so that when
1453 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
1454 */
1455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
1456 VmaAllocator VMA_NOT_NULL allocator,
1457 const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
1458 const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
1459 size_t allocationCount,
1460 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1461 VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
1462
1463 /**
1464 @param allocator
1465 @param buffer
1466 @param pCreateInfo
1467 @param[out] pAllocation Handle to allocated memory.
1468 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1469
1470 You should free the memory using vmaFreeMemory().
1471 */
1472 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
1473 VmaAllocator VMA_NOT_NULL allocator,
1474 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
1475 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1476 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1477 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1478
1479 /// Function similar to vmaAllocateMemoryForBuffer().
1480 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
1481 VmaAllocator VMA_NOT_NULL allocator,
1482 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
1483 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1484 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1485 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1486
1487 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1488
1489 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
1490 */
1491 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
1492 VmaAllocator VMA_NOT_NULL allocator,
1493 const VmaAllocation VMA_NULLABLE allocation);
1494
1495 /** \brief Frees memory and destroys multiple allocations.
1496
1497 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
1498 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
1499 vmaAllocateMemoryPages() and other functions.
1500 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
1501
1502 Allocations in `pAllocations` array can come from any memory pools and types.
1503 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
1504 */
1505 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
1506 VmaAllocator VMA_NOT_NULL allocator,
1507 size_t allocationCount,
1508 const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
1509
1510 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
1511
1512 Current paramteres of given allocation are returned in `pAllocationInfo`.
1513
1514 This function also atomically "touches" allocation - marks it as used in current frame,
1515 just like vmaTouchAllocation().
1516 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
1517
1518 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
1519 you can avoid calling it too often.
1520
1521 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1522 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1523 (e.g. due to defragmentation or allocation becoming lost).
1524 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
1525 */
1526 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
1527 VmaAllocator VMA_NOT_NULL allocator,
1528 VmaAllocation VMA_NOT_NULL allocation,
1529 VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
1530
1531 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
1532
1533 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1534 this function returns `VK_TRUE` if it is not in lost state, so it can still be used.
1535 It then also atomically "touches" the allocation - marks it as used in current frame,
1536 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
1537
1538 If the allocation is in lost state, the function returns `VK_FALSE`.
1539 Memory of such allocation, as well as buffer or image bound to it, should not be used.
1540 Lost allocation and the buffer/image still need to be destroyed.
1541
1542 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1543 this function always returns `VK_TRUE`.
1544 */
1545 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
1546 VmaAllocator VMA_NOT_NULL allocator,
1547 VmaAllocation VMA_NOT_NULL allocation);
1548
1549 /** \brief Sets pUserData in given allocation to new value.
1550
1551 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
1552 pUserData must be either null, or pointer to a null-terminated string. The function
1553 makes local copy of the string and sets it as allocation's `pUserData`. String
1554 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
1555 you can free it after this call. String previously pointed by allocation's
1556 pUserData is freed from memory.
1557
1558 If the flag was not used, the value of pointer `pUserData` is just copied to
1559 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
1560 as a pointer, ordinal number or some handle to you own data.
1561 */
1562 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
1563 VmaAllocator VMA_NOT_NULL allocator,
1564 VmaAllocation VMA_NOT_NULL allocation,
1565 void* VMA_NULLABLE pUserData);
1566
1567 /** \brief Creates new allocation that is in lost state from the beginning.
1568
1569 It can be useful if you need a dummy, non-null allocation.
1570
1571 You still need to destroy created object using vmaFreeMemory().
1572
1573 Returned allocation is not tied to any specific memory pool or memory type and
1574 not bound to any image or buffer. It has size = 0. It cannot be turned into
1575 a real, non-empty allocation.
1576 */
1577 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
1578 VmaAllocator VMA_NOT_NULL allocator,
1579 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
1580
1581 /** \brief Maps memory represented by given allocation and returns pointer to it.
1582
1583 Maps memory represented by given allocation to make it accessible to CPU code.
1584 When succeeded, `*ppData` contains pointer to first byte of this memory.
1585 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
1586 correctly offsetted to the beginning of region assigned to this particular
1587 allocation.
1588
1589 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1590 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1591 multiple times simultaneously, it is safe to call this function on allocations
1592 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1593 mapping and unmapped on last unmapping.
1594
1595 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1596 allocation when mapping is no longer needed or before freeing the allocation, at
1597 the latest.
1598
1599 It also safe to call this function multiple times on the same allocation. You
1600 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1601
1602 It is also safe to call this function on allocation created with
1603 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1604 You must still call vmaUnmapMemory() same number of times as you called
1605 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1606 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1607
1608 This function fails when used on allocation made in memory type that is not
1609 `HOST_VISIBLE`.
1610
1611 This function always fails when called for allocation that was created with
1612 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
1613 mapped.
1614
1615 This function doesn't automatically flush or invalidate caches.
1616 If the allocation is made from a memory types that is not `HOST_COHERENT`,
1617 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
1618 */
1619 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
1620 VmaAllocator VMA_NOT_NULL allocator,
1621 VmaAllocation VMA_NOT_NULL allocation,
1622 void* VMA_NULLABLE * VMA_NOT_NULL ppData);
1623
1624 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
1625
1626 For details, see description of vmaMapMemory().
1627
1628 This function doesn't automatically flush or invalidate caches.
1629 If the allocation is made from a memory types that is not `HOST_COHERENT`,
1630 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
1631 */
1632 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
1633 VmaAllocator VMA_NOT_NULL allocator,
1634 VmaAllocation VMA_NOT_NULL allocation);
1635
1636 /** \brief Flushes memory of given allocation.
1637
1638 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
1639 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
1640 Unmap operation doesn't do that automatically.
1641
1642 - `offset` must be relative to the beginning of allocation.
1643 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
1644 - `offset` and `size` don't have to be aligned.
1645 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
1646 - If `size` is 0, this call is ignored.
1647 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
1648 this call is ignored.
1649
1650 Warning! `offset` and `size` are relative to the contents of given `allocation`.
1651 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
1652 Do not pass allocation's offset as `offset`!!!
1653
1654 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
1655 called, otherwise `VK_SUCCESS`.
1656 */
1657 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
1658 VmaAllocator VMA_NOT_NULL allocator,
1659 VmaAllocation VMA_NOT_NULL allocation,
1660 VkDeviceSize offset,
1661 VkDeviceSize size);
1662
1663 /** \brief Invalidates memory of given allocation.
1664
1665 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
1666 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
1667 Map operation doesn't do that automatically.
1668
1669 - `offset` must be relative to the beginning of allocation.
1670 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
1671 - `offset` and `size` don't have to be aligned.
1672 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
1673 - If `size` is 0, this call is ignored.
1674 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
1675 this call is ignored.
1676
1677 Warning! `offset` and `size` are relative to the contents of given `allocation`.
1678 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
1679 Do not pass allocation's offset as `offset`!!!
1680
1681 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
1682 it is called, otherwise `VK_SUCCESS`.
1683 */
1684 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
1685 VmaAllocator VMA_NOT_NULL allocator,
1686 VmaAllocation VMA_NOT_NULL allocation,
1687 VkDeviceSize offset,
1688 VkDeviceSize size);
1689
1690 /** \brief Flushes memory of given set of allocations.
1691
1692 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
1693 For more information, see documentation of vmaFlushAllocation().
1694
1695 \param allocator
1696 \param allocationCount
1697 \param allocations
1698 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
1699 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
1700
1701 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
1702 called, otherwise `VK_SUCCESS`.
1703 */
1704 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
1705 VmaAllocator VMA_NOT_NULL allocator,
1706 uint32_t allocationCount,
1707 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
1708 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
1709 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
1710
1711 /** \brief Invalidates memory of given set of allocations.
1712
1713 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
1714 For more information, see documentation of vmaInvalidateAllocation().
1715
1716 \param allocator
1717 \param allocationCount
1718 \param allocations
1719 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
1720 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
1721
1722 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
1723 called, otherwise `VK_SUCCESS`.
1724 */
1725 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
1726 VmaAllocator VMA_NOT_NULL allocator,
1727 uint32_t allocationCount,
1728 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
1729 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
1730 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
1731
1732 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
1733
1734 @param allocator
1735 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
1736
1737 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1738 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
1739 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1740
1741 Possible return values:
1742
1743 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
1744 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1745 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1746 `VMA_ASSERT` is also fired in that case.
1747 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1748 */
1749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
1750
1751 /** \struct VmaDefragmentationContext
1752 \brief Represents Opaque object that represents started defragmentation process.
1753
1754 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
1755 Call function vmaDefragmentationEnd() to destroy it.
1756 */
1757 VK_DEFINE_HANDLE(VmaDefragmentationContext)
1758
1759 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
1760 typedef enum VmaDefragmentationFlagBits {
1761 VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
1762 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1763 } VmaDefragmentationFlagBits;
1764 typedef VkFlags VmaDefragmentationFlags;
1765
1766 /** \brief Parameters for defragmentation.
1767
1768 To be used with function vmaDefragmentationBegin().
1769 */
1770 typedef struct VmaDefragmentationInfo2 {
1771 /** \brief Reserved for future use. Should be 0.
1772 */
1773 VmaDefragmentationFlags flags;
1774 /** \brief Number of allocations in `pAllocations` array.
1775 */
1776 uint32_t allocationCount;
1777 /** \brief Pointer to array of allocations that can be defragmented.
1778
1779 The array should have `allocationCount` elements.
1780 The array should not contain nulls.
1781 Elements in the array should be unique - same allocation cannot occur twice.
1782 It is safe to pass allocations that are in the lost state - they are ignored.
1783 All allocations not present in this array are considered non-moveable during this defragmentation.
1784 */
1785 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
1786 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
1787
1788 The array should have `allocationCount` elements.
1789 You can pass null if you are not interested in this information.
1790 */
1791 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
1792 /** \brief Numer of pools in `pPools` array.
1793 */
1794 uint32_t poolCount;
1795 /** \brief Either null or pointer to array of pools to be defragmented.
1796
1797 All the allocations in the specified pools can be moved during defragmentation
1798 and there is no way to check if they were really moved as in `pAllocationsChanged`,
1799 so you must query all the allocations in all these pools for new `VkDeviceMemory`
1800 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
1801 and images bound to them.
1802
1803 The array should have `poolCount` elements.
1804 The array should not contain nulls.
1805 Elements in the array should be unique - same pool cannot occur twice.
1806
1807 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
1808 It might be more efficient.
1809 */
1810 const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
1811 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
1812
1813 `VK_WHOLE_SIZE` means no limit.
1814 */
1815 VkDeviceSize maxCpuBytesToMove;
1816 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
1817
1818 `UINT32_MAX` means no limit.
1819 */
1820 uint32_t maxCpuAllocationsToMove;
1821 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
1822
1823 `VK_WHOLE_SIZE` means no limit.
1824 */
1825 VkDeviceSize maxGpuBytesToMove;
1826 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
1827
1828 `UINT32_MAX` means no limit.
1829 */
1830 uint32_t maxGpuAllocationsToMove;
1831 /** \brief Optional. Command buffer where GPU copy commands will be posted.
1832
1833 If not null, it must be a valid command buffer handle that supports Transfer queue type.
1834 It must be in the recording state and outside of a render pass instance.
1835 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
1836
1837 Passing null means that only CPU defragmentation will be performed.
1838 */
1839 VkCommandBuffer VMA_NULLABLE commandBuffer;
1840 } VmaDefragmentationInfo2;
1841
1842 typedef struct VmaDefragmentationPassMoveInfo {
1843 VmaAllocation VMA_NOT_NULL allocation;
1844 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
1845 VkDeviceSize offset;
1846 } VmaDefragmentationPassMoveInfo;
1847
1848 /** \brief Parameters for incremental defragmentation steps.
1849
1850 To be used with function vmaBeginDefragmentationPass().
1851 */
1852 typedef struct VmaDefragmentationPassInfo {
1853 uint32_t moveCount;
1854 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
1855 } VmaDefragmentationPassInfo;
1856
1857 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
1858
1859 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
1860 */
1861 typedef struct VmaDefragmentationInfo {
1862 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
1863
1864 Default is `VK_WHOLE_SIZE`, which means no limit.
1865 */
1866 VkDeviceSize maxBytesToMove;
1867 /** \brief Maximum number of allocations that can be moved to different place.
1868
1869 Default is `UINT32_MAX`, which means no limit.
1870 */
1871 uint32_t maxAllocationsToMove;
1872 } VmaDefragmentationInfo;
1873
1874 /** \brief Statistics returned by function vmaDefragment(). */
1875 typedef struct VmaDefragmentationStats {
1876 /// Total number of bytes that have been copied while moving allocations to different places.
1877 VkDeviceSize bytesMoved;
1878 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1879 VkDeviceSize bytesFreed;
1880 /// Number of allocations that have been moved to different places.
1881 uint32_t allocationsMoved;
1882 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1883 uint32_t deviceMemoryBlocksFreed;
1884 } VmaDefragmentationStats;
1885
1886 /** \brief Begins defragmentation process.
1887
1888 @param allocator Allocator object.
1889 @param pInfo Structure filled with parameters of defragmentation.
1890 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
1891 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
1892 @return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.
1893
1894 Use this function instead of old, deprecated vmaDefragment().
1895
1896 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
1897
1898 - You should not use any of allocations passed as `pInfo->pAllocations` or
1899 any allocations that belong to pools passed as `pInfo->pPools`,
1900 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
1901 their data.
1902 - Some mutexes protecting internal data structures may be locked, so trying to
1903 make or free any allocations, bind buffers or images, map memory, or launch
1904 another simultaneous defragmentation in between may cause stall (when done on
1905 another thread) or deadlock (when done on the same thread), unless you are
1906 100% sure that defragmented allocations are in different pools.
1907 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
1908 They become valid after call to vmaDefragmentationEnd().
1909 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
1910 and make sure it finished execution before calling vmaDefragmentationEnd().
1911
1912 For more information and important limitations regarding defragmentation, see documentation chapter:
1913 [Defragmentation](@ref defragmentation).
1914 */
1915 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
1916 VmaAllocator VMA_NOT_NULL allocator,
1917 const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
1918 VmaDefragmentationStats* VMA_NULLABLE pStats,
1919 VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
1920
1921 /** \brief Ends defragmentation process.
1922
1923 Use this function to finish defragmentation started by vmaDefragmentationBegin().
1924 It is safe to pass `context == null`. The function then does nothing.
1925 */
1926 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
1927 VmaAllocator VMA_NOT_NULL allocator,
1928 VmaDefragmentationContext VMA_NULLABLE context);
1929
1930 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
1931 VmaAllocator VMA_NOT_NULL allocator,
1932 VmaDefragmentationContext VMA_NULLABLE context,
1933 VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
1934 );
1935 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
1936 VmaAllocator VMA_NOT_NULL allocator,
1937 VmaDefragmentationContext VMA_NULLABLE context
1938 );
1939
1940 /** \brief Deprecated. Compacts memory by moving allocations.
1941
1942 @param allocator
1943 @param pAllocations Array of allocations that can be moved during this compation.
1944 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
1945 @param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
1946 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
1947 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
1948 @return `VK_SUCCESS` if completed, negative error code in case of error.
1949
1950 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
1951
1952 This function works by moving allocations to different places (different
1953 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
1954 usage. Only allocations that are in `pAllocations` array can be moved. All other
1955 allocations are considered nonmovable in this call. Basic rules:
1956
1957 - Only allocations made in memory types that have
1958 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
1959 flags can be compacted. You may pass other allocations but it makes no sense -
1960 these will never be moved.
1961 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
1962 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
1963 passed to this function that come from such pools are ignored.
1964 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
1965 created as dedicated allocations for any other reason are also ignored.
1966 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
1967 flag can be compacted. If not persistently mapped, memory will be mapped
1968 temporarily inside this function if needed.
1969 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
1970
1971 The function also frees empty `VkDeviceMemory` blocks.
1972
1973 Warning: This function may be time-consuming, so you shouldn't call it too often
1974 (like after every resource creation/destruction).
1975 You can call it on special occasions (like when reloading a game level or
1976 when you just destroyed a lot of objects). Calling it every frame may be OK, but
1977 you should measure that on your platform.
1978
1979 For more information, see [Defragmentation](@ref defragmentation) chapter.
1980 */
1981 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
1982 VmaAllocator VMA_NOT_NULL allocator,
1983 const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1984 size_t allocationCount,
1985 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
1986 const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
1987 VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
1988
1989 /** \brief Binds buffer to allocation.
1990
1991 Binds specified buffer to region of memory represented by specified allocation.
1992 Gets `VkDeviceMemory` handle and offset from the allocation.
1993 If you want to create a buffer, allocate memory for it and bind them together separately,
1994 you should use this function for binding instead of standard `vkBindBufferMemory()`,
1995 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
1996 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
1997 (which is illegal in Vulkan).
1998
1999 It is recommended to use function vmaCreateBuffer() instead of this one.
2000 */
2001 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
2002 VmaAllocator VMA_NOT_NULL allocator,
2003 VmaAllocation VMA_NOT_NULL allocation,
2004 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
2005
2006 /** \brief Binds buffer to allocation with additional parameters.
2007
2008 @param allocator
2009 @param allocation
2010 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2011 @param buffer
2012 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
2013
2014 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
2015
2016 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2017 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2018 */
2019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
2020 VmaAllocator VMA_NOT_NULL allocator,
2021 VmaAllocation VMA_NOT_NULL allocation,
2022 VkDeviceSize allocationLocalOffset,
2023 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
2024 const void* VMA_NULLABLE pNext);
2025
2026 /** \brief Binds image to allocation.
2027
2028 Binds specified image to region of memory represented by specified allocation.
2029 Gets `VkDeviceMemory` handle and offset from the allocation.
2030 If you want to create an image, allocate memory for it and bind them together separately,
2031 you should use this function for binding instead of standard `vkBindImageMemory()`,
2032 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2033 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2034 (which is illegal in Vulkan).
2035
2036 It is recommended to use function vmaCreateImage() instead of this one.
2037 */
2038 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
2039 VmaAllocator VMA_NOT_NULL allocator,
2040 VmaAllocation VMA_NOT_NULL allocation,
2041 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
2042
2043 /** \brief Binds image to allocation with additional parameters.
2044
2045 @param allocator
2046 @param allocation
2047 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2048 @param image
2049 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
2050
2051 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
2052
2053 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2054 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2055 */
2056 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
2057 VmaAllocator VMA_NOT_NULL allocator,
2058 VmaAllocation VMA_NOT_NULL allocation,
2059 VkDeviceSize allocationLocalOffset,
2060 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
2061 const void* VMA_NULLABLE pNext);
2062
2063 /**
2064 @param allocator
2065 @param pBufferCreateInfo
2066 @param pAllocationCreateInfo
2067 @param[out] pBuffer Buffer that was created.
2068 @param[out] pAllocation Allocation that was created.
2069 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2070
2071 This function automatically:
2072
2073 -# Creates buffer.
2074 -# Allocates appropriate memory for it.
2075 -# Binds the buffer with the memory.
2076
2077 If any of these operations fail, buffer and allocation are not created,
2078 returned value is negative error code, *pBuffer and *pAllocation are null.
2079
2080 If the function succeeded, you must destroy both buffer and allocation when you
2081 no longer need them using either convenience function vmaDestroyBuffer() or
2082 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2083
2084 If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2085 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2086 it requires or prefers the new buffer to have dedicated allocation. If yes,
2087 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
2088 and #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2089 allocation for this buffer, just like when using
2090 #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2091
2092 \note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,
2093 although recommended as a good practice, is out of scope of this library and could be implemented
2094 by the user as a higher-level logic on top of VMA.
2095 */
2096 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
2097 VmaAllocator VMA_NOT_NULL allocator,
2098 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2099 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2100 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
2101 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2102 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2103
2104 /** \brief Creates a buffer with additional minimum alignment.
2105
2106 Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,
2107 minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.
2108 for interop with OpenGL.
2109 */
2110 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
2111 VmaAllocator VMA_NOT_NULL allocator,
2112 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2113 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2114 VkDeviceSize minAlignment,
2115 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
2116 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2117 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2118
2119 /** \brief Destroys Vulkan buffer and frees allocated memory.
2120
2121 This is just a convenience function equivalent to:
2122
2123 \code
2124 vkDestroyBuffer(device, buffer, allocationCallbacks);
2125 vmaFreeMemory(allocator, allocation);
2126 \endcode
2127
2128 It it safe to pass null as buffer and/or allocation.
2129 */
2130 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
2131 VmaAllocator VMA_NOT_NULL allocator,
2132 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
2133 VmaAllocation VMA_NULLABLE allocation);
2134
2135 /// Function similar to vmaCreateBuffer().
2136 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
2137 VmaAllocator VMA_NOT_NULL allocator,
2138 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2139 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2140 VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
2141 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2142 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2143
2144 /** \brief Destroys Vulkan image and frees allocated memory.
2145
2146 This is just a convenience function equivalent to:
2147
2148 \code
2149 vkDestroyImage(device, image, allocationCallbacks);
2150 vmaFreeMemory(allocator, allocation);
2151 \endcode
2152
2153 It it safe to pass null as image and/or allocation.
2154 */
2155 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
2156 VmaAllocator VMA_NOT_NULL allocator,
2157 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
2158 VmaAllocation VMA_NULLABLE allocation);
2159
2160 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags.
2161 typedef enum VmaVirtualBlockCreateFlagBits {
2162 /** \brief Enables alternative, linear allocation algorithm in this virtual block.
2163
2164 Specify this flag to enable linear allocation algorithm, which always creates
2165 new allocations after last one and doesn't reuse space from allocations freed in
2166 between. It trades memory consumption for simplified algorithm and data
2167 structure, which has better performance and uses less memory for metadata.
2168
2169 By using this flag, you can achieve behavior of free-at-once, stack,
2170 ring buffer, and double stack.
2171 For details, see documentation chapter \ref linear_algorithm.
2172 */
2173 VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,
2174
2175 /** \brief Enables alternative, buddy allocation algorithm in this virtual block.
2176
2177 It operates on a tree of blocks, each having size that is a power of two and
2178 a half of its parent's size. Comparing to default algorithm, this one provides
2179 faster allocation and deallocation and decreased external fragmentation,
2180 at the expense of more memory wasted (internal fragmentation).
2181 For details, see documentation chapter \ref buddy_algorithm.
2182 */
2183 VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT = 0x00000002,
2184
2185 /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2186 */
2187 VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =
2188 VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT |
2189 VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT,
2190
2191 VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2192 } VmaVirtualBlockCreateFlagBits;
2193 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.
2194 typedef VkFlags VmaVirtualBlockCreateFlags;
2195
2196 /// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().
2197 typedef struct VmaVirtualBlockCreateInfo
2198 {
2199 /** \brief Total size of the virtual block.
2200
2201 Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
2202 For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
2203 */
2204 VkDeviceSize size;
2205
2206 /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.
2207 */
2208 VmaVirtualBlockCreateFlagBits flags;
2209
2210 /** \brief Custom CPU memory allocation callbacks. Optional.
2211
2212 Optional, can be null. When specified, they will be used for all CPU-side memory allocations.
2213 */
2214 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2215 } VmaVirtualBlockCreateInfo;
2216
2217 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.
2218 typedef enum VmaVirtualAllocationCreateFlagBits {
2219 /** \brief Allocation will be created from upper stack in a double stack pool.
2220
2221 This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
2222 */
2223 VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
2224 /** \brief Allocation strategy that tries to minimize memory usage.
2225 */
2226 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
2227 /** \brief Allocation strategy that tries to minimize allocation time.
2228 */
2229 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
2230 /** \brief Allocation strategy that tries to minimize memory fragmentation.
2231 */
2232 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT,
2233 /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.
2234
2235 These stategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.
2236 */
2237 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
2238
2239 VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2240 } VmaVirtualAllocationCreateFlagBits;
2241 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.
2242 typedef VkFlags VmaVirtualAllocationCreateFlags;
2243
2244 /// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().
2245 typedef struct VmaVirtualAllocationCreateInfo
2246 {
2247 /** \brief Size of the allocation.
2248
2249 Cannot be zero.
2250 */
2251 VkDeviceSize size;
2252 /** \brief Required alignment of the allocation. Optional.
2253
2254 Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
2255 */
2256 VkDeviceSize alignment;
2257 /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.
2258 */
2259 VmaVirtualAllocationCreateFlags flags;
2260 /** \brief Custom pointer to be associated with the allocation. Optional.
2261
2262 It can be any value and can be used for user-defined purposes. It can be fetched or changed later.
2263 */
2264 void* VMA_NULLABLE pUserData;
2265 } VmaVirtualAllocationCreateInfo;
2266
2267 /// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().
2268 typedef struct VmaVirtualAllocationInfo
2269 {
2270 /** \brief Size of the allocation.
2271
2272 Same value as passed in VmaVirtualAllocationCreateInfo::size.
2273 */
2274 VkDeviceSize size;
2275 /** \brief Custom pointer associated with the allocation.
2276
2277 Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().
2278 */
2279 void* VMA_NULLABLE pUserData;
2280 } VmaVirtualAllocationInfo;
2281
2282 /** \struct VmaVirtualBlock
2283 \brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.
2284
2285 Fill in #VmaVirtualBlockCreateInfo structure and Use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.
2286 For more information, see documentation chapter \ref virtual_allocator.
2287 */
2288 VK_DEFINE_HANDLE(VmaVirtualBlock);
2289
2290 /** \brief Creates new #VmaVirtualBlock object.
2291
2292 \param pCreateInfo Parameters for creation.
2293 \param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.
2294 */
2295 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
2296 const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
2297 VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock);
2298
2299 /** \brief Destroys #VmaVirtualBlock object.
2300
2301 Please note that you should consciously handle virtual allocations that could remain unfreed in the block.
2302 You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()
2303 if you are sure this is what you want. If you do neither, an assert is called.
2304
2305 If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,
2306 don't forget to free them.
2307 */
2308 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock);
2309
2310 /** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.
2311 */
2312 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2313
2314 /** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.
2315 */
2316 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2317 VkDeviceSize offset, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);
2318
2319 /** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.
2320
2321 There is no handle type for a virtual allocation.
2322 Virtual allocations within a specific virtual block are uniquely identified by their offsets.
2323
2324 If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned
2325 (despite the function doesn't ever allocate actual GPU memory).
2326 */
2327 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2328 const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VkDeviceSize* VMA_NOT_NULL pOffset);
2329
2330 /** \brief Frees virtual allocation inside given #VmaVirtualBlock.
2331 */
2332 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VkDeviceSize offset);
2333
2334 /** \brief Frees all virtual allocations inside given #VmaVirtualBlock.
2335
2336 You must either call this function or free each virtual allocation individually with vmaVirtualFree()
2337 before destroying a virtual block. Otherwise, an assert is called.
2338
2339 If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,
2340 don't forget to free it as well.
2341 */
2342 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2343
2344 /** \brief Changes custom pointer associated with given virtual allocation.
2345 */
2346 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2347 VkDeviceSize offset, void* VMA_NULLABLE pUserData);
2348
2349 /** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2350 */
2351 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2352 VmaStatInfo* VMA_NOT_NULL pStatInfo);
2353
2354 /** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.
2355 \param virtualBlock Virtual block.
2356 \param[out] ppStatsString Returned string.
2357 \param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStats(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.
2358
2359 Returned string must be freed using vmaFreeVirtualBlockStatsString().
2360 */
2361 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2362 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap);
2363
2364 /** \brief Frees a string returned by vmaBuildVirtualBlockStatsString().
2365 */
2366 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2367 char* VMA_NULLABLE pStatsString);
2368
2369 #ifdef __cplusplus
2370 }
2371 #endif
2372
2373 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2374
2375 ////////////////////////////////////////////////////////////////////////////////
2376 ////////////////////////////////////////////////////////////////////////////////
2377 //
2378 // IMPLEMENTATION
2379 //
2380 ////////////////////////////////////////////////////////////////////////////////
2381 ////////////////////////////////////////////////////////////////////////////////
2382
2383 // For Visual Studio IntelliSense.
2384 #if defined(__cplusplus) && defined(__INTELLISENSE__)
2385 #define VMA_IMPLEMENTATION
2386 #endif
2387
2388 #ifdef VMA_IMPLEMENTATION
2389 #undef VMA_IMPLEMENTATION
2390
2391 #include <cstdint>
2392 #include <cstdlib>
2393 #include <cstring>
2394 #include <utility>
2395
2396 #if VMA_RECORDING_ENABLED
2397 #include <chrono>
2398 #if defined(_WIN32)
2399 #include <windows.h>
2400 #else
2401 #include <sstream>
2402 #include <thread>
2403 #endif
2404 #endif
2405
2406 /*******************************************************************************
2407 CONFIGURATION SECTION
2408
2409 Define some of these macros before each #include of this header or change them
2410 here if you need other then default behavior depending on your environment.
2411 */
2412
2413 /*
2414 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2415 internally, like:
2416
2417 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2418 */
2419 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2420 #define VMA_STATIC_VULKAN_FUNCTIONS 1
2421 #endif
2422
2423 /*
2424 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2425 internally, like:
2426
2427 vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
2428 */
2429 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
2430 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
2431 #if defined(VK_NO_PROTOTYPES)
2432 extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
2433 extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
2434 #endif
2435 #endif
2436
2437 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
2438 //#define VMA_USE_STL_CONTAINERS 1
2439
2440 /* Set this macro to 1 to make the library including and using STL containers:
2441 std::pair, std::vector, std::list, std::unordered_map.
2442
2443 Set it to 0 or undefined to make the library using its own implementation of
2444 the containers.
2445 */
2446 #if VMA_USE_STL_CONTAINERS
2447 #define VMA_USE_STL_VECTOR 1
2448 #define VMA_USE_STL_UNORDERED_MAP 1
2449 #define VMA_USE_STL_LIST 1
2450 #endif
2451
2452 #ifndef VMA_USE_STL_SHARED_MUTEX
2453 // Compiler conforms to C++17.
2454 #if __cplusplus >= 201703L
2455 #define VMA_USE_STL_SHARED_MUTEX 1
2456 // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
2457 // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
2458 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
2459 #define VMA_USE_STL_SHARED_MUTEX 1
2460 #else
2461 #define VMA_USE_STL_SHARED_MUTEX 0
2462 #endif
2463 #endif
2464
2465 /*
2466 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
2467 Library has its own container implementation.
2468 */
2469 #if VMA_USE_STL_VECTOR
2470 #include <vector>
2471 #endif
2472
2473 #if VMA_USE_STL_UNORDERED_MAP
2474 #include <unordered_map>
2475 #endif
2476
2477 #if VMA_USE_STL_LIST
2478 #include <list>
2479 #endif
2480
2481 /*
2482 Following headers are used in this CONFIGURATION section only, so feel free to
2483 remove them if not needed.
2484 */
2485 #include <cassert> // for assert
2486 #include <algorithm> // for min, max
2487 #include <mutex>
2488
2489 #ifndef VMA_NULL
2490 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2491 #define VMA_NULL nullptr
2492 #endif
2493
2494 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
2495 #include <cstdlib>
vma_aligned_alloc(size_t alignment,size_t size)2496 void *vma_aligned_alloc(size_t alignment, size_t size)
2497 {
2498 // alignment must be >= sizeof(void*)
2499 if(alignment < sizeof(void*))
2500 {
2501 alignment = sizeof(void*);
2502 }
2503
2504 return memalign(alignment, size);
2505 }
2506 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
2507 #include <cstdlib>
2508
2509 #if defined(__APPLE__)
2510 #include <AvailabilityMacros.h>
2511 #endif
2512
vma_aligned_alloc(size_t alignment,size_t size)2513 void *vma_aligned_alloc(size_t alignment, size_t size)
2514 {
2515 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
2516 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
2517 // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
2518 // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
2519 // MAC_OS_X_VERSION_10_16), even though the function is marked
2520 // available for 10.15. That's why the preprocessor checks for 10.16 but
2521 // the __builtin_available checks for 10.15.
2522 // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
2523 if (__builtin_available(macOS 10.15, iOS 13, *))
2524 return aligned_alloc(alignment, size);
2525 #endif
2526 #endif
2527 // alignment must be >= sizeof(void*)
2528 if(alignment < sizeof(void*))
2529 {
2530 alignment = sizeof(void*);
2531 }
2532
2533 void *pointer;
2534 if(posix_memalign(&pointer, alignment, size) == 0)
2535 return pointer;
2536 return VMA_NULL;
2537 }
2538 #elif defined(_WIN32)
vma_aligned_alloc(size_t alignment,size_t size)2539 void *vma_aligned_alloc(size_t alignment, size_t size)
2540 {
2541 return _aligned_malloc(size, alignment);
2542 }
2543 #else
vma_aligned_alloc(size_t alignment,size_t size)2544 void *vma_aligned_alloc(size_t alignment, size_t size)
2545 {
2546 return aligned_alloc(alignment, size);
2547 }
2548 #endif
2549
2550 #if defined(_WIN32)
vma_aligned_free(void * ptr)2551 static void vma_aligned_free(void* ptr)
2552 {
2553 _aligned_free(ptr);
2554 }
2555 #else
vma_aligned_free(void * VMA_NULLABLE ptr)2556 static void vma_aligned_free(void* VMA_NULLABLE ptr)
2557 {
2558 free(ptr);
2559 }
2560 #endif
2561
2562 // If your compiler is not compatible with C++11 and definition of
2563 // aligned_alloc() function is missing, uncommeting following line may help:
2564
2565 //#include <malloc.h>
2566
2567 // Normal assert to check for programmer's errors, especially in Debug configuration.
2568 #ifndef VMA_ASSERT
2569 #ifdef NDEBUG
2570 #define VMA_ASSERT(expr)
2571 #else
2572 #define VMA_ASSERT(expr) assert(expr)
2573 #endif
2574 #endif
2575
2576 // Assert that will be called very often, like inside data structures e.g. operator[].
2577 // Making it non-empty can make program slow.
2578 #ifndef VMA_HEAVY_ASSERT
2579 #ifdef NDEBUG
2580 #define VMA_HEAVY_ASSERT(expr)
2581 #else
2582 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
2583 #endif
2584 #endif
2585
2586 #ifndef VMA_ALIGN_OF
2587 #define VMA_ALIGN_OF(type) (__alignof(type))
2588 #endif
2589
2590 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2591 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
2592 #endif
2593
2594 #ifndef VMA_SYSTEM_ALIGNED_FREE
2595 // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
2596 #if defined(VMA_SYSTEM_FREE)
2597 #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
2598 #else
2599 #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
2600 #endif
2601 #endif
2602
2603 #ifndef VMA_MIN
2604 #define VMA_MIN(v1, v2) ((std::min)((v1), (v2)))
2605 #endif
2606
2607 #ifndef VMA_MAX
2608 #define VMA_MAX(v1, v2) ((std::max)((v1), (v2)))
2609 #endif
2610
2611 #ifndef VMA_SWAP
2612 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
2613 #endif
2614
2615 #ifndef VMA_SORT
2616 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
2617 #endif
2618
2619 #ifndef VMA_DEBUG_LOG
2620 #define VMA_DEBUG_LOG(format, ...)
2621 /*
2622 #define VMA_DEBUG_LOG(format, ...) do { \
2623 printf(format, __VA_ARGS__); \
2624 printf("\n"); \
2625 } while(false)
2626 */
2627 #endif
2628
2629 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2630 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint32_t num)2631 static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
2632 {
2633 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2634 }
VmaUint64ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint64_t num)2635 static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
2636 {
2637 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2638 }
VmaPtrToStr(char * VMA_NOT_NULL outStr,size_t strLen,const void * ptr)2639 static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
2640 {
2641 snprintf(outStr, strLen, "%p", ptr);
2642 }
2643 #endif
2644
2645 #ifndef VMA_MUTEX
2646 class VmaMutex
2647 {
2648 public:
Lock()2649 void Lock() { m_Mutex.lock(); }
Unlock()2650 void Unlock() { m_Mutex.unlock(); }
TryLock()2651 bool TryLock() { return m_Mutex.try_lock(); }
2652 private:
2653 std::mutex m_Mutex;
2654 };
2655 #define VMA_MUTEX VmaMutex
2656 #endif
2657
2658 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
2659 #ifndef VMA_RW_MUTEX
2660 #if VMA_USE_STL_SHARED_MUTEX
2661 // Use std::shared_mutex from C++17.
2662 #include <shared_mutex>
2663 class VmaRWMutex
2664 {
2665 public:
LockRead()2666 void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()2667 void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()2668 bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()2669 void LockWrite() { m_Mutex.lock(); }
UnlockWrite()2670 void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()2671 bool TryLockWrite() { return m_Mutex.try_lock(); }
2672 private:
2673 std::shared_mutex m_Mutex;
2674 };
2675 #define VMA_RW_MUTEX VmaRWMutex
2676 #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
2677 // Use SRWLOCK from WinAPI.
2678 // Minimum supported client = Windows Vista, server = Windows Server 2008.
2679 class VmaRWMutex
2680 {
2681 public:
VmaRWMutex()2682 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()2683 void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()2684 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()2685 bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()2686 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()2687 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()2688 bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
2689 private:
2690 SRWLOCK m_Lock;
2691 };
2692 #define VMA_RW_MUTEX VmaRWMutex
2693 #else
2694 // Less efficient fallback: Use normal mutex.
2695 class VmaRWMutex
2696 {
2697 public:
LockRead()2698 void LockRead() { m_Mutex.Lock(); }
UnlockRead()2699 void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()2700 bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()2701 void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()2702 void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()2703 bool TryLockWrite() { return m_Mutex.TryLock(); }
2704 private:
2705 VMA_MUTEX m_Mutex;
2706 };
2707 #define VMA_RW_MUTEX VmaRWMutex
2708 #endif // #if VMA_USE_STL_SHARED_MUTEX
2709 #endif // #ifndef VMA_RW_MUTEX
2710
2711 /*
2712 If providing your own implementation, you need to implement a subset of std::atomic.
2713 */
2714 #ifndef VMA_ATOMIC_UINT32
2715 #include <atomic>
2716 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2717 #endif
2718
2719 #ifndef VMA_ATOMIC_UINT64
2720 #include <atomic>
2721 #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
2722 #endif
2723
2724 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2725 /**
2726 Every allocation will have its own memory block.
2727 Define to 1 for debugging purposes only.
2728 */
2729 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2730 #endif
2731
2732 #ifndef VMA_MIN_ALIGNMENT
2733 /**
2734 Minimum alignment of all allocations, in bytes.
2735 Set to more than 1 for debugging purposes. Must be power of two.
2736 */
2737 #ifdef VMA_DEBUG_ALIGNMENT // Old name
2738 #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
2739 #else
2740 #define VMA_MIN_ALIGNMENT (1)
2741 #endif
2742 #endif
2743
2744 #ifndef VMA_DEBUG_MARGIN
2745 /**
2746 Minimum margin before and after every allocation, in bytes.
2747 Set nonzero for debugging purposes only.
2748 */
2749 #define VMA_DEBUG_MARGIN (0)
2750 #endif
2751
2752 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
2753 /**
2754 Define this macro to 1 to automatically fill new allocations and destroyed
2755 allocations with some bit pattern.
2756 */
2757 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
2758 #endif
2759
2760 #ifndef VMA_DEBUG_DETECT_CORRUPTION
2761 /**
2762 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
2763 enable writing magic value to the margin before and after every allocation and
2764 validating it, so that memory corruptions (out-of-bounds writes) are detected.
2765 */
2766 #define VMA_DEBUG_DETECT_CORRUPTION (0)
2767 #endif
2768
2769 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2770 /**
2771 Set this to 1 for debugging purposes only, to enable single mutex protecting all
2772 entry calls to the library. Can be useful for debugging multithreading issues.
2773 */
2774 #define VMA_DEBUG_GLOBAL_MUTEX (0)
2775 #endif
2776
2777 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2778 /**
2779 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2780 Set to more than 1 for debugging purposes only. Must be power of two.
2781 */
2782 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2783 #endif
2784
2785 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
2786 /*
2787 Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
2788 and return error instead of leaving up to Vulkan implementation what to do in such cases.
2789 */
2790 #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
2791 #endif
2792
2793 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2794 /// Maximum size of a memory heap in Vulkan to consider it "small".
2795 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2796 #endif
2797
2798 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2799 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2800 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2801 #endif
2802
2803 #ifndef VMA_CLASS_NO_COPY
2804 #define VMA_CLASS_NO_COPY(className) \
2805 private: \
2806 className(const className&) = delete; \
2807 className& operator=(const className&) = delete;
2808 #endif
2809
2810 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
2811
2812 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
2813 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
2814
2815 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
2816 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
2817
2818 /*******************************************************************************
2819 END OF CONFIGURATION
2820 */
2821
2822 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
2823
2824 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
2825 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
2826 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
2827
2828 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
2829
2830 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
2831 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
2832
2833 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)2834 static inline uint32_t VmaCountBitsSet(uint32_t v)
2835 {
2836 uint32_t c = v - ((v >> 1) & 0x55555555);
2837 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
2838 c = ((c >> 4) + c) & 0x0F0F0F0F;
2839 c = ((c >> 8) + c) & 0x00FF00FF;
2840 c = ((c >> 16) + c) & 0x0000FFFF;
2841 return c;
2842 }
2843
2844 /*
2845 Returns true if given number is a power of two.
2846 T must be unsigned integer number or signed integer but always nonnegative.
2847 For 0 returns true.
2848 */
2849 template <typename T>
VmaIsPow2(T x)2850 inline bool VmaIsPow2(T x)
2851 {
2852 return (x & (x-1)) == 0;
2853 }
2854
2855 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
2856 // Use types like uint32_t, uint64_t as T.
2857 template <typename T>
VmaAlignUp(T val,T alignment)2858 static inline T VmaAlignUp(T val, T alignment)
2859 {
2860 VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
2861 return (val + alignment - 1) & ~(alignment - 1);
2862 }
2863 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
2864 // Use types like uint32_t, uint64_t as T.
2865 template <typename T>
VmaAlignDown(T val,T alignment)2866 static inline T VmaAlignDown(T val, T alignment)
2867 {
2868 VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
2869 return val & ~(alignment - 1);
2870 }
2871
2872 // Division with mathematical rounding to nearest number.
2873 template <typename T>
VmaRoundDiv(T x,T y)2874 static inline T VmaRoundDiv(T x, T y)
2875 {
2876 return (x + (y / (T)2)) / y;
2877 }
2878
2879 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)2880 static inline uint32_t VmaNextPow2(uint32_t v)
2881 {
2882 v--;
2883 v |= v >> 1;
2884 v |= v >> 2;
2885 v |= v >> 4;
2886 v |= v >> 8;
2887 v |= v >> 16;
2888 v++;
2889 return v;
2890 }
VmaNextPow2(uint64_t v)2891 static inline uint64_t VmaNextPow2(uint64_t v)
2892 {
2893 v--;
2894 v |= v >> 1;
2895 v |= v >> 2;
2896 v |= v >> 4;
2897 v |= v >> 8;
2898 v |= v >> 16;
2899 v |= v >> 32;
2900 v++;
2901 return v;
2902 }
2903
2904 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)2905 static inline uint32_t VmaPrevPow2(uint32_t v)
2906 {
2907 v |= v >> 1;
2908 v |= v >> 2;
2909 v |= v >> 4;
2910 v |= v >> 8;
2911 v |= v >> 16;
2912 v = v ^ (v >> 1);
2913 return v;
2914 }
VmaPrevPow2(uint64_t v)2915 static inline uint64_t VmaPrevPow2(uint64_t v)
2916 {
2917 v |= v >> 1;
2918 v |= v >> 2;
2919 v |= v >> 4;
2920 v |= v >> 8;
2921 v |= v >> 16;
2922 v |= v >> 32;
2923 v = v ^ (v >> 1);
2924 return v;
2925 }
2926
VmaStrIsEmpty(const char * pStr)2927 static inline bool VmaStrIsEmpty(const char* pStr)
2928 {
2929 return pStr == VMA_NULL || *pStr == '\0';
2930 }
2931
2932 #if VMA_STATS_STRING_ENABLED
2933
VmaAlgorithmToStr(uint32_t algorithm)2934 static const char* VmaAlgorithmToStr(uint32_t algorithm)
2935 {
2936 switch(algorithm)
2937 {
2938 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
2939 return "Linear";
2940 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
2941 return "Buddy";
2942 case 0:
2943 return "Default";
2944 default:
2945 VMA_ASSERT(0);
2946 return "";
2947 }
2948 }
2949
2950 #endif // #if VMA_STATS_STRING_ENABLED
2951
2952 #ifndef VMA_SORT
2953
2954 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)2955 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
2956 {
2957 Iterator centerValue = end; --centerValue;
2958 Iterator insertIndex = beg;
2959 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
2960 {
2961 if(cmp(*memTypeIndex, *centerValue))
2962 {
2963 if(insertIndex != memTypeIndex)
2964 {
2965 VMA_SWAP(*memTypeIndex, *insertIndex);
2966 }
2967 ++insertIndex;
2968 }
2969 }
2970 if(insertIndex != centerValue)
2971 {
2972 VMA_SWAP(*insertIndex, *centerValue);
2973 }
2974 return insertIndex;
2975 }
2976
2977 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)2978 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
2979 {
2980 if(beg < end)
2981 {
2982 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
2983 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
2984 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
2985 }
2986 }
2987
2988 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
2989
2990 #endif // #ifndef VMA_SORT
2991
2992 /*
2993 Returns true if two memory blocks occupy overlapping pages.
2994 ResourceA must be in less memory offset than ResourceB.
2995
2996 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
2997 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
2998 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)2999 static inline bool VmaBlocksOnSamePage(
3000 VkDeviceSize resourceAOffset,
3001 VkDeviceSize resourceASize,
3002 VkDeviceSize resourceBOffset,
3003 VkDeviceSize pageSize)
3004 {
3005 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3006 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3007 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3008 VkDeviceSize resourceBStart = resourceBOffset;
3009 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3010 return resourceAEndPage == resourceBStartPage;
3011 }
3012
3013 enum VmaSuballocationType
3014 {
3015 VMA_SUBALLOCATION_TYPE_FREE = 0,
3016 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3017 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3018 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3019 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3020 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3021 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3022 };
3023
3024 /*
3025 Returns true if given suballocation types could conflict and must respect
3026 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3027 or linear image and another one is optimal image. If type is unknown, behave
3028 conservatively.
3029 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3030 static inline bool VmaIsBufferImageGranularityConflict(
3031 VmaSuballocationType suballocType1,
3032 VmaSuballocationType suballocType2)
3033 {
3034 if(suballocType1 > suballocType2)
3035 {
3036 VMA_SWAP(suballocType1, suballocType2);
3037 }
3038
3039 switch(suballocType1)
3040 {
3041 case VMA_SUBALLOCATION_TYPE_FREE:
3042 return false;
3043 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3044 return true;
3045 case VMA_SUBALLOCATION_TYPE_BUFFER:
3046 return
3047 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3048 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3049 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3050 return
3051 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3052 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3053 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3054 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3055 return
3056 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3057 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3058 return false;
3059 default:
3060 VMA_ASSERT(0);
3061 return true;
3062 }
3063 }
3064
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3065 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3066 {
3067 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3068 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3069 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3070 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3071 {
3072 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3073 }
3074 #else
3075 // no-op
3076 #endif
3077 }
3078
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3079 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3080 {
3081 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3082 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3083 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3084 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3085 {
3086 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3087 {
3088 return false;
3089 }
3090 }
3091 #endif
3092 return true;
3093 }
3094
3095 /*
3096 Fills structure with parameters of an example buffer to be used for transfers
3097 during GPU memory defragmentation.
3098 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)3099 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
3100 {
3101 memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
3102 outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3103 outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3104 outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
3105 }
3106
3107 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3108 struct VmaMutexLock
3109 {
VMA_CLASS_NO_COPYVmaMutexLock3110 VMA_CLASS_NO_COPY(VmaMutexLock)
3111 public:
3112 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
3113 m_pMutex(useMutex ? &mutex : VMA_NULL)
3114 { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock3115 ~VmaMutexLock()
3116 { if(m_pMutex) { m_pMutex->Unlock(); } }
3117 private:
3118 VMA_MUTEX* m_pMutex;
3119 };
3120
3121 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3122 struct VmaMutexLockRead
3123 {
VMA_CLASS_NO_COPYVmaMutexLockRead3124 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3125 public:
3126 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3127 m_pMutex(useMutex ? &mutex : VMA_NULL)
3128 { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead3129 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3130 private:
3131 VMA_RW_MUTEX* m_pMutex;
3132 };
3133
3134 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3135 struct VmaMutexLockWrite
3136 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3137 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3138 public:
3139 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3140 m_pMutex(useMutex ? &mutex : VMA_NULL)
3141 { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite3142 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3143 private:
3144 VMA_RW_MUTEX* m_pMutex;
3145 };
3146
3147 #if VMA_DEBUG_GLOBAL_MUTEX
3148 static VMA_MUTEX gDebugGlobalMutex;
3149 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3150 #else
3151 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3152 #endif
3153
3154 /*
3155 Performs binary search and returns iterator to first element that is greater or
3156 equal to (key), according to comparison (cmp).
3157
3158 Cmp should return true if first argument is less than second argument.
3159
3160 Returned value is the found element, if present in the collection or place where
3161 new element with value (key) should be inserted.
3162 */
3163 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)3164 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
3165 {
3166 size_t down = 0, up = (end - beg);
3167 while(down < up)
3168 {
3169 const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
3170 if(cmp(*(beg+mid), key))
3171 {
3172 down = mid + 1;
3173 }
3174 else
3175 {
3176 up = mid;
3177 }
3178 }
3179 return beg + down;
3180 }
3181
3182 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)3183 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
3184 {
3185 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
3186 beg, end, value, cmp);
3187 if(it == end ||
3188 (!cmp(*it, value) && !cmp(value, *it)))
3189 {
3190 return it;
3191 }
3192 return end;
3193 }
3194
3195 /*
3196 Returns true if all pointers in the array are not-null and unique.
3197 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3198 T must be pointer type, e.g. VmaAllocation, VmaPool.
3199 */
3200 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3201 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3202 {
3203 for(uint32_t i = 0; i < count; ++i)
3204 {
3205 const T iPtr = arr[i];
3206 if(iPtr == VMA_NULL)
3207 {
3208 return false;
3209 }
3210 for(uint32_t j = i + 1; j < count; ++j)
3211 {
3212 if(iPtr == arr[j])
3213 {
3214 return false;
3215 }
3216 }
3217 }
3218 return true;
3219 }
3220
3221 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)3222 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
3223 {
3224 newStruct->pNext = mainStruct->pNext;
3225 mainStruct->pNext = newStruct;
3226 }
3227
3228 ////////////////////////////////////////////////////////////////////////////////
3229 // Memory allocation
3230
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3231 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3232 {
3233 void* result = VMA_NULL;
3234 if((pAllocationCallbacks != VMA_NULL) &&
3235 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3236 {
3237 result = (*pAllocationCallbacks->pfnAllocation)(
3238 pAllocationCallbacks->pUserData,
3239 size,
3240 alignment,
3241 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3242 }
3243 else
3244 {
3245 result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3246 }
3247 VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
3248 return result;
3249 }
3250
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3251 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3252 {
3253 if((pAllocationCallbacks != VMA_NULL) &&
3254 (pAllocationCallbacks->pfnFree != VMA_NULL))
3255 {
3256 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3257 }
3258 else
3259 {
3260 VMA_SYSTEM_ALIGNED_FREE(ptr);
3261 }
3262 }
3263
3264 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3265 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3266 {
3267 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3268 }
3269
3270 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3271 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3272 {
3273 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3274 }
3275
3276 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3277
3278 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3279
3280 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3281 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3282 {
3283 ptr->~T();
3284 VmaFree(pAllocationCallbacks, ptr);
3285 }
3286
3287 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3288 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3289 {
3290 if(ptr != VMA_NULL)
3291 {
3292 for(size_t i = count; i--; )
3293 {
3294 ptr[i].~T();
3295 }
3296 VmaFree(pAllocationCallbacks, ptr);
3297 }
3298 }
3299
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)3300 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
3301 {
3302 if(srcStr != VMA_NULL)
3303 {
3304 const size_t len = strlen(srcStr);
3305 char* const result = vma_new_array(allocs, char, len + 1);
3306 memcpy(result, srcStr, len + 1);
3307 return result;
3308 }
3309 return VMA_NULL;
3310 }
3311
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr,size_t strLen)3312 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
3313 {
3314 if(srcStr != VMA_NULL)
3315 {
3316 char* const result = vma_new_array(allocs, char, strLen + 1);
3317 memcpy(result, srcStr, strLen);
3318 result[strLen] = '\0';
3319 return result;
3320 }
3321 return VMA_NULL;
3322 }
3323
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)3324 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
3325 {
3326 if(str != VMA_NULL)
3327 {
3328 const size_t len = strlen(str);
3329 vma_delete_array(allocs, str, len + 1);
3330 }
3331 }
3332
3333 // STL-compatible allocator.
3334 template<typename T>
3335 class VmaStlAllocator
3336 {
3337 public:
3338 const VkAllocationCallbacks* const m_pCallbacks;
3339 typedef T value_type;
3340
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)3341 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)3342 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3343
allocate(size_t n)3344 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)3345 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3346
3347 template<typename U>
3348 bool operator==(const VmaStlAllocator<U>& rhs) const
3349 {
3350 return m_pCallbacks == rhs.m_pCallbacks;
3351 }
3352 template<typename U>
3353 bool operator!=(const VmaStlAllocator<U>& rhs) const
3354 {
3355 return m_pCallbacks != rhs.m_pCallbacks;
3356 }
3357
3358 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3359 VmaStlAllocator(const VmaStlAllocator&) = default;
3360 };
3361
3362 #if VMA_USE_STL_VECTOR
3363
3364 #define VmaVector std::vector
3365
3366 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)3367 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3368 {
3369 vec.insert(vec.begin() + index, item);
3370 }
3371
3372 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)3373 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3374 {
3375 vec.erase(vec.begin() + index);
3376 }
3377
3378 #else // #if VMA_USE_STL_VECTOR
3379
3380 /* Class with interface compatible with subset of std::vector.
3381 T must be POD because constructors and destructors are not called and memcpy is
3382 used for these objects. */
3383 template<typename T, typename AllocatorT>
3384 class VmaVector
3385 {
3386 public:
3387 typedef T value_type;
3388
VmaVector(const AllocatorT & allocator)3389 VmaVector(const AllocatorT& allocator) :
3390 m_Allocator(allocator),
3391 m_pArray(VMA_NULL),
3392 m_Count(0),
3393 m_Capacity(0)
3394 {
3395 }
3396
VmaVector(size_t count,const AllocatorT & allocator)3397 VmaVector(size_t count, const AllocatorT& allocator) :
3398 m_Allocator(allocator),
3399 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3400 m_Count(count),
3401 m_Capacity(count)
3402 {
3403 }
3404
3405 // This version of the constructor is here for compatibility with pre-C++14 std::vector.
3406 // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)3407 VmaVector(size_t count, const T& value, const AllocatorT& allocator)
3408 : VmaVector(count, allocator) {}
3409
VmaVector(const VmaVector<T,AllocatorT> & src)3410 VmaVector(const VmaVector<T, AllocatorT>& src) :
3411 m_Allocator(src.m_Allocator),
3412 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3413 m_Count(src.m_Count),
3414 m_Capacity(src.m_Count)
3415 {
3416 if(m_Count != 0)
3417 {
3418 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3419 }
3420 }
3421
~VmaVector()3422 ~VmaVector()
3423 {
3424 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3425 }
3426
3427 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3428 {
3429 if(&rhs != this)
3430 {
3431 resize(rhs.m_Count);
3432 if(m_Count != 0)
3433 {
3434 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3435 }
3436 }
3437 return *this;
3438 }
3439
empty()3440 bool empty() const { return m_Count == 0; }
size()3441 size_t size() const { return m_Count; }
data()3442 T* data() { return m_pArray; }
data()3443 const T* data() const { return m_pArray; }
3444
3445 T& operator[](size_t index)
3446 {
3447 VMA_HEAVY_ASSERT(index < m_Count);
3448 return m_pArray[index];
3449 }
3450 const T& operator[](size_t index) const
3451 {
3452 VMA_HEAVY_ASSERT(index < m_Count);
3453 return m_pArray[index];
3454 }
3455
front()3456 T& front()
3457 {
3458 VMA_HEAVY_ASSERT(m_Count > 0);
3459 return m_pArray[0];
3460 }
front()3461 const T& front() const
3462 {
3463 VMA_HEAVY_ASSERT(m_Count > 0);
3464 return m_pArray[0];
3465 }
back()3466 T& back()
3467 {
3468 VMA_HEAVY_ASSERT(m_Count > 0);
3469 return m_pArray[m_Count - 1];
3470 }
back()3471 const T& back() const
3472 {
3473 VMA_HEAVY_ASSERT(m_Count > 0);
3474 return m_pArray[m_Count - 1];
3475 }
3476
3477 void reserve(size_t newCapacity, bool freeMemory = false)
3478 {
3479 newCapacity = VMA_MAX(newCapacity, m_Count);
3480
3481 if((newCapacity < m_Capacity) && !freeMemory)
3482 {
3483 newCapacity = m_Capacity;
3484 }
3485
3486 if(newCapacity != m_Capacity)
3487 {
3488 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
3489 if(m_Count != 0)
3490 {
3491 memcpy(newArray, m_pArray, m_Count * sizeof(T));
3492 }
3493 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3494 m_Capacity = newCapacity;
3495 m_pArray = newArray;
3496 }
3497 }
3498
resize(size_t newCount)3499 void resize(size_t newCount)
3500 {
3501 size_t newCapacity = m_Capacity;
3502 if(newCount > m_Capacity)
3503 {
3504 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
3505 }
3506
3507 if(newCapacity != m_Capacity)
3508 {
3509 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
3510 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
3511 if(elementsToCopy != 0)
3512 {
3513 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
3514 }
3515 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3516 m_Capacity = newCapacity;
3517 m_pArray = newArray;
3518 }
3519
3520 m_Count = newCount;
3521 }
3522
clear()3523 void clear()
3524 {
3525 resize(0);
3526 }
3527
shrink_to_fit()3528 void shrink_to_fit()
3529 {
3530 if(m_Capacity > m_Count)
3531 {
3532 T* newArray = VMA_NULL;
3533 if(m_Count > 0)
3534 {
3535 newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
3536 memcpy(newArray, m_pArray, m_Count * sizeof(T));
3537 }
3538 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3539 m_Capacity = m_Count;
3540 m_pArray = newArray;
3541 }
3542 }
3543
insert(size_t index,const T & src)3544 void insert(size_t index, const T& src)
3545 {
3546 VMA_HEAVY_ASSERT(index <= m_Count);
3547 const size_t oldCount = size();
3548 resize(oldCount + 1);
3549 if(index < oldCount)
3550 {
3551 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
3552 }
3553 m_pArray[index] = src;
3554 }
3555
remove(size_t index)3556 void remove(size_t index)
3557 {
3558 VMA_HEAVY_ASSERT(index < m_Count);
3559 const size_t oldCount = size();
3560 if(index < oldCount - 1)
3561 {
3562 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
3563 }
3564 resize(oldCount - 1);
3565 }
3566
push_back(const T & src)3567 void push_back(const T& src)
3568 {
3569 const size_t newIndex = size();
3570 resize(newIndex + 1);
3571 m_pArray[newIndex] = src;
3572 }
3573
pop_back()3574 void pop_back()
3575 {
3576 VMA_HEAVY_ASSERT(m_Count > 0);
3577 resize(size() - 1);
3578 }
3579
push_front(const T & src)3580 void push_front(const T& src)
3581 {
3582 insert(0, src);
3583 }
3584
pop_front()3585 void pop_front()
3586 {
3587 VMA_HEAVY_ASSERT(m_Count > 0);
3588 remove(0);
3589 }
3590
3591 typedef T* iterator;
3592 typedef const T* const_iterator;
3593
begin()3594 iterator begin() { return m_pArray; }
end()3595 iterator end() { return m_pArray + m_Count; }
cbegin()3596 const_iterator cbegin() const { return m_pArray; }
cend()3597 const_iterator cend() const { return m_pArray + m_Count; }
begin()3598 const_iterator begin() const { return cbegin(); }
end()3599 const_iterator end() const { return cend(); }
3600
3601 private:
3602 AllocatorT m_Allocator;
3603 T* m_pArray;
3604 size_t m_Count;
3605 size_t m_Capacity;
3606 };
3607
3608 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)3609 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
3610 {
3611 vec.insert(index, item);
3612 }
3613
3614 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)3615 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
3616 {
3617 vec.remove(index);
3618 }
3619
3620 #endif // #if VMA_USE_STL_VECTOR
3621
3622 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)3623 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
3624 {
3625 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3626 vector.data(),
3627 vector.data() + vector.size(),
3628 value,
3629 CmpLess()) - vector.data();
3630 VmaVectorInsert(vector, indexToInsert, value);
3631 return indexToInsert;
3632 }
3633
3634 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)3635 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
3636 {
3637 CmpLess comparator;
3638 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3639 vector.begin(),
3640 vector.end(),
3641 value,
3642 comparator);
3643 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3644 {
3645 size_t indexToRemove = it - vector.begin();
3646 VmaVectorRemove(vector, indexToRemove);
3647 return true;
3648 }
3649 return false;
3650 }
3651
3652 ////////////////////////////////////////////////////////////////////////////////
3653 // class VmaSmallVector
3654
3655 /*
3656 This is a vector (a variable-sized array), optimized for the case when the array is small.
3657
3658 It contains some number of elements in-place, which allows it to avoid heap allocation
3659 when the actual number of elements is below that threshold. This allows normal "small"
3660 cases to be fast without losing generality for large inputs.
3661 */
3662
3663 template<typename T, typename AllocatorT, size_t N>
3664 class VmaSmallVector
3665 {
3666 public:
3667 typedef T value_type;
3668
VmaSmallVector(const AllocatorT & allocator)3669 VmaSmallVector(const AllocatorT& allocator) :
3670 m_Count(0),
3671 m_DynamicArray(allocator)
3672 {
3673 }
VmaSmallVector(size_t count,const AllocatorT & allocator)3674 VmaSmallVector(size_t count, const AllocatorT& allocator) :
3675 m_Count(count),
3676 m_DynamicArray(count > N ? count : 0, allocator)
3677 {
3678 }
3679 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
3680 VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
3681 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
3682 VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
3683
empty()3684 bool empty() const { return m_Count == 0; }
size()3685 size_t size() const { return m_Count; }
data()3686 T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()3687 const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
3688
3689 T& operator[](size_t index)
3690 {
3691 VMA_HEAVY_ASSERT(index < m_Count);
3692 return data()[index];
3693 }
3694 const T& operator[](size_t index) const
3695 {
3696 VMA_HEAVY_ASSERT(index < m_Count);
3697 return data()[index];
3698 }
3699
front()3700 T& front()
3701 {
3702 VMA_HEAVY_ASSERT(m_Count > 0);
3703 return data()[0];
3704 }
front()3705 const T& front() const
3706 {
3707 VMA_HEAVY_ASSERT(m_Count > 0);
3708 return data()[0];
3709 }
back()3710 T& back()
3711 {
3712 VMA_HEAVY_ASSERT(m_Count > 0);
3713 return data()[m_Count - 1];
3714 }
back()3715 const T& back() const
3716 {
3717 VMA_HEAVY_ASSERT(m_Count > 0);
3718 return data()[m_Count - 1];
3719 }
3720
3721 void resize(size_t newCount, bool freeMemory = false)
3722 {
3723 if(newCount > N && m_Count > N)
3724 {
3725 // Any direction, staying in m_DynamicArray
3726 m_DynamicArray.resize(newCount);
3727 if(freeMemory)
3728 {
3729 m_DynamicArray.shrink_to_fit();
3730 }
3731 }
3732 else if(newCount > N && m_Count <= N)
3733 {
3734 // Growing, moving from m_StaticArray to m_DynamicArray
3735 m_DynamicArray.resize(newCount);
3736 if(m_Count > 0)
3737 {
3738 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
3739 }
3740 }
3741 else if(newCount <= N && m_Count > N)
3742 {
3743 // Shrinking, moving from m_DynamicArray to m_StaticArray
3744 if(newCount > 0)
3745 {
3746 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
3747 }
3748 m_DynamicArray.resize(0);
3749 if(freeMemory)
3750 {
3751 m_DynamicArray.shrink_to_fit();
3752 }
3753 }
3754 else
3755 {
3756 // Any direction, staying in m_StaticArray - nothing to do here
3757 }
3758 m_Count = newCount;
3759 }
3760
3761 void clear(bool freeMemory = false)
3762 {
3763 m_DynamicArray.clear();
3764 if(freeMemory)
3765 {
3766 m_DynamicArray.shrink_to_fit();
3767 }
3768 m_Count = 0;
3769 }
3770
insert(size_t index,const T & src)3771 void insert(size_t index, const T& src)
3772 {
3773 VMA_HEAVY_ASSERT(index <= m_Count);
3774 const size_t oldCount = size();
3775 resize(oldCount + 1);
3776 T* const dataPtr = data();
3777 if(index < oldCount)
3778 {
3779 // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
3780 memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
3781 }
3782 dataPtr[index] = src;
3783 }
3784
remove(size_t index)3785 void remove(size_t index)
3786 {
3787 VMA_HEAVY_ASSERT(index < m_Count);
3788 const size_t oldCount = size();
3789 if(index < oldCount - 1)
3790 {
3791 // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
3792 T* const dataPtr = data();
3793 memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
3794 }
3795 resize(oldCount - 1);
3796 }
3797
push_back(const T & src)3798 void push_back(const T& src)
3799 {
3800 const size_t newIndex = size();
3801 resize(newIndex + 1);
3802 data()[newIndex] = src;
3803 }
3804
pop_back()3805 void pop_back()
3806 {
3807 VMA_HEAVY_ASSERT(m_Count > 0);
3808 resize(size() - 1);
3809 }
3810
push_front(const T & src)3811 void push_front(const T& src)
3812 {
3813 insert(0, src);
3814 }
3815
pop_front()3816 void pop_front()
3817 {
3818 VMA_HEAVY_ASSERT(m_Count > 0);
3819 remove(0);
3820 }
3821
3822 typedef T* iterator;
3823
begin()3824 iterator begin() { return data(); }
end()3825 iterator end() { return data() + m_Count; }
3826
3827 private:
3828 size_t m_Count;
3829 T m_StaticArray[N]; // Used when m_Size <= N
3830 VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
3831 };
3832
3833 ////////////////////////////////////////////////////////////////////////////////
3834 // class VmaPoolAllocator
3835
3836 /*
3837 Allocator for objects of type T using a list of arrays (pools) to speed up
3838 allocation. Number of elements that can be allocated is not bounded because
3839 allocator can create multiple blocks.
3840 */
3841 template<typename T>
3842 class VmaPoolAllocator
3843 {
3844 VMA_CLASS_NO_COPY(VmaPoolAllocator)
3845 public:
3846 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
3847 ~VmaPoolAllocator();
3848 template<typename... Types> T* Alloc(Types&&... args);
3849 void Free(T* ptr);
3850
3851 private:
3852 union Item
3853 {
3854 uint32_t NextFreeIndex;
3855 alignas(T) char Value[sizeof(T)];
3856 };
3857
3858 struct ItemBlock
3859 {
3860 Item* pItems;
3861 uint32_t Capacity;
3862 uint32_t FirstFreeIndex;
3863 };
3864
3865 const VkAllocationCallbacks* m_pAllocationCallbacks;
3866 const uint32_t m_FirstBlockCapacity;
3867 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
3868
3869 ItemBlock& CreateNewBlock();
3870 };
3871
3872 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)3873 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
3874 m_pAllocationCallbacks(pAllocationCallbacks),
3875 m_FirstBlockCapacity(firstBlockCapacity),
3876 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
3877 {
3878 VMA_ASSERT(m_FirstBlockCapacity > 1);
3879 }
3880
3881 template<typename T>
~VmaPoolAllocator()3882 VmaPoolAllocator<T>::~VmaPoolAllocator()
3883 {
3884 for(size_t i = m_ItemBlocks.size(); i--; )
3885 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
3886 m_ItemBlocks.clear();
3887 }
3888
3889 template<typename T>
Alloc(Types &&...args)3890 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
3891 {
3892 for(size_t i = m_ItemBlocks.size(); i--; )
3893 {
3894 ItemBlock& block = m_ItemBlocks[i];
3895 // This block has some free items: Use first one.
3896 if(block.FirstFreeIndex != UINT32_MAX)
3897 {
3898 Item* const pItem = &block.pItems[block.FirstFreeIndex];
3899 block.FirstFreeIndex = pItem->NextFreeIndex;
3900 T* result = (T*)&pItem->Value;
3901 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
3902 return result;
3903 }
3904 }
3905
3906 // No block has free item: Create new one and use it.
3907 ItemBlock& newBlock = CreateNewBlock();
3908 Item* const pItem = &newBlock.pItems[0];
3909 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
3910 T* result = (T*)&pItem->Value;
3911 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
3912 return result;
3913 }
3914
3915 template<typename T>
Free(T * ptr)3916 void VmaPoolAllocator<T>::Free(T* ptr)
3917 {
3918 // Search all memory blocks to find ptr.
3919 for(size_t i = m_ItemBlocks.size(); i--; )
3920 {
3921 ItemBlock& block = m_ItemBlocks[i];
3922
3923 // Casting to union.
3924 Item* pItemPtr;
3925 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
3926
3927 // Check if pItemPtr is in address range of this block.
3928 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
3929 {
3930 ptr->~T(); // Explicit destructor call.
3931 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
3932 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
3933 block.FirstFreeIndex = index;
3934 return;
3935 }
3936 }
3937 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
3938 }
3939
3940 template<typename T>
CreateNewBlock()3941 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
3942 {
3943 const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
3944 m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
3945
3946 const ItemBlock newBlock = {
3947 vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
3948 newBlockCapacity,
3949 0 };
3950
3951 m_ItemBlocks.push_back(newBlock);
3952
3953 // Setup singly-linked list of all free items in this block.
3954 for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
3955 newBlock.pItems[i].NextFreeIndex = i + 1;
3956 newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
3957 return m_ItemBlocks.back();
3958 }
3959
3960 ////////////////////////////////////////////////////////////////////////////////
3961 // class VmaRawList, VmaList
3962
3963 #if VMA_USE_STL_LIST
3964
3965 #define VmaList std::list
3966
3967 #else // #if VMA_USE_STL_LIST
3968
3969 template<typename T>
3970 struct VmaListItem
3971 {
3972 VmaListItem* pPrev;
3973 VmaListItem* pNext;
3974 T Value;
3975 };
3976
3977 // Doubly linked list.
3978 template<typename T>
3979 class VmaRawList
3980 {
3981 VMA_CLASS_NO_COPY(VmaRawList)
3982 public:
3983 typedef VmaListItem<T> ItemType;
3984
3985 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
3986 ~VmaRawList();
3987 void Clear();
3988
GetCount()3989 size_t GetCount() const { return m_Count; }
IsEmpty()3990 bool IsEmpty() const { return m_Count == 0; }
3991
Front()3992 ItemType* Front() { return m_pFront; }
Front()3993 const ItemType* Front() const { return m_pFront; }
Back()3994 ItemType* Back() { return m_pBack; }
Back()3995 const ItemType* Back() const { return m_pBack; }
3996
3997 ItemType* PushBack();
3998 ItemType* PushFront();
3999 ItemType* PushBack(const T& value);
4000 ItemType* PushFront(const T& value);
4001 void PopBack();
4002 void PopFront();
4003
4004 // Item can be null - it means PushBack.
4005 ItemType* InsertBefore(ItemType* pItem);
4006 // Item can be null - it means PushFront.
4007 ItemType* InsertAfter(ItemType* pItem);
4008
4009 ItemType* InsertBefore(ItemType* pItem, const T& value);
4010 ItemType* InsertAfter(ItemType* pItem, const T& value);
4011
4012 void Remove(ItemType* pItem);
4013
4014 private:
4015 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4016 VmaPoolAllocator<ItemType> m_ItemAllocator;
4017 ItemType* m_pFront;
4018 ItemType* m_pBack;
4019 size_t m_Count;
4020 };
4021
4022 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4023 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4024 m_pAllocationCallbacks(pAllocationCallbacks),
4025 m_ItemAllocator(pAllocationCallbacks, 128),
4026 m_pFront(VMA_NULL),
4027 m_pBack(VMA_NULL),
4028 m_Count(0)
4029 {
4030 }
4031
4032 template<typename T>
4033 VmaRawList<T>::~VmaRawList() = default;
4034 // Intentionally not calling Clear, because that would be unnecessary
4035 // computations to return all items to m_ItemAllocator as free.
4036
4037 template<typename T>
Clear()4038 void VmaRawList<T>::Clear()
4039 {
4040 if(IsEmpty() == false)
4041 {
4042 ItemType* pItem = m_pBack;
4043 while(pItem != VMA_NULL)
4044 {
4045 ItemType* const pPrevItem = pItem->pPrev;
4046 m_ItemAllocator.Free(pItem);
4047 pItem = pPrevItem;
4048 }
4049 m_pFront = VMA_NULL;
4050 m_pBack = VMA_NULL;
4051 m_Count = 0;
4052 }
4053 }
4054
4055 template<typename T>
PushBack()4056 VmaListItem<T>* VmaRawList<T>::PushBack()
4057 {
4058 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4059 pNewItem->pNext = VMA_NULL;
4060 if(IsEmpty())
4061 {
4062 pNewItem->pPrev = VMA_NULL;
4063 m_pFront = pNewItem;
4064 m_pBack = pNewItem;
4065 m_Count = 1;
4066 }
4067 else
4068 {
4069 pNewItem->pPrev = m_pBack;
4070 m_pBack->pNext = pNewItem;
4071 m_pBack = pNewItem;
4072 ++m_Count;
4073 }
4074 return pNewItem;
4075 }
4076
4077 template<typename T>
PushFront()4078 VmaListItem<T>* VmaRawList<T>::PushFront()
4079 {
4080 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4081 pNewItem->pPrev = VMA_NULL;
4082 if(IsEmpty())
4083 {
4084 pNewItem->pNext = VMA_NULL;
4085 m_pFront = pNewItem;
4086 m_pBack = pNewItem;
4087 m_Count = 1;
4088 }
4089 else
4090 {
4091 pNewItem->pNext = m_pFront;
4092 m_pFront->pPrev = pNewItem;
4093 m_pFront = pNewItem;
4094 ++m_Count;
4095 }
4096 return pNewItem;
4097 }
4098
4099 template<typename T>
PushBack(const T & value)4100 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4101 {
4102 ItemType* const pNewItem = PushBack();
4103 pNewItem->Value = value;
4104 return pNewItem;
4105 }
4106
4107 template<typename T>
PushFront(const T & value)4108 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4109 {
4110 ItemType* const pNewItem = PushFront();
4111 pNewItem->Value = value;
4112 return pNewItem;
4113 }
4114
4115 template<typename T>
PopBack()4116 void VmaRawList<T>::PopBack()
4117 {
4118 VMA_HEAVY_ASSERT(m_Count > 0);
4119 ItemType* const pBackItem = m_pBack;
4120 ItemType* const pPrevItem = pBackItem->pPrev;
4121 if(pPrevItem != VMA_NULL)
4122 {
4123 pPrevItem->pNext = VMA_NULL;
4124 }
4125 m_pBack = pPrevItem;
4126 m_ItemAllocator.Free(pBackItem);
4127 --m_Count;
4128 }
4129
4130 template<typename T>
PopFront()4131 void VmaRawList<T>::PopFront()
4132 {
4133 VMA_HEAVY_ASSERT(m_Count > 0);
4134 ItemType* const pFrontItem = m_pFront;
4135 ItemType* const pNextItem = pFrontItem->pNext;
4136 if(pNextItem != VMA_NULL)
4137 {
4138 pNextItem->pPrev = VMA_NULL;
4139 }
4140 m_pFront = pNextItem;
4141 m_ItemAllocator.Free(pFrontItem);
4142 --m_Count;
4143 }
4144
4145 template<typename T>
Remove(ItemType * pItem)4146 void VmaRawList<T>::Remove(ItemType* pItem)
4147 {
4148 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4149 VMA_HEAVY_ASSERT(m_Count > 0);
4150
4151 if(pItem->pPrev != VMA_NULL)
4152 {
4153 pItem->pPrev->pNext = pItem->pNext;
4154 }
4155 else
4156 {
4157 VMA_HEAVY_ASSERT(m_pFront == pItem);
4158 m_pFront = pItem->pNext;
4159 }
4160
4161 if(pItem->pNext != VMA_NULL)
4162 {
4163 pItem->pNext->pPrev = pItem->pPrev;
4164 }
4165 else
4166 {
4167 VMA_HEAVY_ASSERT(m_pBack == pItem);
4168 m_pBack = pItem->pPrev;
4169 }
4170
4171 m_ItemAllocator.Free(pItem);
4172 --m_Count;
4173 }
4174
4175 template<typename T>
InsertBefore(ItemType * pItem)4176 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4177 {
4178 if(pItem != VMA_NULL)
4179 {
4180 ItemType* const prevItem = pItem->pPrev;
4181 ItemType* const newItem = m_ItemAllocator.Alloc();
4182 newItem->pPrev = prevItem;
4183 newItem->pNext = pItem;
4184 pItem->pPrev = newItem;
4185 if(prevItem != VMA_NULL)
4186 {
4187 prevItem->pNext = newItem;
4188 }
4189 else
4190 {
4191 VMA_HEAVY_ASSERT(m_pFront == pItem);
4192 m_pFront = newItem;
4193 }
4194 ++m_Count;
4195 return newItem;
4196 }
4197 else
4198 return PushBack();
4199 }
4200
4201 template<typename T>
InsertAfter(ItemType * pItem)4202 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4203 {
4204 if(pItem != VMA_NULL)
4205 {
4206 ItemType* const nextItem = pItem->pNext;
4207 ItemType* const newItem = m_ItemAllocator.Alloc();
4208 newItem->pNext = nextItem;
4209 newItem->pPrev = pItem;
4210 pItem->pNext = newItem;
4211 if(nextItem != VMA_NULL)
4212 {
4213 nextItem->pPrev = newItem;
4214 }
4215 else
4216 {
4217 VMA_HEAVY_ASSERT(m_pBack == pItem);
4218 m_pBack = newItem;
4219 }
4220 ++m_Count;
4221 return newItem;
4222 }
4223 else
4224 return PushFront();
4225 }
4226
4227 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4228 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4229 {
4230 ItemType* const newItem = InsertBefore(pItem);
4231 newItem->Value = value;
4232 return newItem;
4233 }
4234
4235 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4236 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4237 {
4238 ItemType* const newItem = InsertAfter(pItem);
4239 newItem->Value = value;
4240 return newItem;
4241 }
4242
4243 template<typename T, typename AllocatorT>
4244 class VmaList
4245 {
VMA_CLASS_NO_COPY(VmaList)4246 VMA_CLASS_NO_COPY(VmaList)
4247 public:
4248 class iterator
4249 {
4250 public:
4251 iterator() :
4252 m_pList(VMA_NULL),
4253 m_pItem(VMA_NULL)
4254 {
4255 }
4256
4257 T& operator*() const
4258 {
4259 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4260 return m_pItem->Value;
4261 }
4262 T* operator->() const
4263 {
4264 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4265 return &m_pItem->Value;
4266 }
4267
4268 iterator& operator++()
4269 {
4270 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4271 m_pItem = m_pItem->pNext;
4272 return *this;
4273 }
4274 iterator& operator--()
4275 {
4276 if(m_pItem != VMA_NULL)
4277 {
4278 m_pItem = m_pItem->pPrev;
4279 }
4280 else
4281 {
4282 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4283 m_pItem = m_pList->Back();
4284 }
4285 return *this;
4286 }
4287
4288 iterator operator++(int)
4289 {
4290 iterator result = *this;
4291 ++*this;
4292 return result;
4293 }
4294 iterator operator--(int)
4295 {
4296 iterator result = *this;
4297 --*this;
4298 return result;
4299 }
4300
4301 bool operator==(const iterator& rhs) const
4302 {
4303 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4304 return m_pItem == rhs.m_pItem;
4305 }
4306 bool operator!=(const iterator& rhs) const
4307 {
4308 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4309 return m_pItem != rhs.m_pItem;
4310 }
4311
4312 private:
4313 VmaRawList<T>* m_pList;
4314 VmaListItem<T>* m_pItem;
4315
4316 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4317 m_pList(pList),
4318 m_pItem(pItem)
4319 {
4320 }
4321
4322 friend class VmaList<T, AllocatorT>;
4323 };
4324
4325 class const_iterator
4326 {
4327 public:
const_iterator()4328 const_iterator() :
4329 m_pList(VMA_NULL),
4330 m_pItem(VMA_NULL)
4331 {
4332 }
4333
const_iterator(const iterator & src)4334 const_iterator(const iterator& src) :
4335 m_pList(src.m_pList),
4336 m_pItem(src.m_pItem)
4337 {
4338 }
4339
4340 const T& operator*() const
4341 {
4342 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4343 return m_pItem->Value;
4344 }
4345 const T* operator->() const
4346 {
4347 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4348 return &m_pItem->Value;
4349 }
4350
4351 const_iterator& operator++()
4352 {
4353 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4354 m_pItem = m_pItem->pNext;
4355 return *this;
4356 }
4357 const_iterator& operator--()
4358 {
4359 if(m_pItem != VMA_NULL)
4360 {
4361 m_pItem = m_pItem->pPrev;
4362 }
4363 else
4364 {
4365 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4366 m_pItem = m_pList->Back();
4367 }
4368 return *this;
4369 }
4370
4371 const_iterator operator++(int)
4372 {
4373 const_iterator result = *this;
4374 ++*this;
4375 return result;
4376 }
4377 const_iterator operator--(int)
4378 {
4379 const_iterator result = *this;
4380 --*this;
4381 return result;
4382 }
4383
4384 bool operator==(const const_iterator& rhs) const
4385 {
4386 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4387 return m_pItem == rhs.m_pItem;
4388 }
4389 bool operator!=(const const_iterator& rhs) const
4390 {
4391 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4392 return m_pItem != rhs.m_pItem;
4393 }
4394
4395 private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4396 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4397 m_pList(pList),
4398 m_pItem(pItem)
4399 {
4400 }
4401
4402 const VmaRawList<T>* m_pList;
4403 const VmaListItem<T>* m_pItem;
4404
4405 friend class VmaList<T, AllocatorT>;
4406 };
4407
VmaList(const AllocatorT & allocator)4408 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4409
empty()4410 bool empty() const { return m_RawList.IsEmpty(); }
size()4411 size_t size() const { return m_RawList.GetCount(); }
4412
begin()4413 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4414 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4415
cbegin()4416 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4417 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4418
begin()4419 const_iterator begin() const { return cbegin(); }
end()4420 const_iterator end() const { return cend(); }
4421
clear()4422 void clear() { m_RawList.Clear(); }
push_back(const T & value)4423 void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)4424 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)4425 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4426
4427 private:
4428 VmaRawList<T> m_RawList;
4429 };
4430
4431 #endif // #if VMA_USE_STL_LIST
4432
4433 ////////////////////////////////////////////////////////////////////////////////
4434 // class VmaIntrusiveLinkedList
4435
4436 /*
4437 Expected interface of ItemTypeTraits:
4438 struct MyItemTypeTraits
4439 {
4440 typedef MyItem ItemType;
4441 static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
4442 static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
4443 static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
4444 static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
4445 };
4446 */
4447 template<typename ItemTypeTraits>
4448 class VmaIntrusiveLinkedList
4449 {
4450 public:
4451 typedef typename ItemTypeTraits::ItemType ItemType;
GetPrev(const ItemType * item)4452 static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
GetNext(const ItemType * item)4453 static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
4454 // Movable, not copyable.
4455 VmaIntrusiveLinkedList() = default;
4456 VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits> && src)4457 VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) :
4458 m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
4459 {
4460 src.m_Front = src.m_Back = VMA_NULL;
4461 src.m_Count = 0;
4462 }
~VmaIntrusiveLinkedList()4463 ~VmaIntrusiveLinkedList()
4464 {
4465 VMA_HEAVY_ASSERT(IsEmpty());
4466 }
4467 VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
4468 VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src)
4469 {
4470 if(&src != this)
4471 {
4472 VMA_HEAVY_ASSERT(IsEmpty());
4473 m_Front = src.m_Front;
4474 m_Back = src.m_Back;
4475 m_Count = src.m_Count;
4476 src.m_Front = src.m_Back = VMA_NULL;
4477 src.m_Count = 0;
4478 }
4479 return *this;
4480 }
RemoveAll()4481 void RemoveAll()
4482 {
4483 if(!IsEmpty())
4484 {
4485 ItemType* item = m_Back;
4486 while(item != VMA_NULL)
4487 {
4488 ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
4489 ItemTypeTraits::AccessPrev(item) = VMA_NULL;
4490 ItemTypeTraits::AccessNext(item) = VMA_NULL;
4491 item = prevItem;
4492 }
4493 m_Front = VMA_NULL;
4494 m_Back = VMA_NULL;
4495 m_Count = 0;
4496 }
4497 }
GetCount()4498 size_t GetCount() const { return m_Count; }
IsEmpty()4499 bool IsEmpty() const { return m_Count == 0; }
Front()4500 ItemType* Front() { return m_Front; }
Front()4501 const ItemType* Front() const { return m_Front; }
Back()4502 ItemType* Back() { return m_Back; }
Back()4503 const ItemType* Back() const { return m_Back; }
PushBack(ItemType * item)4504 void PushBack(ItemType* item)
4505 {
4506 VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
4507 if(IsEmpty())
4508 {
4509 m_Front = item;
4510 m_Back = item;
4511 m_Count = 1;
4512 }
4513 else
4514 {
4515 ItemTypeTraits::AccessPrev(item) = m_Back;
4516 ItemTypeTraits::AccessNext(m_Back) = item;
4517 m_Back = item;
4518 ++m_Count;
4519 }
4520 }
PushFront(ItemType * item)4521 void PushFront(ItemType* item)
4522 {
4523 VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
4524 if(IsEmpty())
4525 {
4526 m_Front = item;
4527 m_Back = item;
4528 m_Count = 1;
4529 }
4530 else
4531 {
4532 ItemTypeTraits::AccessNext(item) = m_Front;
4533 ItemTypeTraits::AccessPrev(m_Front) = item;
4534 m_Front = item;
4535 ++m_Count;
4536 }
4537 }
PopBack()4538 ItemType* PopBack()
4539 {
4540 VMA_HEAVY_ASSERT(m_Count > 0);
4541 ItemType* const backItem = m_Back;
4542 ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
4543 if(prevItem != VMA_NULL)
4544 {
4545 ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
4546 }
4547 m_Back = prevItem;
4548 --m_Count;
4549 ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
4550 ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
4551 return backItem;
4552 }
PopFront()4553 ItemType* PopFront()
4554 {
4555 VMA_HEAVY_ASSERT(m_Count > 0);
4556 ItemType* const frontItem = m_Front;
4557 ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
4558 if(nextItem != VMA_NULL)
4559 {
4560 ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
4561 }
4562 m_Front = nextItem;
4563 --m_Count;
4564 ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
4565 ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
4566 return frontItem;
4567 }
4568
4569 // MyItem can be null - it means PushBack.
InsertBefore(ItemType * existingItem,ItemType * newItem)4570 void InsertBefore(ItemType* existingItem, ItemType* newItem)
4571 {
4572 VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
4573 if(existingItem != VMA_NULL)
4574 {
4575 ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
4576 ItemTypeTraits::AccessPrev(newItem) = prevItem;
4577 ItemTypeTraits::AccessNext(newItem) = existingItem;
4578 ItemTypeTraits::AccessPrev(existingItem) = newItem;
4579 if(prevItem != VMA_NULL)
4580 {
4581 ItemTypeTraits::AccessNext(prevItem) = newItem;
4582 }
4583 else
4584 {
4585 VMA_HEAVY_ASSERT(m_Front == existingItem);
4586 m_Front = newItem;
4587 }
4588 ++m_Count;
4589 }
4590 else
4591 PushBack(newItem);
4592 }
4593 // MyItem can be null - it means PushFront.
InsertAfter(ItemType * existingItem,ItemType * newItem)4594 void InsertAfter(ItemType* existingItem, ItemType* newItem)
4595 {
4596 VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
4597 if(existingItem != VMA_NULL)
4598 {
4599 ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
4600 ItemTypeTraits::AccessNext(newItem) = nextItem;
4601 ItemTypeTraits::AccessPrev(newItem) = existingItem;
4602 ItemTypeTraits::AccessNext(existingItem) = newItem;
4603 if(nextItem != VMA_NULL)
4604 {
4605 ItemTypeTraits::AccessPrev(nextItem) = newItem;
4606 }
4607 else
4608 {
4609 VMA_HEAVY_ASSERT(m_Back == existingItem);
4610 m_Back = newItem;
4611 }
4612 ++m_Count;
4613 }
4614 else
4615 return PushFront(newItem);
4616 }
Remove(ItemType * item)4617 void Remove(ItemType* item)
4618 {
4619 VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
4620 if(ItemTypeTraits::GetPrev(item) != VMA_NULL)
4621 {
4622 ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
4623 }
4624 else
4625 {
4626 VMA_HEAVY_ASSERT(m_Front == item);
4627 m_Front = ItemTypeTraits::GetNext(item);
4628 }
4629
4630 if(ItemTypeTraits::GetNext(item) != VMA_NULL)
4631 {
4632 ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
4633 }
4634 else
4635 {
4636 VMA_HEAVY_ASSERT(m_Back == item);
4637 m_Back = ItemTypeTraits::GetPrev(item);
4638 }
4639 ItemTypeTraits::AccessPrev(item) = VMA_NULL;
4640 ItemTypeTraits::AccessNext(item) = VMA_NULL;
4641 --m_Count;
4642 }
4643 private:
4644 ItemType* m_Front = VMA_NULL;
4645 ItemType* m_Back = VMA_NULL;
4646 size_t m_Count = 0;
4647 };
4648
4649 ////////////////////////////////////////////////////////////////////////////////
4650 // class VmaMap
4651
4652 // Unused in this version.
4653 #if 0
4654
4655 #if VMA_USE_STL_UNORDERED_MAP
4656
4657 #define VmaPair std::pair
4658
4659 #define VMA_MAP_TYPE(KeyT, ValueT) \
4660 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4661
4662 #else // #if VMA_USE_STL_UNORDERED_MAP
4663
4664 template<typename T1, typename T2>
4665 struct VmaPair
4666 {
4667 T1 first;
4668 T2 second;
4669
4670 VmaPair() : first(), second() { }
4671 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4672 };
4673
4674 /* Class compatible with subset of interface of std::unordered_map.
4675 KeyT, ValueT must be POD because they will be stored in VmaVector.
4676 */
4677 template<typename KeyT, typename ValueT>
4678 class VmaMap
4679 {
4680 public:
4681 typedef VmaPair<KeyT, ValueT> PairType;
4682 typedef PairType* iterator;
4683
4684 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4685
4686 iterator begin() { return m_Vector.begin(); }
4687 iterator end() { return m_Vector.end(); }
4688
4689 void insert(const PairType& pair);
4690 iterator find(const KeyT& key);
4691 void erase(iterator it);
4692
4693 private:
4694 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4695 };
4696
4697 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4698
4699 template<typename FirstT, typename SecondT>
4700 struct VmaPairFirstLess
4701 {
4702 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4703 {
4704 return lhs.first < rhs.first;
4705 }
4706 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4707 {
4708 return lhs.first < rhsFirst;
4709 }
4710 };
4711
4712 template<typename KeyT, typename ValueT>
4713 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4714 {
4715 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4716 m_Vector.data(),
4717 m_Vector.data() + m_Vector.size(),
4718 pair,
4719 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4720 VmaVectorInsert(m_Vector, indexToInsert, pair);
4721 }
4722
4723 template<typename KeyT, typename ValueT>
4724 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4725 {
4726 PairType* it = VmaBinaryFindFirstNotLess(
4727 m_Vector.data(),
4728 m_Vector.data() + m_Vector.size(),
4729 key,
4730 VmaPairFirstLess<KeyT, ValueT>());
4731 if((it != m_Vector.end()) && (it->first == key))
4732 {
4733 return it;
4734 }
4735 else
4736 {
4737 return m_Vector.end();
4738 }
4739 }
4740
4741 template<typename KeyT, typename ValueT>
4742 void VmaMap<KeyT, ValueT>::erase(iterator it)
4743 {
4744 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4745 }
4746
4747 #endif // #if VMA_USE_STL_UNORDERED_MAP
4748
4749 #endif // #if 0
4750
4751 ////////////////////////////////////////////////////////////////////////////////
4752
4753 class VmaDeviceMemoryBlock;
4754
4755 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4756
4757 struct VmaAllocation_T
4758 {
4759 private:
4760 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4761
4762 enum FLAGS
4763 {
4764 FLAG_USER_DATA_STRING = 0x01,
4765 };
4766
4767 public:
4768 enum ALLOCATION_TYPE
4769 {
4770 ALLOCATION_TYPE_NONE,
4771 ALLOCATION_TYPE_BLOCK,
4772 ALLOCATION_TYPE_DEDICATED,
4773 };
4774
4775 /*
4776 This struct is allocated using VmaPoolAllocator.
4777 */
4778
VmaAllocation_TVmaAllocation_T4779 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4780 m_Alignment{1},
4781 m_Size{0},
4782 m_pUserData{VMA_NULL},
4783 m_LastUseFrameIndex{currentFrameIndex},
4784 m_MemoryTypeIndex{0},
4785 m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
4786 m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
4787 m_MapCount{0},
4788 m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
4789 {
4790 #if VMA_STATS_STRING_ENABLED
4791 m_CreationFrameIndex = currentFrameIndex;
4792 m_BufferImageUsage = 0;
4793 #endif
4794 }
4795
~VmaAllocation_TVmaAllocation_T4796 ~VmaAllocation_T()
4797 {
4798 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4799
4800 // Check if owned string was freed.
4801 VMA_ASSERT(m_pUserData == VMA_NULL);
4802 }
4803
InitBlockAllocationVmaAllocation_T4804 void InitBlockAllocation(
4805 VmaDeviceMemoryBlock* block,
4806 VkDeviceSize offset,
4807 VkDeviceSize alignment,
4808 VkDeviceSize size,
4809 uint32_t memoryTypeIndex,
4810 VmaSuballocationType suballocationType,
4811 bool mapped,
4812 bool canBecomeLost)
4813 {
4814 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4815 VMA_ASSERT(block != VMA_NULL);
4816 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4817 m_Alignment = alignment;
4818 m_Size = size;
4819 m_MemoryTypeIndex = memoryTypeIndex;
4820 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4821 m_SuballocationType = (uint8_t)suballocationType;
4822 m_BlockAllocation.m_Block = block;
4823 m_BlockAllocation.m_Offset = offset;
4824 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4825 }
4826
InitLostVmaAllocation_T4827 void InitLost()
4828 {
4829 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4830 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4831 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4832 m_MemoryTypeIndex = 0;
4833 m_BlockAllocation.m_Block = VMA_NULL;
4834 m_BlockAllocation.m_Offset = 0;
4835 m_BlockAllocation.m_CanBecomeLost = true;
4836 }
4837
4838 void ChangeBlockAllocation(
4839 VmaAllocator hAllocator,
4840 VmaDeviceMemoryBlock* block,
4841 VkDeviceSize offset);
4842
4843 void ChangeOffset(VkDeviceSize newOffset);
4844
4845 // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T4846 void InitDedicatedAllocation(
4847 uint32_t memoryTypeIndex,
4848 VkDeviceMemory hMemory,
4849 VmaSuballocationType suballocationType,
4850 void* pMappedData,
4851 VkDeviceSize size)
4852 {
4853 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4854 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4855 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4856 m_Alignment = 0;
4857 m_Size = size;
4858 m_MemoryTypeIndex = memoryTypeIndex;
4859 m_SuballocationType = (uint8_t)suballocationType;
4860 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4861 m_DedicatedAllocation.m_hMemory = hMemory;
4862 m_DedicatedAllocation.m_pMappedData = pMappedData;
4863 m_DedicatedAllocation.m_Prev = VMA_NULL;
4864 m_DedicatedAllocation.m_Next = VMA_NULL;
4865 }
4866
GetTypeVmaAllocation_T4867 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T4868 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T4869 VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T4870 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T4871 void* GetUserData() const { return m_pUserData; }
4872 void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T4873 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
4874
GetBlockVmaAllocation_T4875 VmaDeviceMemoryBlock* GetBlock() const
4876 {
4877 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4878 return m_BlockAllocation.m_Block;
4879 }
4880 VkDeviceSize GetOffset() const;
4881 VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T4882 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T4883 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
4884 void* GetMappedData() const;
4885 bool CanBecomeLost() const;
4886
GetLastUseFrameIndexVmaAllocation_T4887 uint32_t GetLastUseFrameIndex() const
4888 {
4889 return m_LastUseFrameIndex.load();
4890 }
CompareExchangeLastUseFrameIndexVmaAllocation_T4891 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
4892 {
4893 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
4894 }
4895 /*
4896 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
4897 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
4898 - Else, returns false.
4899
4900 If hAllocation is already lost, assert - you should not call it then.
4901 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
4902 */
4903 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
4904
DedicatedAllocCalcStatsInfoVmaAllocation_T4905 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
4906 {
4907 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
4908 outInfo.blockCount = 1;
4909 outInfo.allocationCount = 1;
4910 outInfo.unusedRangeCount = 0;
4911 outInfo.usedBytes = m_Size;
4912 outInfo.unusedBytes = 0;
4913 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
4914 outInfo.unusedRangeSizeMin = UINT64_MAX;
4915 outInfo.unusedRangeSizeMax = 0;
4916 }
4917
4918 void BlockAllocMap();
4919 void BlockAllocUnmap();
4920 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
4921 void DedicatedAllocUnmap(VmaAllocator hAllocator);
4922
4923 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T4924 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T4925 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
4926
InitBufferImageUsageVmaAllocation_T4927 void InitBufferImageUsage(uint32_t bufferImageUsage)
4928 {
4929 VMA_ASSERT(m_BufferImageUsage == 0);
4930 m_BufferImageUsage = bufferImageUsage;
4931 }
4932
4933 void PrintParameters(class VmaJsonWriter& json) const;
4934 #endif
4935
4936 private:
4937 VkDeviceSize m_Alignment;
4938 VkDeviceSize m_Size;
4939 void* m_pUserData;
4940 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
4941 uint32_t m_MemoryTypeIndex;
4942 uint8_t m_Type; // ALLOCATION_TYPE
4943 uint8_t m_SuballocationType; // VmaSuballocationType
4944 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
4945 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
4946 uint8_t m_MapCount;
4947 uint8_t m_Flags; // enum FLAGS
4948
4949 // Allocation out of VmaDeviceMemoryBlock.
4950 struct BlockAllocation
4951 {
4952 VmaDeviceMemoryBlock* m_Block;
4953 VkDeviceSize m_Offset;
4954 bool m_CanBecomeLost;
4955 };
4956
4957 // Allocation for an object that has its own private VkDeviceMemory.
4958 struct DedicatedAllocation
4959 {
4960 VkDeviceMemory m_hMemory;
4961 void* m_pMappedData; // Not null means memory is mapped.
4962 VmaAllocation_T* m_Prev;
4963 VmaAllocation_T* m_Next;
4964 };
4965
4966 union
4967 {
4968 // Allocation out of VmaDeviceMemoryBlock.
4969 BlockAllocation m_BlockAllocation;
4970 // Allocation for an object that has its own private VkDeviceMemory.
4971 DedicatedAllocation m_DedicatedAllocation;
4972 };
4973
4974 #if VMA_STATS_STRING_ENABLED
4975 uint32_t m_CreationFrameIndex;
4976 uint32_t m_BufferImageUsage; // 0 if unknown.
4977 #endif
4978
4979 void FreeUserDataString(VmaAllocator hAllocator);
4980
4981 friend struct VmaDedicatedAllocationListItemTraits;
4982 };
4983
4984 struct VmaDedicatedAllocationListItemTraits
4985 {
4986 typedef VmaAllocation_T ItemType;
GetPrevVmaDedicatedAllocationListItemTraits4987 static ItemType* GetPrev(const ItemType* item)
4988 {
4989 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
4990 return item->m_DedicatedAllocation.m_Prev;
4991 }
GetNextVmaDedicatedAllocationListItemTraits4992 static ItemType* GetNext(const ItemType* item)
4993 {
4994 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
4995 return item->m_DedicatedAllocation.m_Next;
4996 }
AccessPrevVmaDedicatedAllocationListItemTraits4997 static ItemType*& AccessPrev(ItemType* item)
4998 {
4999 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
5000 return item->m_DedicatedAllocation.m_Prev;
5001 }
AccessNextVmaDedicatedAllocationListItemTraits5002 static ItemType*& AccessNext(ItemType* item){
5003 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
5004 return item->m_DedicatedAllocation.m_Next;
5005 }
5006 };
5007
5008 /*
5009 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5010 allocated memory block or free.
5011 */
5012 struct VmaSuballocation
5013 {
5014 VkDeviceSize offset;
5015 VkDeviceSize size;
5016 void* userData;
5017 VmaSuballocationType type;
5018 };
5019
5020 // Comparator for offsets.
5021 struct VmaSuballocationOffsetLess
5022 {
operatorVmaSuballocationOffsetLess5023 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5024 {
5025 return lhs.offset < rhs.offset;
5026 }
5027 };
5028 struct VmaSuballocationOffsetGreater
5029 {
operatorVmaSuballocationOffsetGreater5030 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5031 {
5032 return lhs.offset > rhs.offset;
5033 }
5034 };
5035
5036 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5037
5038 // Cost of one additional allocation lost, as equivalent in bytes.
5039 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5040
5041 enum class VmaAllocationRequestType
5042 {
5043 Normal,
5044 // Used by "Linear" algorithm.
5045 UpperAddress,
5046 EndOf1st,
5047 EndOf2nd,
5048 };
5049
5050 /*
5051 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5052
5053 If canMakeOtherLost was false:
5054 - item points to a FREE suballocation.
5055 - itemsToMakeLostCount is 0.
5056
5057 If canMakeOtherLost was true:
5058 - item points to first of sequence of suballocations, which are either FREE,
5059 or point to VmaAllocations that can become lost.
5060 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5061 the requested allocation to succeed.
5062 */
5063 struct VmaAllocationRequest
5064 {
5065 VkDeviceSize offset;
5066 VkDeviceSize size;
5067 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5068 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5069 VmaSuballocationList::iterator item;
5070 size_t itemsToMakeLostCount;
5071 void* customData;
5072 VmaAllocationRequestType type;
5073
CalcCostVmaAllocationRequest5074 VkDeviceSize CalcCost() const
5075 {
5076 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5077 }
5078 };
5079
5080 /*
5081 Data structure used for bookkeeping of allocations and unused ranges of memory
5082 in a single VkDeviceMemory block.
5083 */
5084 class VmaBlockMetadata
5085 {
5086 public:
5087 // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.
5088 VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
~VmaBlockMetadata()5089 virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)5090 virtual void Init(VkDeviceSize size) { m_Size = size; }
5091
5092 // Validates all data structures inside this object. If not valid, returns false.
5093 virtual bool Validate() const = 0;
IsVirtual()5094 bool IsVirtual() const { return m_IsVirtual; }
GetSize()5095 VkDeviceSize GetSize() const { return m_Size; }
5096 virtual size_t GetAllocationCount() const = 0;
5097 virtual VkDeviceSize GetSumFreeSize() const = 0;
5098 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5099 // Returns true if this block is empty - contains only single free suballocation.
5100 virtual bool IsEmpty() const = 0;
5101 virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) = 0;
5102
5103 // Must set blockCount to 1.
5104 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5105 // Shouldn't modify blockCount.
5106 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5107
5108 #if VMA_STATS_STRING_ENABLED
5109 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5110 #endif
5111
5112 // Tries to find a place for suballocation with given parameters inside this block.
5113 // If succeeded, fills pAllocationRequest and returns true.
5114 // If failed, returns false.
5115 virtual bool CreateAllocationRequest(
5116 uint32_t currentFrameIndex,
5117 uint32_t frameInUseCount,
5118 VkDeviceSize bufferImageGranularity,
5119 VkDeviceSize allocSize,
5120 VkDeviceSize allocAlignment,
5121 bool upperAddress,
5122 VmaSuballocationType allocType,
5123 bool canMakeOtherLost,
5124 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5125 uint32_t strategy,
5126 VmaAllocationRequest* pAllocationRequest) = 0;
5127
5128 virtual bool MakeRequestedAllocationsLost(
5129 uint32_t currentFrameIndex,
5130 uint32_t frameInUseCount,
5131 VmaAllocationRequest* pAllocationRequest) = 0;
5132
5133 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5134
5135 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5136
5137 // Makes actual allocation based on request. Request must already be checked and valid.
5138 virtual void Alloc(
5139 const VmaAllocationRequest& request,
5140 VmaSuballocationType type,
5141 void* userData) = 0;
5142
5143 // Frees suballocation assigned to given memory region.
5144 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5145
5146 // Frees all allocations.
5147 // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!
5148 virtual void Clear() = 0;
5149
5150 virtual void SetAllocationUserData(VkDeviceSize offset, void* userData) = 0;
5151
5152 protected:
GetAllocationCallbacks()5153 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
GetDebugMargin()5154 VkDeviceSize GetDebugMargin() const
5155 {
5156 return IsVirtual() ? 0 : VMA_DEBUG_MARGIN;
5157 }
5158
5159 #if VMA_STATS_STRING_ENABLED
5160 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5161 VkDeviceSize unusedBytes,
5162 size_t allocationCount,
5163 size_t unusedRangeCount) const;
5164 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5165 VkDeviceSize offset, VkDeviceSize size, void* userData) const;
5166 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5167 VkDeviceSize offset,
5168 VkDeviceSize size) const;
5169 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5170 #endif
5171
5172 private:
5173 VkDeviceSize m_Size;
5174 const VkAllocationCallbacks* m_pAllocationCallbacks;
5175 const bool m_IsVirtual;
5176 };
5177
5178 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5179 VMA_ASSERT(0 && "Validation failed: " #cond); \
5180 return false; \
5181 } } while(false)
5182
5183 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5184 {
5185 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5186 public:
5187 VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5188 virtual ~VmaBlockMetadata_Generic();
5189 virtual void Init(VkDeviceSize size);
5190
5191 virtual bool Validate() const;
GetAllocationCount()5192 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()5193 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5194 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5195 virtual bool IsEmpty() const;
5196
5197 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5198 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5199
5200 #if VMA_STATS_STRING_ENABLED
5201 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5202 #endif
5203
5204 virtual bool CreateAllocationRequest(
5205 uint32_t currentFrameIndex,
5206 uint32_t frameInUseCount,
5207 VkDeviceSize bufferImageGranularity,
5208 VkDeviceSize allocSize,
5209 VkDeviceSize allocAlignment,
5210 bool upperAddress,
5211 VmaSuballocationType allocType,
5212 bool canMakeOtherLost,
5213 uint32_t strategy,
5214 VmaAllocationRequest* pAllocationRequest);
5215
5216 virtual bool MakeRequestedAllocationsLost(
5217 uint32_t currentFrameIndex,
5218 uint32_t frameInUseCount,
5219 VmaAllocationRequest* pAllocationRequest);
5220
5221 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5222
5223 virtual VkResult CheckCorruption(const void* pBlockData);
5224
5225 virtual void Alloc(
5226 const VmaAllocationRequest& request,
5227 VmaSuballocationType type,
5228 void* userData);
5229
5230 virtual void FreeAtOffset(VkDeviceSize offset);
5231 virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5232 virtual void Clear();
5233 virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5234
5235 ////////////////////////////////////////////////////////////////////////////////
5236 // For defragmentation
5237
5238 bool IsBufferImageGranularityConflictPossible(
5239 VkDeviceSize bufferImageGranularity,
5240 VmaSuballocationType& inOutPrevSuballocType) const;
5241
5242 private:
5243 friend class VmaDefragmentationAlgorithm_Generic;
5244 friend class VmaDefragmentationAlgorithm_Fast;
5245
5246 uint32_t m_FreeCount;
5247 VkDeviceSize m_SumFreeSize;
5248 VmaSuballocationList m_Suballocations;
5249 // Suballocations that are free. Sorted by size, ascending.
5250 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5251
AlignAllocationSize(VkDeviceSize size)5252 VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
5253 {
5254 return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16);
5255 }
5256
5257 bool ValidateFreeSuballocationList() const;
5258
5259 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5260 // If yes, fills pOffset and returns true. If no, returns false.
5261 bool CheckAllocation(
5262 uint32_t currentFrameIndex,
5263 uint32_t frameInUseCount,
5264 VkDeviceSize bufferImageGranularity,
5265 VkDeviceSize allocSize,
5266 VkDeviceSize allocAlignment,
5267 VmaSuballocationType allocType,
5268 VmaSuballocationList::const_iterator suballocItem,
5269 bool canMakeOtherLost,
5270 VkDeviceSize* pOffset,
5271 size_t* itemsToMakeLostCount,
5272 VkDeviceSize* pSumFreeSize,
5273 VkDeviceSize* pSumItemSize) const;
5274 // Given free suballocation, it merges it with following one, which must also be free.
5275 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5276 // Releases given suballocation, making it free.
5277 // Merges it with adjacent free suballocations if applicable.
5278 // Returns iterator to new free suballocation at this place.
5279 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5280 // Given free suballocation, it inserts it into sorted list of
5281 // m_FreeSuballocationsBySize if it is suitable.
5282 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5283 // Given free suballocation, it removes it from sorted list of
5284 // m_FreeSuballocationsBySize if it is suitable.
5285 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5286 };
5287
5288 /*
5289 Allocations and their references in internal data structure look like this:
5290
5291 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5292
5293 0 +-------+
5294 | |
5295 | |
5296 | |
5297 +-------+
5298 | Alloc | 1st[m_1stNullItemsBeginCount]
5299 +-------+
5300 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5301 +-------+
5302 | ... |
5303 +-------+
5304 | Alloc | 1st[1st.size() - 1]
5305 +-------+
5306 | |
5307 | |
5308 | |
5309 GetSize() +-------+
5310
5311 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5312
5313 0 +-------+
5314 | Alloc | 2nd[0]
5315 +-------+
5316 | Alloc | 2nd[1]
5317 +-------+
5318 | ... |
5319 +-------+
5320 | Alloc | 2nd[2nd.size() - 1]
5321 +-------+
5322 | |
5323 | |
5324 | |
5325 +-------+
5326 | Alloc | 1st[m_1stNullItemsBeginCount]
5327 +-------+
5328 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5329 +-------+
5330 | ... |
5331 +-------+
5332 | Alloc | 1st[1st.size() - 1]
5333 +-------+
5334 | |
5335 GetSize() +-------+
5336
5337 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5338
5339 0 +-------+
5340 | |
5341 | |
5342 | |
5343 +-------+
5344 | Alloc | 1st[m_1stNullItemsBeginCount]
5345 +-------+
5346 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5347 +-------+
5348 | ... |
5349 +-------+
5350 | Alloc | 1st[1st.size() - 1]
5351 +-------+
5352 | |
5353 | |
5354 | |
5355 +-------+
5356 | Alloc | 2nd[2nd.size() - 1]
5357 +-------+
5358 | ... |
5359 +-------+
5360 | Alloc | 2nd[1]
5361 +-------+
5362 | Alloc | 2nd[0]
5363 GetSize() +-------+
5364
5365 */
5366 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5367 {
5368 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5369 public:
5370 VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5371 virtual ~VmaBlockMetadata_Linear();
5372 virtual void Init(VkDeviceSize size);
5373
5374 virtual bool Validate() const;
5375 virtual size_t GetAllocationCount() const;
GetSumFreeSize()5376 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5377 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5378 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5379
5380 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5381 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5382
5383 #if VMA_STATS_STRING_ENABLED
5384 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5385 #endif
5386
5387 virtual bool CreateAllocationRequest(
5388 uint32_t currentFrameIndex,
5389 uint32_t frameInUseCount,
5390 VkDeviceSize bufferImageGranularity,
5391 VkDeviceSize allocSize,
5392 VkDeviceSize allocAlignment,
5393 bool upperAddress,
5394 VmaSuballocationType allocType,
5395 bool canMakeOtherLost,
5396 uint32_t strategy,
5397 VmaAllocationRequest* pAllocationRequest);
5398
5399 virtual bool MakeRequestedAllocationsLost(
5400 uint32_t currentFrameIndex,
5401 uint32_t frameInUseCount,
5402 VmaAllocationRequest* pAllocationRequest);
5403
5404 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5405
5406 virtual VkResult CheckCorruption(const void* pBlockData);
5407
5408 virtual void Alloc(
5409 const VmaAllocationRequest& request,
5410 VmaSuballocationType type,
5411 void* userData);
5412
5413 virtual void FreeAtOffset(VkDeviceSize offset);
5414 virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5415 virtual void Clear();
5416 virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5417
5418 private:
5419 /*
5420 There are two suballocation vectors, used in ping-pong way.
5421 The one with index m_1stVectorIndex is called 1st.
5422 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5423 2nd can be non-empty only when 1st is not empty.
5424 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5425 */
5426 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5427
5428 enum SECOND_VECTOR_MODE
5429 {
5430 SECOND_VECTOR_EMPTY,
5431 /*
5432 Suballocations in 2nd vector are created later than the ones in 1st, but they
5433 all have smaller offset.
5434 */
5435 SECOND_VECTOR_RING_BUFFER,
5436 /*
5437 Suballocations in 2nd vector are upper side of double stack.
5438 They all have offsets higher than those in 1st vector.
5439 Top of this stack means smaller offsets, but higher indices in this vector.
5440 */
5441 SECOND_VECTOR_DOUBLE_STACK,
5442 };
5443
5444 VkDeviceSize m_SumFreeSize;
5445 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5446 uint32_t m_1stVectorIndex;
5447 SECOND_VECTOR_MODE m_2ndVectorMode;
5448
AccessSuballocations1st()5449 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5450 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()5451 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5452 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5453 VmaSuballocation& FindSuballocation(VkDeviceSize offset);
5454
5455 // Number of items in 1st vector with hAllocation = null at the beginning.
5456 size_t m_1stNullItemsBeginCount;
5457 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5458 size_t m_1stNullItemsMiddleCount;
5459 // Number of items in 2nd vector with hAllocation = null.
5460 size_t m_2ndNullItemsCount;
5461
5462 bool ShouldCompact1st() const;
5463 void CleanupAfterFree();
5464
5465 bool CreateAllocationRequest_LowerAddress(
5466 uint32_t currentFrameIndex,
5467 uint32_t frameInUseCount,
5468 VkDeviceSize bufferImageGranularity,
5469 VkDeviceSize allocSize,
5470 VkDeviceSize allocAlignment,
5471 VmaSuballocationType allocType,
5472 bool canMakeOtherLost,
5473 uint32_t strategy,
5474 VmaAllocationRequest* pAllocationRequest);
5475 bool CreateAllocationRequest_UpperAddress(
5476 uint32_t currentFrameIndex,
5477 uint32_t frameInUseCount,
5478 VkDeviceSize bufferImageGranularity,
5479 VkDeviceSize allocSize,
5480 VkDeviceSize allocAlignment,
5481 VmaSuballocationType allocType,
5482 bool canMakeOtherLost,
5483 uint32_t strategy,
5484 VmaAllocationRequest* pAllocationRequest);
5485 };
5486
5487 /*
5488 - GetSize() is the original size of allocated memory block.
5489 - m_UsableSize is this size aligned down to a power of two.
5490 All allocations and calculations happen relative to m_UsableSize.
5491 - GetUnusableSize() is the difference between them.
5492 It is reported as separate, unused range, not available for allocations.
5493
5494 Node at level 0 has size = m_UsableSize.
5495 Each next level contains nodes with size 2 times smaller than current level.
5496 m_LevelCount is the maximum number of levels to use in the current object.
5497 */
5498 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5499 {
5500 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5501 public:
5502 VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5503 virtual ~VmaBlockMetadata_Buddy();
5504 virtual void Init(VkDeviceSize size);
5505
5506 virtual bool Validate() const;
GetAllocationCount()5507 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()5508 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5509 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5510 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5511
5512 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5513 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5514
5515 #if VMA_STATS_STRING_ENABLED
5516 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5517 #endif
5518
5519 virtual bool CreateAllocationRequest(
5520 uint32_t currentFrameIndex,
5521 uint32_t frameInUseCount,
5522 VkDeviceSize bufferImageGranularity,
5523 VkDeviceSize allocSize,
5524 VkDeviceSize allocAlignment,
5525 bool upperAddress,
5526 VmaSuballocationType allocType,
5527 bool canMakeOtherLost,
5528 uint32_t strategy,
5529 VmaAllocationRequest* pAllocationRequest);
5530
5531 virtual bool MakeRequestedAllocationsLost(
5532 uint32_t currentFrameIndex,
5533 uint32_t frameInUseCount,
5534 VmaAllocationRequest* pAllocationRequest);
5535
5536 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5537
CheckCorruption(const void * pBlockData)5538 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5539
5540 virtual void Alloc(
5541 const VmaAllocationRequest& request,
5542 VmaSuballocationType type,
5543 void* userData);
5544
5545 virtual void FreeAtOffset(VkDeviceSize offset);
5546 virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5547 virtual void Clear();
5548 virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5549
5550 private:
5551 static const size_t MAX_LEVELS = 48;
5552
5553 struct ValidationContext
5554 {
5555 size_t calculatedAllocationCount = 0;
5556 size_t calculatedFreeCount = 0;
5557 VkDeviceSize calculatedSumFreeSize = 0;
5558 };
5559
5560 struct Node
5561 {
5562 VkDeviceSize offset;
5563 enum TYPE
5564 {
5565 TYPE_FREE,
5566 TYPE_ALLOCATION,
5567 TYPE_SPLIT,
5568 TYPE_COUNT
5569 } type;
5570 Node* parent;
5571 Node* buddy;
5572
5573 union
5574 {
5575 struct
5576 {
5577 Node* prev;
5578 Node* next;
5579 } free;
5580 struct
5581 {
5582 void* userData;
5583 } allocation;
5584 struct
5585 {
5586 Node* leftChild;
5587 } split;
5588 };
5589 };
5590
5591 // Size of the memory block aligned down to a power of two.
5592 VkDeviceSize m_UsableSize;
5593 uint32_t m_LevelCount;
5594
5595 VmaPoolAllocator<Node> m_NodeAllocator;
5596
5597 Node* m_Root;
5598 struct {
5599 Node* front;
5600 Node* back;
5601 } m_FreeList[MAX_LEVELS];
5602 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5603 size_t m_AllocationCount;
5604 // Number of nodes in the tree with type == TYPE_FREE.
5605 size_t m_FreeCount;
5606 // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes.
5607 // Doesn't include unusable size.
5608 VkDeviceSize m_SumFreeSize;
5609
AlignAllocationSize(VkDeviceSize size)5610 VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
5611 {
5612 if(!IsVirtual())
5613 {
5614 size = VmaAlignUp(size, (VkDeviceSize)16);
5615 }
5616 return VmaNextPow2(size);
5617 }
GetUnusableSize()5618 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5619 Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel);
5620 void DeleteNodeChildren(Node* node);
5621 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5622 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)5623 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5624 void CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5625 // Adds node to the front of FreeList at given level.
5626 // node->type must be FREE.
5627 // node->free.prev, next can be undefined.
5628 void AddToFreeListFront(uint32_t level, Node* node);
5629 // Removes node from FreeList at given level.
5630 // node->type must be FREE.
5631 // node->free.prev, next stay untouched.
5632 void RemoveFromFreeList(uint32_t level, Node* node);
5633
5634 #if VMA_STATS_STRING_ENABLED
5635 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5636 #endif
5637 };
5638
5639 /*
5640 Represents a single block of device memory (`VkDeviceMemory`) with all the
5641 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5642
5643 Thread-safety: This class must be externally synchronized.
5644 */
5645 class VmaDeviceMemoryBlock
5646 {
5647 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5648 public:
5649 VmaBlockMetadata* m_pMetadata;
5650
5651 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5652
~VmaDeviceMemoryBlock()5653 ~VmaDeviceMemoryBlock()
5654 {
5655 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5656 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5657 }
5658
5659 // Always call after construction.
5660 void Init(
5661 VmaAllocator hAllocator,
5662 VmaPool hParentPool,
5663 uint32_t newMemoryTypeIndex,
5664 VkDeviceMemory newMemory,
5665 VkDeviceSize newSize,
5666 uint32_t id,
5667 uint32_t algorithm);
5668 // Always call before destruction.
5669 void Destroy(VmaAllocator allocator);
5670
GetParentPool()5671 VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()5672 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5673 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5674 uint32_t GetId() const { return m_Id; }
GetMappedData()5675 void* GetMappedData() const { return m_pMappedData; }
5676
5677 // Validates all data structures inside this object. If not valid, returns false.
5678 bool Validate() const;
5679
5680 VkResult CheckCorruption(VmaAllocator hAllocator);
5681
5682 // ppData can be null.
5683 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5684 void Unmap(VmaAllocator hAllocator, uint32_t count);
5685
5686 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5687 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5688
5689 VkResult BindBufferMemory(
5690 const VmaAllocator hAllocator,
5691 const VmaAllocation hAllocation,
5692 VkDeviceSize allocationLocalOffset,
5693 VkBuffer hBuffer,
5694 const void* pNext);
5695 VkResult BindImageMemory(
5696 const VmaAllocator hAllocator,
5697 const VmaAllocation hAllocation,
5698 VkDeviceSize allocationLocalOffset,
5699 VkImage hImage,
5700 const void* pNext);
5701
5702 private:
5703 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
5704 uint32_t m_MemoryTypeIndex;
5705 uint32_t m_Id;
5706 VkDeviceMemory m_hMemory;
5707
5708 /*
5709 Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5710 Also protects m_MapCount, m_pMappedData.
5711 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5712 */
5713 VMA_MUTEX m_Mutex;
5714 uint32_t m_MapCount;
5715 void* m_pMappedData;
5716 };
5717
5718 struct VmaDefragmentationMove
5719 {
5720 size_t srcBlockIndex;
5721 size_t dstBlockIndex;
5722 VkDeviceSize srcOffset;
5723 VkDeviceSize dstOffset;
5724 VkDeviceSize size;
5725 VmaAllocation hAllocation;
5726 VmaDeviceMemoryBlock* pSrcBlock;
5727 VmaDeviceMemoryBlock* pDstBlock;
5728 };
5729
5730 class VmaDefragmentationAlgorithm;
5731
5732 /*
5733 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5734 Vulkan memory type.
5735
5736 Synchronized internally with a mutex.
5737 */
5738 struct VmaBlockVector
5739 {
5740 VMA_CLASS_NO_COPY(VmaBlockVector)
5741 public:
5742 VmaBlockVector(
5743 VmaAllocator hAllocator,
5744 VmaPool hParentPool,
5745 uint32_t memoryTypeIndex,
5746 VkDeviceSize preferredBlockSize,
5747 size_t minBlockCount,
5748 size_t maxBlockCount,
5749 VkDeviceSize bufferImageGranularity,
5750 uint32_t frameInUseCount,
5751 bool explicitBlockSize,
5752 uint32_t algorithm,
5753 float priority,
5754 VkDeviceSize minAllocationAlignment,
5755 void* pMemoryAllocateNext);
5756 ~VmaBlockVector();
5757
5758 VkResult CreateMinBlocks();
5759
GetAllocatorVmaBlockVector5760 VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector5761 VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector5762 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector5763 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector5764 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector5765 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector5766 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector5767 uint32_t GetAlgorithm() const { return m_Algorithm; }
5768
5769 void GetPoolStats(VmaPoolStats* pStats);
5770
5771 bool IsEmpty();
5772 bool IsCorruptionDetectionEnabled() const;
5773
5774 VkResult Allocate(
5775 uint32_t currentFrameIndex,
5776 VkDeviceSize size,
5777 VkDeviceSize alignment,
5778 const VmaAllocationCreateInfo& createInfo,
5779 VmaSuballocationType suballocType,
5780 size_t allocationCount,
5781 VmaAllocation* pAllocations);
5782
5783 void Free(const VmaAllocation hAllocation);
5784
5785 // Adds statistics of this BlockVector to pStats.
5786 void AddStats(VmaStats* pStats);
5787
5788 #if VMA_STATS_STRING_ENABLED
5789 void PrintDetailedMap(class VmaJsonWriter& json);
5790 #endif
5791
5792 void MakePoolAllocationsLost(
5793 uint32_t currentFrameIndex,
5794 size_t* pLostAllocationCount);
5795 VkResult CheckCorruption();
5796
5797 // Saves results in pCtx->res.
5798 void Defragment(
5799 class VmaBlockVectorDefragmentationContext* pCtx,
5800 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
5801 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5802 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5803 VkCommandBuffer commandBuffer);
5804 void DefragmentationEnd(
5805 class VmaBlockVectorDefragmentationContext* pCtx,
5806 uint32_t flags,
5807 VmaDefragmentationStats* pStats);
5808
5809 uint32_t ProcessDefragmentations(
5810 class VmaBlockVectorDefragmentationContext *pCtx,
5811 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
5812
5813 void CommitDefragmentations(
5814 class VmaBlockVectorDefragmentationContext *pCtx,
5815 VmaDefragmentationStats* pStats);
5816
5817 ////////////////////////////////////////////////////////////////////////////////
5818 // To be used only while the m_Mutex is locked. Used during defragmentation.
5819
GetBlockCountVmaBlockVector5820 size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector5821 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5822 size_t CalcAllocationCount() const;
5823 bool IsBufferImageGranularityConflictPossible() const;
5824
5825 private:
5826 friend class VmaDefragmentationAlgorithm_Generic;
5827
5828 const VmaAllocator m_hAllocator;
5829 const VmaPool m_hParentPool;
5830 const uint32_t m_MemoryTypeIndex;
5831 const VkDeviceSize m_PreferredBlockSize;
5832 const size_t m_MinBlockCount;
5833 const size_t m_MaxBlockCount;
5834 const VkDeviceSize m_BufferImageGranularity;
5835 const uint32_t m_FrameInUseCount;
5836 const bool m_ExplicitBlockSize;
5837 const uint32_t m_Algorithm;
5838 const float m_Priority;
5839 const VkDeviceSize m_MinAllocationAlignment;
5840 void* const m_pMemoryAllocateNext;
5841 VMA_RW_MUTEX m_Mutex;
5842
5843 /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
5844 a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
5845 bool m_HasEmptyBlock;
5846 // Incrementally sorted by sumFreeSize, ascending.
5847 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5848 uint32_t m_NextBlockId;
5849
5850 VkDeviceSize CalcMaxBlockSize() const;
5851
5852 // Finds and removes given block from vector.
5853 void Remove(VmaDeviceMemoryBlock* pBlock);
5854
5855 // Performs single step in sorting m_Blocks. They may not be fully sorted
5856 // after this call.
5857 void IncrementallySortBlocks();
5858
5859 VkResult AllocatePage(
5860 uint32_t currentFrameIndex,
5861 VkDeviceSize size,
5862 VkDeviceSize alignment,
5863 const VmaAllocationCreateInfo& createInfo,
5864 VmaSuballocationType suballocType,
5865 VmaAllocation* pAllocation);
5866
5867 // To be used only without CAN_MAKE_OTHER_LOST flag.
5868 VkResult AllocateFromBlock(
5869 VmaDeviceMemoryBlock* pBlock,
5870 uint32_t currentFrameIndex,
5871 VkDeviceSize size,
5872 VkDeviceSize alignment,
5873 VmaAllocationCreateFlags allocFlags,
5874 void* pUserData,
5875 VmaSuballocationType suballocType,
5876 uint32_t strategy,
5877 VmaAllocation* pAllocation);
5878
5879 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5880
5881 // Saves result to pCtx->res.
5882 void ApplyDefragmentationMovesCpu(
5883 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5884 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5885 // Saves result to pCtx->res.
5886 void ApplyDefragmentationMovesGpu(
5887 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5888 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5889 VkCommandBuffer commandBuffer);
5890
5891 /*
5892 Used during defragmentation. pDefragmentationStats is optional. It is in/out
5893 - updated with new data.
5894 */
5895 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5896
5897 void UpdateHasEmptyBlock();
5898 };
5899
5900 struct VmaPool_T
5901 {
5902 VMA_CLASS_NO_COPY(VmaPool_T)
5903 public:
5904 VmaBlockVector m_BlockVector;
5905
5906 VmaPool_T(
5907 VmaAllocator hAllocator,
5908 const VmaPoolCreateInfo& createInfo,
5909 VkDeviceSize preferredBlockSize);
5910 ~VmaPool_T();
5911
GetIdVmaPool_T5912 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T5913 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5914
GetNameVmaPool_T5915 const char* GetName() const { return m_Name; }
5916 void SetName(const char* pName);
5917
5918 #if VMA_STATS_STRING_ENABLED
5919 //void PrintDetailedMap(class VmaStringBuilder& sb);
5920 #endif
5921
5922 private:
5923 uint32_t m_Id;
5924 char* m_Name;
5925 VmaPool_T* m_PrevPool = VMA_NULL;
5926 VmaPool_T* m_NextPool = VMA_NULL;
5927 friend struct VmaPoolListItemTraits;
5928 };
5929
5930 struct VmaPoolListItemTraits
5931 {
5932 typedef VmaPool_T ItemType;
GetPrevVmaPoolListItemTraits5933 static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
GetNextVmaPoolListItemTraits5934 static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
AccessPrevVmaPoolListItemTraits5935 static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
AccessNextVmaPoolListItemTraits5936 static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
5937 };
5938
5939 /*
5940 Performs defragmentation:
5941
5942 - Updates `pBlockVector->m_pMetadata`.
5943 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5944 - Does not move actual data, only returns requested moves as `moves`.
5945 */
5946 class VmaDefragmentationAlgorithm
5947 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)5948 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5949 public:
5950 VmaDefragmentationAlgorithm(
5951 VmaAllocator hAllocator,
5952 VmaBlockVector* pBlockVector,
5953 uint32_t currentFrameIndex) :
5954 m_hAllocator(hAllocator),
5955 m_pBlockVector(pBlockVector),
5956 m_CurrentFrameIndex(currentFrameIndex)
5957 {
5958 }
~VmaDefragmentationAlgorithm()5959 virtual ~VmaDefragmentationAlgorithm()
5960 {
5961 }
5962
5963 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5964 virtual void AddAll() = 0;
5965
5966 virtual VkResult Defragment(
5967 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5968 VkDeviceSize maxBytesToMove,
5969 uint32_t maxAllocationsToMove,
5970 VmaDefragmentationFlags flags) = 0;
5971
5972 virtual VkDeviceSize GetBytesMoved() const = 0;
5973 virtual uint32_t GetAllocationsMoved() const = 0;
5974
5975 protected:
5976 VmaAllocator const m_hAllocator;
5977 VmaBlockVector* const m_pBlockVector;
5978 const uint32_t m_CurrentFrameIndex;
5979
5980 struct AllocationInfo
5981 {
5982 VmaAllocation m_hAllocation;
5983 VkBool32* m_pChanged;
5984
AllocationInfoAllocationInfo5985 AllocationInfo() :
5986 m_hAllocation(VK_NULL_HANDLE),
5987 m_pChanged(VMA_NULL)
5988 {
5989 }
AllocationInfoAllocationInfo5990 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
5991 m_hAllocation(hAlloc),
5992 m_pChanged(pChanged)
5993 {
5994 }
5995 };
5996 };
5997
5998 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
5999 {
6000 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6001 public:
6002 VmaDefragmentationAlgorithm_Generic(
6003 VmaAllocator hAllocator,
6004 VmaBlockVector* pBlockVector,
6005 uint32_t currentFrameIndex,
6006 bool overlappingMoveSupported);
6007 virtual ~VmaDefragmentationAlgorithm_Generic();
6008
6009 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6010 virtual void AddAll() { m_AllAllocations = true; }
6011
6012 virtual VkResult Defragment(
6013 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6014 VkDeviceSize maxBytesToMove,
6015 uint32_t maxAllocationsToMove,
6016 VmaDefragmentationFlags flags);
6017
GetBytesMoved()6018 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6019 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6020
6021 private:
6022 uint32_t m_AllocationCount;
6023 bool m_AllAllocations;
6024
6025 VkDeviceSize m_BytesMoved;
6026 uint32_t m_AllocationsMoved;
6027
6028 struct AllocationInfoSizeGreater
6029 {
operatorAllocationInfoSizeGreater6030 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6031 {
6032 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6033 }
6034 };
6035
6036 struct AllocationInfoOffsetGreater
6037 {
operatorAllocationInfoOffsetGreater6038 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6039 {
6040 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6041 }
6042 };
6043
6044 struct BlockInfo
6045 {
6046 size_t m_OriginalBlockIndex;
6047 VmaDeviceMemoryBlock* m_pBlock;
6048 bool m_HasNonMovableAllocations;
6049 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6050
BlockInfoBlockInfo6051 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6052 m_OriginalBlockIndex(SIZE_MAX),
6053 m_pBlock(VMA_NULL),
6054 m_HasNonMovableAllocations(true),
6055 m_Allocations(pAllocationCallbacks)
6056 {
6057 }
6058
CalcHasNonMovableAllocationsBlockInfo6059 void CalcHasNonMovableAllocations()
6060 {
6061 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6062 const size_t defragmentAllocCount = m_Allocations.size();
6063 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6064 }
6065
SortAllocationsBySizeDescendingBlockInfo6066 void SortAllocationsBySizeDescending()
6067 {
6068 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6069 }
6070
SortAllocationsByOffsetDescendingBlockInfo6071 void SortAllocationsByOffsetDescending()
6072 {
6073 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6074 }
6075 };
6076
6077 struct BlockPointerLess
6078 {
operatorBlockPointerLess6079 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6080 {
6081 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6082 }
operatorBlockPointerLess6083 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6084 {
6085 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6086 }
6087 };
6088
6089 // 1. Blocks with some non-movable allocations go first.
6090 // 2. Blocks with smaller sumFreeSize go first.
6091 struct BlockInfoCompareMoveDestination
6092 {
operatorBlockInfoCompareMoveDestination6093 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6094 {
6095 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6096 {
6097 return true;
6098 }
6099 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6100 {
6101 return false;
6102 }
6103 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6104 {
6105 return true;
6106 }
6107 return false;
6108 }
6109 };
6110
6111 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6112 BlockInfoVector m_Blocks;
6113
6114 VkResult DefragmentRound(
6115 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6116 VkDeviceSize maxBytesToMove,
6117 uint32_t maxAllocationsToMove,
6118 bool freeOldAllocations);
6119
6120 size_t CalcBlocksWithNonMovableCount() const;
6121
6122 static bool MoveMakesSense(
6123 size_t dstBlockIndex, VkDeviceSize dstOffset,
6124 size_t srcBlockIndex, VkDeviceSize srcOffset);
6125 };
6126
6127 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6128 {
6129 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6130 public:
6131 VmaDefragmentationAlgorithm_Fast(
6132 VmaAllocator hAllocator,
6133 VmaBlockVector* pBlockVector,
6134 uint32_t currentFrameIndex,
6135 bool overlappingMoveSupported);
6136 virtual ~VmaDefragmentationAlgorithm_Fast();
6137
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)6138 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()6139 virtual void AddAll() { m_AllAllocations = true; }
6140
6141 virtual VkResult Defragment(
6142 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6143 VkDeviceSize maxBytesToMove,
6144 uint32_t maxAllocationsToMove,
6145 VmaDefragmentationFlags flags);
6146
GetBytesMoved()6147 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6148 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6149
6150 private:
6151 struct BlockInfo
6152 {
6153 size_t origBlockIndex;
6154 };
6155
6156 class FreeSpaceDatabase
6157 {
6158 public:
FreeSpaceDatabase()6159 FreeSpaceDatabase()
6160 {
6161 FreeSpace s = {};
6162 s.blockInfoIndex = SIZE_MAX;
6163 for(size_t i = 0; i < MAX_COUNT; ++i)
6164 {
6165 m_FreeSpaces[i] = s;
6166 }
6167 }
6168
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)6169 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6170 {
6171 // Find first invalid or the smallest structure.
6172 size_t bestIndex = SIZE_MAX;
6173 for(size_t i = 0; i < MAX_COUNT; ++i)
6174 {
6175 // Empty structure.
6176 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6177 {
6178 bestIndex = i;
6179 break;
6180 }
6181 if(m_FreeSpaces[i].size < size &&
6182 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6183 {
6184 bestIndex = i;
6185 }
6186 }
6187
6188 if(bestIndex != SIZE_MAX)
6189 {
6190 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6191 m_FreeSpaces[bestIndex].offset = offset;
6192 m_FreeSpaces[bestIndex].size = size;
6193 }
6194 }
6195
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)6196 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6197 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6198 {
6199 size_t bestIndex = SIZE_MAX;
6200 VkDeviceSize bestFreeSpaceAfter = 0;
6201 for(size_t i = 0; i < MAX_COUNT; ++i)
6202 {
6203 // Structure is valid.
6204 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6205 {
6206 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6207 // Allocation fits into this structure.
6208 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6209 {
6210 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6211 (dstOffset + size);
6212 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6213 {
6214 bestIndex = i;
6215 bestFreeSpaceAfter = freeSpaceAfter;
6216 }
6217 }
6218 }
6219 }
6220
6221 if(bestIndex != SIZE_MAX)
6222 {
6223 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6224 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6225
6226 // Leave this structure for remaining empty space.
6227 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6228 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6229 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6230
6231 return true;
6232 }
6233
6234 return false;
6235 }
6236
6237 private:
6238 static const size_t MAX_COUNT = 4;
6239
6240 struct FreeSpace
6241 {
6242 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6243 VkDeviceSize offset;
6244 VkDeviceSize size;
6245 } m_FreeSpaces[MAX_COUNT];
6246 };
6247
6248 const bool m_OverlappingMoveSupported;
6249
6250 uint32_t m_AllocationCount;
6251 bool m_AllAllocations;
6252
6253 VkDeviceSize m_BytesMoved;
6254 uint32_t m_AllocationsMoved;
6255
6256 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6257
6258 void PreprocessMetadata();
6259 void PostprocessMetadata();
6260 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6261 };
6262
6263 struct VmaBlockDefragmentationContext
6264 {
6265 enum BLOCK_FLAG
6266 {
6267 BLOCK_FLAG_USED = 0x00000001,
6268 };
6269 uint32_t flags;
6270 VkBuffer hBuffer;
6271 };
6272
6273 class VmaBlockVectorDefragmentationContext
6274 {
6275 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6276 public:
6277 VkResult res;
6278 bool mutexLocked;
6279 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6280 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
6281 uint32_t defragmentationMovesProcessed;
6282 uint32_t defragmentationMovesCommitted;
6283 bool hasDefragmentationPlan;
6284
6285 VmaBlockVectorDefragmentationContext(
6286 VmaAllocator hAllocator,
6287 VmaPool hCustomPool, // Optional.
6288 VmaBlockVector* pBlockVector,
6289 uint32_t currFrameIndex);
6290 ~VmaBlockVectorDefragmentationContext();
6291
GetCustomPool()6292 VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()6293 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()6294 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6295
6296 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6297 void AddAll() { m_AllAllocations = true; }
6298
6299 void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
6300
6301 private:
6302 const VmaAllocator m_hAllocator;
6303 // Null if not from custom pool.
6304 const VmaPool m_hCustomPool;
6305 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6306 VmaBlockVector* const m_pBlockVector;
6307 const uint32_t m_CurrFrameIndex;
6308 // Owner of this object.
6309 VmaDefragmentationAlgorithm* m_pAlgorithm;
6310
6311 struct AllocInfo
6312 {
6313 VmaAllocation hAlloc;
6314 VkBool32* pChanged;
6315 };
6316 // Used between constructor and Begin.
6317 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6318 bool m_AllAllocations;
6319 };
6320
6321 struct VmaDefragmentationContext_T
6322 {
6323 private:
6324 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6325 public:
6326 VmaDefragmentationContext_T(
6327 VmaAllocator hAllocator,
6328 uint32_t currFrameIndex,
6329 uint32_t flags,
6330 VmaDefragmentationStats* pStats);
6331 ~VmaDefragmentationContext_T();
6332
6333 void AddPools(uint32_t poolCount, const VmaPool* pPools);
6334 void AddAllocations(
6335 uint32_t allocationCount,
6336 const VmaAllocation* pAllocations,
6337 VkBool32* pAllocationsChanged);
6338
6339 /*
6340 Returns:
6341 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6342 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6343 - Negative value if error occurred and object can be destroyed immediately.
6344 */
6345 VkResult Defragment(
6346 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6347 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6348 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
6349
6350 VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
6351 VkResult DefragmentPassEnd();
6352
6353 private:
6354 const VmaAllocator m_hAllocator;
6355 const uint32_t m_CurrFrameIndex;
6356 const uint32_t m_Flags;
6357 VmaDefragmentationStats* const m_pStats;
6358
6359 VkDeviceSize m_MaxCpuBytesToMove;
6360 uint32_t m_MaxCpuAllocationsToMove;
6361 VkDeviceSize m_MaxGpuBytesToMove;
6362 uint32_t m_MaxGpuAllocationsToMove;
6363
6364 // Owner of these objects.
6365 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6366 // Owner of these objects.
6367 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6368 };
6369
6370 #if VMA_RECORDING_ENABLED
6371
6372 class VmaRecorder
6373 {
6374 public:
6375 VmaRecorder();
6376 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6377 void WriteConfiguration(
6378 const VkPhysicalDeviceProperties& devProps,
6379 const VkPhysicalDeviceMemoryProperties& memProps,
6380 uint32_t vulkanApiVersion,
6381 bool dedicatedAllocationExtensionEnabled,
6382 bool bindMemory2ExtensionEnabled,
6383 bool memoryBudgetExtensionEnabled,
6384 bool deviceCoherentMemoryExtensionEnabled);
6385 ~VmaRecorder();
6386
6387 void RecordCreateAllocator(uint32_t frameIndex);
6388 void RecordDestroyAllocator(uint32_t frameIndex);
6389 void RecordCreatePool(uint32_t frameIndex,
6390 const VmaPoolCreateInfo& createInfo,
6391 VmaPool pool);
6392 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6393 void RecordAllocateMemory(uint32_t frameIndex,
6394 const VkMemoryRequirements& vkMemReq,
6395 const VmaAllocationCreateInfo& createInfo,
6396 VmaAllocation allocation);
6397 void RecordAllocateMemoryPages(uint32_t frameIndex,
6398 const VkMemoryRequirements& vkMemReq,
6399 const VmaAllocationCreateInfo& createInfo,
6400 uint64_t allocationCount,
6401 const VmaAllocation* pAllocations);
6402 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6403 const VkMemoryRequirements& vkMemReq,
6404 bool requiresDedicatedAllocation,
6405 bool prefersDedicatedAllocation,
6406 const VmaAllocationCreateInfo& createInfo,
6407 VmaAllocation allocation);
6408 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6409 const VkMemoryRequirements& vkMemReq,
6410 bool requiresDedicatedAllocation,
6411 bool prefersDedicatedAllocation,
6412 const VmaAllocationCreateInfo& createInfo,
6413 VmaAllocation allocation);
6414 void RecordFreeMemory(uint32_t frameIndex,
6415 VmaAllocation allocation);
6416 void RecordFreeMemoryPages(uint32_t frameIndex,
6417 uint64_t allocationCount,
6418 const VmaAllocation* pAllocations);
6419 void RecordSetAllocationUserData(uint32_t frameIndex,
6420 VmaAllocation allocation,
6421 const void* pUserData);
6422 void RecordCreateLostAllocation(uint32_t frameIndex,
6423 VmaAllocation allocation);
6424 void RecordMapMemory(uint32_t frameIndex,
6425 VmaAllocation allocation);
6426 void RecordUnmapMemory(uint32_t frameIndex,
6427 VmaAllocation allocation);
6428 void RecordFlushAllocation(uint32_t frameIndex,
6429 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6430 void RecordInvalidateAllocation(uint32_t frameIndex,
6431 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6432 void RecordCreateBuffer(uint32_t frameIndex,
6433 const VkBufferCreateInfo& bufCreateInfo,
6434 const VmaAllocationCreateInfo& allocCreateInfo,
6435 VmaAllocation allocation);
6436 void RecordCreateImage(uint32_t frameIndex,
6437 const VkImageCreateInfo& imageCreateInfo,
6438 const VmaAllocationCreateInfo& allocCreateInfo,
6439 VmaAllocation allocation);
6440 void RecordDestroyBuffer(uint32_t frameIndex,
6441 VmaAllocation allocation);
6442 void RecordDestroyImage(uint32_t frameIndex,
6443 VmaAllocation allocation);
6444 void RecordTouchAllocation(uint32_t frameIndex,
6445 VmaAllocation allocation);
6446 void RecordGetAllocationInfo(uint32_t frameIndex,
6447 VmaAllocation allocation);
6448 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6449 VmaPool pool);
6450 void RecordDefragmentationBegin(uint32_t frameIndex,
6451 const VmaDefragmentationInfo2& info,
6452 VmaDefragmentationContext ctx);
6453 void RecordDefragmentationEnd(uint32_t frameIndex,
6454 VmaDefragmentationContext ctx);
6455 void RecordSetPoolName(uint32_t frameIndex,
6456 VmaPool pool,
6457 const char* name);
6458
6459 private:
6460 struct CallParams
6461 {
6462 uint32_t threadId;
6463 double time;
6464 };
6465
6466 class UserDataString
6467 {
6468 public:
6469 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()6470 const char* GetString() const { return m_Str; }
6471
6472 private:
6473 char m_PtrStr[17];
6474 const char* m_Str;
6475 };
6476
6477 bool m_UseMutex;
6478 VmaRecordFlags m_Flags;
6479 FILE* m_File;
6480 VMA_MUTEX m_FileMutex;
6481 std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
6482
6483 void GetBasicParams(CallParams& outParams);
6484
6485 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6486 template<typename T>
PrintPointerList(uint64_t count,const T * pItems)6487 void PrintPointerList(uint64_t count, const T* pItems)
6488 {
6489 if(count)
6490 {
6491 fprintf(m_File, "%p", pItems[0]);
6492 for(uint64_t i = 1; i < count; ++i)
6493 {
6494 fprintf(m_File, " %p", pItems[i]);
6495 }
6496 }
6497 }
6498
6499 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6500 void Flush();
6501 };
6502
6503 #endif // #if VMA_RECORDING_ENABLED
6504
6505 /*
6506 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
6507 */
6508 class VmaAllocationObjectAllocator
6509 {
6510 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
6511 public:
6512 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
6513
6514 template<typename... Types> VmaAllocation Allocate(Types&&... args);
6515 void Free(VmaAllocation hAlloc);
6516
6517 private:
6518 VMA_MUTEX m_Mutex;
6519 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
6520 };
6521
6522 struct VmaCurrentBudgetData
6523 {
6524 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
6525 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
6526
6527 #if VMA_MEMORY_BUDGET
6528 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
6529 VMA_RW_MUTEX m_BudgetMutex;
6530 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
6531 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
6532 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
6533 #endif // #if VMA_MEMORY_BUDGET
6534
VmaCurrentBudgetDataVmaCurrentBudgetData6535 VmaCurrentBudgetData()
6536 {
6537 for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
6538 {
6539 m_BlockBytes[heapIndex] = 0;
6540 m_AllocationBytes[heapIndex] = 0;
6541 #if VMA_MEMORY_BUDGET
6542 m_VulkanUsage[heapIndex] = 0;
6543 m_VulkanBudget[heapIndex] = 0;
6544 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
6545 #endif
6546 }
6547
6548 #if VMA_MEMORY_BUDGET
6549 m_OperationsSinceBudgetFetch = 0;
6550 #endif
6551 }
6552
AddAllocationVmaCurrentBudgetData6553 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6554 {
6555 m_AllocationBytes[heapIndex] += allocationSize;
6556 #if VMA_MEMORY_BUDGET
6557 ++m_OperationsSinceBudgetFetch;
6558 #endif
6559 }
6560
RemoveAllocationVmaCurrentBudgetData6561 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6562 {
6563 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
6564 m_AllocationBytes[heapIndex] -= allocationSize;
6565 #if VMA_MEMORY_BUDGET
6566 ++m_OperationsSinceBudgetFetch;
6567 #endif
6568 }
6569 };
6570
6571 // Main allocator object.
6572 struct VmaAllocator_T
6573 {
6574 VMA_CLASS_NO_COPY(VmaAllocator_T)
6575 public:
6576 bool m_UseMutex;
6577 uint32_t m_VulkanApiVersion;
6578 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
6579 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
6580 bool m_UseExtMemoryBudget;
6581 bool m_UseAmdDeviceCoherentMemory;
6582 bool m_UseKhrBufferDeviceAddress;
6583 bool m_UseExtMemoryPriority;
6584 VkDevice m_hDevice;
6585 VkInstance m_hInstance;
6586 bool m_AllocationCallbacksSpecified;
6587 VkAllocationCallbacks m_AllocationCallbacks;
6588 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6589 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
6590
6591 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
6592 uint32_t m_HeapSizeLimitMask;
6593
6594 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6595 VkPhysicalDeviceMemoryProperties m_MemProps;
6596
6597 // Default pools.
6598 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6599
6600 typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
6601 DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
6602 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6603
6604 VmaCurrentBudgetData m_Budget;
6605 VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
6606
6607 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6608 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6609 ~VmaAllocator_T();
6610
GetAllocationCallbacksVmaAllocator_T6611 const VkAllocationCallbacks* GetAllocationCallbacks() const
6612 {
6613 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
6614 }
GetVulkanFunctionsVmaAllocator_T6615 const VmaVulkanFunctions& GetVulkanFunctions() const
6616 {
6617 return m_VulkanFunctions;
6618 }
6619
GetPhysicalDeviceVmaAllocator_T6620 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
6621
GetBufferImageGranularityVmaAllocator_T6622 VkDeviceSize GetBufferImageGranularity() const
6623 {
6624 return VMA_MAX(
6625 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6626 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6627 }
6628
GetMemoryHeapCountVmaAllocator_T6629 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T6630 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6631
MemoryTypeIndexToHeapIndexVmaAllocator_T6632 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6633 {
6634 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6635 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6636 }
6637 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T6638 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6639 {
6640 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6641 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6642 }
6643 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T6644 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6645 {
6646 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6647 VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6648 (VkDeviceSize)VMA_MIN_ALIGNMENT;
6649 }
6650
IsIntegratedGpuVmaAllocator_T6651 bool IsIntegratedGpu() const
6652 {
6653 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6654 }
6655
GetGlobalMemoryTypeBitsVmaAllocator_T6656 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
6657
6658 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T6659 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6660 #endif
6661
6662 void GetBufferMemoryRequirements(
6663 VkBuffer hBuffer,
6664 VkMemoryRequirements& memReq,
6665 bool& requiresDedicatedAllocation,
6666 bool& prefersDedicatedAllocation) const;
6667 void GetImageMemoryRequirements(
6668 VkImage hImage,
6669 VkMemoryRequirements& memReq,
6670 bool& requiresDedicatedAllocation,
6671 bool& prefersDedicatedAllocation) const;
6672
6673 // Main allocation function.
6674 VkResult AllocateMemory(
6675 const VkMemoryRequirements& vkMemReq,
6676 bool requiresDedicatedAllocation,
6677 bool prefersDedicatedAllocation,
6678 VkBuffer dedicatedBuffer,
6679 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
6680 VkImage dedicatedImage,
6681 const VmaAllocationCreateInfo& createInfo,
6682 VmaSuballocationType suballocType,
6683 size_t allocationCount,
6684 VmaAllocation* pAllocations);
6685
6686 // Main deallocation function.
6687 void FreeMemory(
6688 size_t allocationCount,
6689 const VmaAllocation* pAllocations);
6690
6691 void CalculateStats(VmaStats* pStats);
6692
6693 void GetBudget(
6694 VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
6695
6696 #if VMA_STATS_STRING_ENABLED
6697 void PrintDetailedMap(class VmaJsonWriter& json);
6698 #endif
6699
6700 VkResult DefragmentationBegin(
6701 const VmaDefragmentationInfo2& info,
6702 VmaDefragmentationStats* pStats,
6703 VmaDefragmentationContext* pContext);
6704 VkResult DefragmentationEnd(
6705 VmaDefragmentationContext context);
6706
6707 VkResult DefragmentationPassBegin(
6708 VmaDefragmentationPassInfo* pInfo,
6709 VmaDefragmentationContext context);
6710 VkResult DefragmentationPassEnd(
6711 VmaDefragmentationContext context);
6712
6713 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6714 bool TouchAllocation(VmaAllocation hAllocation);
6715
6716 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6717 void DestroyPool(VmaPool pool);
6718 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6719
6720 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T6721 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6722
6723 void MakePoolAllocationsLost(
6724 VmaPool hPool,
6725 size_t* pLostAllocationCount);
6726 VkResult CheckPoolCorruption(VmaPool hPool);
6727 VkResult CheckCorruption(uint32_t memoryTypeBits);
6728
6729 void CreateLostAllocation(VmaAllocation* pAllocation);
6730
6731 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
6732 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6733 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
6734 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6735 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
6736 VkResult BindVulkanBuffer(
6737 VkDeviceMemory memory,
6738 VkDeviceSize memoryOffset,
6739 VkBuffer buffer,
6740 const void* pNext);
6741 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
6742 VkResult BindVulkanImage(
6743 VkDeviceMemory memory,
6744 VkDeviceSize memoryOffset,
6745 VkImage image,
6746 const void* pNext);
6747
6748 VkResult Map(VmaAllocation hAllocation, void** ppData);
6749 void Unmap(VmaAllocation hAllocation);
6750
6751 VkResult BindBufferMemory(
6752 VmaAllocation hAllocation,
6753 VkDeviceSize allocationLocalOffset,
6754 VkBuffer hBuffer,
6755 const void* pNext);
6756 VkResult BindImageMemory(
6757 VmaAllocation hAllocation,
6758 VkDeviceSize allocationLocalOffset,
6759 VkImage hImage,
6760 const void* pNext);
6761
6762 VkResult FlushOrInvalidateAllocation(
6763 VmaAllocation hAllocation,
6764 VkDeviceSize offset, VkDeviceSize size,
6765 VMA_CACHE_OPERATION op);
6766 VkResult FlushOrInvalidateAllocations(
6767 uint32_t allocationCount,
6768 const VmaAllocation* allocations,
6769 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
6770 VMA_CACHE_OPERATION op);
6771
6772 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6773
6774 /*
6775 Returns bit mask of memory types that can support defragmentation on GPU as
6776 they support creation of required buffer for copy operations.
6777 */
6778 uint32_t GetGpuDefragmentationMemoryTypeBits();
6779
6780 #if VMA_EXTERNAL_MEMORY
GetExternalMemoryHandleTypeFlagsVmaAllocator_T6781 VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
6782 {
6783 return m_TypeExternalMemoryHandleTypes[memTypeIndex];
6784 }
6785 #endif // #if VMA_EXTERNAL_MEMORY
6786
6787 private:
6788 VkDeviceSize m_PreferredLargeHeapBlockSize;
6789
6790 VkPhysicalDevice m_PhysicalDevice;
6791 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6792 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
6793 #if VMA_EXTERNAL_MEMORY
6794 VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
6795 #endif // #if VMA_EXTERNAL_MEMORY
6796
6797 VMA_RW_MUTEX m_PoolsMutex;
6798 typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
6799 // Protected by m_PoolsMutex.
6800 PoolList m_Pools;
6801 uint32_t m_NextPoolId;
6802
6803 VmaVulkanFunctions m_VulkanFunctions;
6804
6805 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
6806 uint32_t m_GlobalMemoryTypeBits;
6807
6808 #if VMA_RECORDING_ENABLED
6809 VmaRecorder* m_pRecorder;
6810 #endif
6811
6812 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6813
6814 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
6815 void ImportVulkanFunctions_Static();
6816 #endif
6817
6818 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
6819
6820 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
6821 void ImportVulkanFunctions_Dynamic();
6822 #endif
6823
6824 void ValidateVulkanFunctions();
6825
6826 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6827
6828 VkResult AllocateMemoryOfType(
6829 VkDeviceSize size,
6830 VkDeviceSize alignment,
6831 bool dedicatedAllocation,
6832 VkBuffer dedicatedBuffer,
6833 VkBufferUsageFlags dedicatedBufferUsage,
6834 VkImage dedicatedImage,
6835 const VmaAllocationCreateInfo& createInfo,
6836 uint32_t memTypeIndex,
6837 VmaSuballocationType suballocType,
6838 size_t allocationCount,
6839 VmaAllocation* pAllocations);
6840
6841 // Helper function only to be used inside AllocateDedicatedMemory.
6842 VkResult AllocateDedicatedMemoryPage(
6843 VkDeviceSize size,
6844 VmaSuballocationType suballocType,
6845 uint32_t memTypeIndex,
6846 const VkMemoryAllocateInfo& allocInfo,
6847 bool map,
6848 bool isUserDataString,
6849 void* pUserData,
6850 VmaAllocation* pAllocation);
6851
6852 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6853 VkResult AllocateDedicatedMemory(
6854 VkDeviceSize size,
6855 VmaSuballocationType suballocType,
6856 uint32_t memTypeIndex,
6857 bool withinBudget,
6858 bool map,
6859 bool isUserDataString,
6860 void* pUserData,
6861 float priority,
6862 VkBuffer dedicatedBuffer,
6863 VkBufferUsageFlags dedicatedBufferUsage,
6864 VkImage dedicatedImage,
6865 size_t allocationCount,
6866 VmaAllocation* pAllocations);
6867
6868 void FreeDedicatedMemory(const VmaAllocation allocation);
6869
6870 /*
6871 Calculates and returns bit mask of memory types that can support defragmentation
6872 on GPU as they support creation of required buffer for copy operations.
6873 */
6874 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
6875
6876 uint32_t CalculateGlobalMemoryTypeBits() const;
6877
6878 bool GetFlushOrInvalidateRange(
6879 VmaAllocation allocation,
6880 VkDeviceSize offset, VkDeviceSize size,
6881 VkMappedMemoryRange& outRange) const;
6882
6883 #if VMA_MEMORY_BUDGET
6884 void UpdateVulkanBudget();
6885 #endif // #if VMA_MEMORY_BUDGET
6886 };
6887
6888 class VmaStringBuilder;
6889
VmaInitStatInfo(VmaStatInfo & outInfo)6890 static void VmaInitStatInfo(VmaStatInfo& outInfo)
6891 {
6892 memset(&outInfo, 0, sizeof(outInfo));
6893 outInfo.allocationSizeMin = UINT64_MAX;
6894 outInfo.unusedRangeSizeMin = UINT64_MAX;
6895 }
6896
6897 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)6898 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
6899 {
6900 inoutInfo.blockCount += srcInfo.blockCount;
6901 inoutInfo.allocationCount += srcInfo.allocationCount;
6902 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
6903 inoutInfo.usedBytes += srcInfo.usedBytes;
6904 inoutInfo.unusedBytes += srcInfo.unusedBytes;
6905 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
6906 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
6907 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
6908 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
6909 }
6910
VmaAddStatInfoAllocation(VmaStatInfo & inoutInfo,VkDeviceSize size)6911 static void VmaAddStatInfoAllocation(VmaStatInfo& inoutInfo, VkDeviceSize size)
6912 {
6913 ++inoutInfo.allocationCount;
6914 inoutInfo.usedBytes += size;
6915 if(size < inoutInfo.allocationSizeMin)
6916 {
6917 inoutInfo.allocationSizeMin = size;
6918 }
6919 if(size > inoutInfo.allocationSizeMax)
6920 {
6921 inoutInfo.allocationSizeMax = size;
6922 }
6923 }
6924
VmaAddStatInfoUnusedRange(VmaStatInfo & inoutInfo,VkDeviceSize size)6925 static void VmaAddStatInfoUnusedRange(VmaStatInfo& inoutInfo, VkDeviceSize size)
6926 {
6927 ++inoutInfo.unusedRangeCount;
6928 inoutInfo.unusedBytes += size;
6929 if(size < inoutInfo.unusedRangeSizeMin)
6930 {
6931 inoutInfo.unusedRangeSizeMin = size;
6932 }
6933 if(size > inoutInfo.unusedRangeSizeMax)
6934 {
6935 inoutInfo.unusedRangeSizeMax = size;
6936 }
6937 }
6938
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)6939 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
6940 {
6941 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
6942 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
6943 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
6944 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
6945 }
6946
6947 struct VmaVirtualBlock_T
6948 {
6949 VMA_CLASS_NO_COPY(VmaVirtualBlock_T)
6950 public:
6951 const bool m_AllocationCallbacksSpecified;
6952 const VkAllocationCallbacks m_AllocationCallbacks;
6953
6954 VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
6955 ~VmaVirtualBlock_T();
InitVmaVirtualBlock_T6956 VkResult Init()
6957 {
6958 return VK_SUCCESS;
6959 }
6960
GetAllocationCallbacksVmaVirtualBlock_T6961 const VkAllocationCallbacks* GetAllocationCallbacks() const
6962 {
6963 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
6964 }
IsEmptyVmaVirtualBlock_T6965 bool IsEmpty() const
6966 {
6967 return m_Metadata->IsEmpty();
6968 }
GetAllocationInfoVmaVirtualBlock_T6969 void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
6970 {
6971 m_Metadata->GetAllocationInfo(offset, outInfo);
6972 }
6973 VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset);
FreeVmaVirtualBlock_T6974 void Free(VkDeviceSize offset)
6975 {
6976 m_Metadata->FreeAtOffset(offset);
6977 }
ClearVmaVirtualBlock_T6978 void Clear()
6979 {
6980 m_Metadata->Clear();
6981 }
SetAllocationUserDataVmaVirtualBlock_T6982 void SetAllocationUserData(VkDeviceSize offset, void* userData)
6983 {
6984 m_Metadata->SetAllocationUserData(offset, userData);
6985 }
CalculateStatsVmaVirtualBlock_T6986 void CalculateStats(VmaStatInfo& outStatInfo) const
6987 {
6988 m_Metadata->CalcAllocationStatInfo(outStatInfo);
6989 VmaPostprocessCalcStatInfo(outStatInfo);
6990 }
6991 #if VMA_STATS_STRING_ENABLED
6992 void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
6993 #endif
6994
6995 private:
6996 VmaBlockMetadata* m_Metadata;
6997 };
6998
6999 ////////////////////////////////////////////////////////////////////////////////
7000 // Memory allocation #2 after VmaAllocator_T definition
7001
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)7002 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7003 {
7004 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7005 }
7006
VmaFree(VmaAllocator hAllocator,void * ptr)7007 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7008 {
7009 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7010 }
7011
7012 template<typename T>
VmaAllocate(VmaAllocator hAllocator)7013 static T* VmaAllocate(VmaAllocator hAllocator)
7014 {
7015 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7016 }
7017
7018 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)7019 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7020 {
7021 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7022 }
7023
7024 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)7025 static void vma_delete(VmaAllocator hAllocator, T* ptr)
7026 {
7027 if(ptr != VMA_NULL)
7028 {
7029 ptr->~T();
7030 VmaFree(hAllocator, ptr);
7031 }
7032 }
7033
7034 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)7035 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
7036 {
7037 if(ptr != VMA_NULL)
7038 {
7039 for(size_t i = count; i--; )
7040 ptr[i].~T();
7041 VmaFree(hAllocator, ptr);
7042 }
7043 }
7044
7045 ////////////////////////////////////////////////////////////////////////////////
7046 // VmaStringBuilder
7047
7048 #if VMA_STATS_STRING_ENABLED
7049
7050 class VmaStringBuilder
7051 {
7052 public:
VmaStringBuilder(const VkAllocationCallbacks * allocationCallbacks)7053 VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) { }
GetLength()7054 size_t GetLength() const { return m_Data.size(); }
GetData()7055 const char* GetData() const { return m_Data.data(); }
7056
Add(char ch)7057 void Add(char ch) { m_Data.push_back(ch); }
7058 void Add(const char* pStr);
AddNewLine()7059 void AddNewLine() { Add('\n'); }
7060 void AddNumber(uint32_t num);
7061 void AddNumber(uint64_t num);
7062 void AddPointer(const void* ptr);
7063
7064 private:
7065 VmaVector< char, VmaStlAllocator<char> > m_Data;
7066 };
7067
Add(const char * pStr)7068 void VmaStringBuilder::Add(const char* pStr)
7069 {
7070 const size_t strLen = strlen(pStr);
7071 if(strLen > 0)
7072 {
7073 const size_t oldCount = m_Data.size();
7074 m_Data.resize(oldCount + strLen);
7075 memcpy(m_Data.data() + oldCount, pStr, strLen);
7076 }
7077 }
7078
AddNumber(uint32_t num)7079 void VmaStringBuilder::AddNumber(uint32_t num)
7080 {
7081 char buf[11];
7082 buf[10] = '\0';
7083 char *p = &buf[10];
7084 do
7085 {
7086 *--p = '0' + (num % 10);
7087 num /= 10;
7088 }
7089 while(num);
7090 Add(p);
7091 }
7092
AddNumber(uint64_t num)7093 void VmaStringBuilder::AddNumber(uint64_t num)
7094 {
7095 char buf[21];
7096 buf[20] = '\0';
7097 char *p = &buf[20];
7098 do
7099 {
7100 *--p = '0' + (num % 10);
7101 num /= 10;
7102 }
7103 while(num);
7104 Add(p);
7105 }
7106
AddPointer(const void * ptr)7107 void VmaStringBuilder::AddPointer(const void* ptr)
7108 {
7109 char buf[21];
7110 VmaPtrToStr(buf, sizeof(buf), ptr);
7111 Add(buf);
7112 }
7113
7114 #endif // #if VMA_STATS_STRING_ENABLED
7115
7116 ////////////////////////////////////////////////////////////////////////////////
7117 // VmaJsonWriter
7118
7119 #if VMA_STATS_STRING_ENABLED
7120
7121 class VmaJsonWriter
7122 {
7123 VMA_CLASS_NO_COPY(VmaJsonWriter)
7124 public:
7125 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
7126 ~VmaJsonWriter();
7127
7128 void BeginObject(bool singleLine = false);
7129 void EndObject();
7130
7131 void BeginArray(bool singleLine = false);
7132 void EndArray();
7133
7134 void WriteString(const char* pStr);
7135 void BeginString(const char* pStr = VMA_NULL);
7136 void ContinueString(const char* pStr);
7137 void ContinueString(uint32_t n);
7138 void ContinueString(uint64_t n);
7139 void ContinueString_Pointer(const void* ptr);
7140 void EndString(const char* pStr = VMA_NULL);
7141
7142 void WriteNumber(uint32_t n);
7143 void WriteNumber(uint64_t n);
7144 void WriteBool(bool b);
7145 void WriteNull();
7146
7147 private:
7148 static const char* const INDENT;
7149
7150 enum COLLECTION_TYPE
7151 {
7152 COLLECTION_TYPE_OBJECT,
7153 COLLECTION_TYPE_ARRAY,
7154 };
7155 struct StackItem
7156 {
7157 COLLECTION_TYPE type;
7158 uint32_t valueCount;
7159 bool singleLineMode;
7160 };
7161
7162 VmaStringBuilder& m_SB;
7163 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
7164 bool m_InsideString;
7165
7166 void BeginValue(bool isString);
7167 void WriteIndent(bool oneLess = false);
7168 };
7169
7170 const char* const VmaJsonWriter::INDENT = " ";
7171
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)7172 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
7173 m_SB(sb),
7174 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
7175 m_InsideString(false)
7176 {
7177 }
7178
~VmaJsonWriter()7179 VmaJsonWriter::~VmaJsonWriter()
7180 {
7181 VMA_ASSERT(!m_InsideString);
7182 VMA_ASSERT(m_Stack.empty());
7183 }
7184
BeginObject(bool singleLine)7185 void VmaJsonWriter::BeginObject(bool singleLine)
7186 {
7187 VMA_ASSERT(!m_InsideString);
7188
7189 BeginValue(false);
7190 m_SB.Add('{');
7191
7192 StackItem item;
7193 item.type = COLLECTION_TYPE_OBJECT;
7194 item.valueCount = 0;
7195 item.singleLineMode = singleLine;
7196 m_Stack.push_back(item);
7197 }
7198
EndObject()7199 void VmaJsonWriter::EndObject()
7200 {
7201 VMA_ASSERT(!m_InsideString);
7202
7203 WriteIndent(true);
7204 m_SB.Add('}');
7205
7206 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
7207 m_Stack.pop_back();
7208 }
7209
BeginArray(bool singleLine)7210 void VmaJsonWriter::BeginArray(bool singleLine)
7211 {
7212 VMA_ASSERT(!m_InsideString);
7213
7214 BeginValue(false);
7215 m_SB.Add('[');
7216
7217 StackItem item;
7218 item.type = COLLECTION_TYPE_ARRAY;
7219 item.valueCount = 0;
7220 item.singleLineMode = singleLine;
7221 m_Stack.push_back(item);
7222 }
7223
EndArray()7224 void VmaJsonWriter::EndArray()
7225 {
7226 VMA_ASSERT(!m_InsideString);
7227
7228 WriteIndent(true);
7229 m_SB.Add(']');
7230
7231 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
7232 m_Stack.pop_back();
7233 }
7234
WriteString(const char * pStr)7235 void VmaJsonWriter::WriteString(const char* pStr)
7236 {
7237 BeginString(pStr);
7238 EndString();
7239 }
7240
BeginString(const char * pStr)7241 void VmaJsonWriter::BeginString(const char* pStr)
7242 {
7243 VMA_ASSERT(!m_InsideString);
7244
7245 BeginValue(true);
7246 m_SB.Add('"');
7247 m_InsideString = true;
7248 if(pStr != VMA_NULL && pStr[0] != '\0')
7249 {
7250 ContinueString(pStr);
7251 }
7252 }
7253
ContinueString(const char * pStr)7254 void VmaJsonWriter::ContinueString(const char* pStr)
7255 {
7256 VMA_ASSERT(m_InsideString);
7257
7258 const size_t strLen = strlen(pStr);
7259 for(size_t i = 0; i < strLen; ++i)
7260 {
7261 char ch = pStr[i];
7262 if(ch == '\\')
7263 {
7264 m_SB.Add("\\\\");
7265 }
7266 else if(ch == '"')
7267 {
7268 m_SB.Add("\\\"");
7269 }
7270 else if(ch >= 32)
7271 {
7272 m_SB.Add(ch);
7273 }
7274 else switch(ch)
7275 {
7276 case '\b':
7277 m_SB.Add("\\b");
7278 break;
7279 case '\f':
7280 m_SB.Add("\\f");
7281 break;
7282 case '\n':
7283 m_SB.Add("\\n");
7284 break;
7285 case '\r':
7286 m_SB.Add("\\r");
7287 break;
7288 case '\t':
7289 m_SB.Add("\\t");
7290 break;
7291 default:
7292 VMA_ASSERT(0 && "Character not currently supported.");
7293 break;
7294 }
7295 }
7296 }
7297
ContinueString(uint32_t n)7298 void VmaJsonWriter::ContinueString(uint32_t n)
7299 {
7300 VMA_ASSERT(m_InsideString);
7301 m_SB.AddNumber(n);
7302 }
7303
ContinueString(uint64_t n)7304 void VmaJsonWriter::ContinueString(uint64_t n)
7305 {
7306 VMA_ASSERT(m_InsideString);
7307 m_SB.AddNumber(n);
7308 }
7309
ContinueString_Pointer(const void * ptr)7310 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7311 {
7312 VMA_ASSERT(m_InsideString);
7313 m_SB.AddPointer(ptr);
7314 }
7315
EndString(const char * pStr)7316 void VmaJsonWriter::EndString(const char* pStr)
7317 {
7318 VMA_ASSERT(m_InsideString);
7319 if(pStr != VMA_NULL && pStr[0] != '\0')
7320 {
7321 ContinueString(pStr);
7322 }
7323 m_SB.Add('"');
7324 m_InsideString = false;
7325 }
7326
WriteNumber(uint32_t n)7327 void VmaJsonWriter::WriteNumber(uint32_t n)
7328 {
7329 VMA_ASSERT(!m_InsideString);
7330 BeginValue(false);
7331 m_SB.AddNumber(n);
7332 }
7333
WriteNumber(uint64_t n)7334 void VmaJsonWriter::WriteNumber(uint64_t n)
7335 {
7336 VMA_ASSERT(!m_InsideString);
7337 BeginValue(false);
7338 m_SB.AddNumber(n);
7339 }
7340
WriteBool(bool b)7341 void VmaJsonWriter::WriteBool(bool b)
7342 {
7343 VMA_ASSERT(!m_InsideString);
7344 BeginValue(false);
7345 m_SB.Add(b ? "true" : "false");
7346 }
7347
WriteNull()7348 void VmaJsonWriter::WriteNull()
7349 {
7350 VMA_ASSERT(!m_InsideString);
7351 BeginValue(false);
7352 m_SB.Add("null");
7353 }
7354
BeginValue(bool isString)7355 void VmaJsonWriter::BeginValue(bool isString)
7356 {
7357 if(!m_Stack.empty())
7358 {
7359 StackItem& currItem = m_Stack.back();
7360 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7361 currItem.valueCount % 2 == 0)
7362 {
7363 VMA_ASSERT(isString);
7364 }
7365
7366 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7367 currItem.valueCount % 2 != 0)
7368 {
7369 m_SB.Add(": ");
7370 }
7371 else if(currItem.valueCount > 0)
7372 {
7373 m_SB.Add(", ");
7374 WriteIndent();
7375 }
7376 else
7377 {
7378 WriteIndent();
7379 }
7380 ++currItem.valueCount;
7381 }
7382 }
7383
WriteIndent(bool oneLess)7384 void VmaJsonWriter::WriteIndent(bool oneLess)
7385 {
7386 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7387 {
7388 m_SB.AddNewLine();
7389
7390 size_t count = m_Stack.size();
7391 if(count > 0 && oneLess)
7392 {
7393 --count;
7394 }
7395 for(size_t i = 0; i < count; ++i)
7396 {
7397 m_SB.Add(INDENT);
7398 }
7399 }
7400 }
7401
7402 #endif // #if VMA_STATS_STRING_ENABLED
7403
7404 ////////////////////////////////////////////////////////////////////////////////
7405
SetUserData(VmaAllocator hAllocator,void * pUserData)7406 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7407 {
7408 if(IsUserDataString())
7409 {
7410 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7411
7412 FreeUserDataString(hAllocator);
7413
7414 if(pUserData != VMA_NULL)
7415 {
7416 m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
7417 }
7418 }
7419 else
7420 {
7421 m_pUserData = pUserData;
7422 }
7423 }
7424
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)7425 void VmaAllocation_T::ChangeBlockAllocation(
7426 VmaAllocator hAllocator,
7427 VmaDeviceMemoryBlock* block,
7428 VkDeviceSize offset)
7429 {
7430 VMA_ASSERT(block != VMA_NULL);
7431 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7432
7433 // Move mapping reference counter from old block to new block.
7434 if(block != m_BlockAllocation.m_Block)
7435 {
7436 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7437 if(IsPersistentMap())
7438 ++mapRefCount;
7439 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7440 block->Map(hAllocator, mapRefCount, VMA_NULL);
7441 }
7442
7443 m_BlockAllocation.m_Block = block;
7444 m_BlockAllocation.m_Offset = offset;
7445 }
7446
ChangeOffset(VkDeviceSize newOffset)7447 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7448 {
7449 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7450 m_BlockAllocation.m_Offset = newOffset;
7451 }
7452
GetOffset()7453 VkDeviceSize VmaAllocation_T::GetOffset() const
7454 {
7455 switch(m_Type)
7456 {
7457 case ALLOCATION_TYPE_BLOCK:
7458 return m_BlockAllocation.m_Offset;
7459 case ALLOCATION_TYPE_DEDICATED:
7460 return 0;
7461 default:
7462 VMA_ASSERT(0);
7463 return 0;
7464 }
7465 }
7466
GetMemory()7467 VkDeviceMemory VmaAllocation_T::GetMemory() const
7468 {
7469 switch(m_Type)
7470 {
7471 case ALLOCATION_TYPE_BLOCK:
7472 return m_BlockAllocation.m_Block->GetDeviceMemory();
7473 case ALLOCATION_TYPE_DEDICATED:
7474 return m_DedicatedAllocation.m_hMemory;
7475 default:
7476 VMA_ASSERT(0);
7477 return VK_NULL_HANDLE;
7478 }
7479 }
7480
GetMappedData()7481 void* VmaAllocation_T::GetMappedData() const
7482 {
7483 switch(m_Type)
7484 {
7485 case ALLOCATION_TYPE_BLOCK:
7486 if(m_MapCount != 0)
7487 {
7488 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7489 VMA_ASSERT(pBlockData != VMA_NULL);
7490 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7491 }
7492 else
7493 {
7494 return VMA_NULL;
7495 }
7496 break;
7497 case ALLOCATION_TYPE_DEDICATED:
7498 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7499 return m_DedicatedAllocation.m_pMappedData;
7500 default:
7501 VMA_ASSERT(0);
7502 return VMA_NULL;
7503 }
7504 }
7505
CanBecomeLost()7506 bool VmaAllocation_T::CanBecomeLost() const
7507 {
7508 switch(m_Type)
7509 {
7510 case ALLOCATION_TYPE_BLOCK:
7511 return m_BlockAllocation.m_CanBecomeLost;
7512 case ALLOCATION_TYPE_DEDICATED:
7513 return false;
7514 default:
7515 VMA_ASSERT(0);
7516 return false;
7517 }
7518 }
7519
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)7520 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7521 {
7522 VMA_ASSERT(CanBecomeLost());
7523
7524 /*
7525 Warning: This is a carefully designed algorithm.
7526 Do not modify unless you really know what you're doing :)
7527 */
7528 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7529 for(;;)
7530 {
7531 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7532 {
7533 VMA_ASSERT(0);
7534 return false;
7535 }
7536 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7537 {
7538 return false;
7539 }
7540 else // Last use time earlier than current time.
7541 {
7542 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7543 {
7544 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7545 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7546 return true;
7547 }
7548 }
7549 }
7550 }
7551
7552 #if VMA_STATS_STRING_ENABLED
7553
7554 // Correspond to values of enum VmaSuballocationType.
7555 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7556 "FREE",
7557 "UNKNOWN",
7558 "BUFFER",
7559 "IMAGE_UNKNOWN",
7560 "IMAGE_LINEAR",
7561 "IMAGE_OPTIMAL",
7562 };
7563
PrintParameters(class VmaJsonWriter & json)7564 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7565 {
7566 json.WriteString("Type");
7567 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7568
7569 json.WriteString("Size");
7570 json.WriteNumber(m_Size);
7571
7572 if(m_pUserData != VMA_NULL)
7573 {
7574 json.WriteString("UserData");
7575 if(IsUserDataString())
7576 {
7577 json.WriteString((const char*)m_pUserData);
7578 }
7579 else
7580 {
7581 json.BeginString();
7582 json.ContinueString_Pointer(m_pUserData);
7583 json.EndString();
7584 }
7585 }
7586
7587 json.WriteString("CreationFrameIndex");
7588 json.WriteNumber(m_CreationFrameIndex);
7589
7590 json.WriteString("LastUseFrameIndex");
7591 json.WriteNumber(GetLastUseFrameIndex());
7592
7593 if(m_BufferImageUsage != 0)
7594 {
7595 json.WriteString("Usage");
7596 json.WriteNumber(m_BufferImageUsage);
7597 }
7598 }
7599
7600 #endif
7601
FreeUserDataString(VmaAllocator hAllocator)7602 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7603 {
7604 VMA_ASSERT(IsUserDataString());
7605 VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
7606 m_pUserData = VMA_NULL;
7607 }
7608
BlockAllocMap()7609 void VmaAllocation_T::BlockAllocMap()
7610 {
7611 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7612
7613 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7614 {
7615 ++m_MapCount;
7616 }
7617 else
7618 {
7619 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7620 }
7621 }
7622
BlockAllocUnmap()7623 void VmaAllocation_T::BlockAllocUnmap()
7624 {
7625 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7626
7627 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7628 {
7629 --m_MapCount;
7630 }
7631 else
7632 {
7633 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7634 }
7635 }
7636
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)7637 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7638 {
7639 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7640
7641 if(m_MapCount != 0)
7642 {
7643 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7644 {
7645 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7646 *ppData = m_DedicatedAllocation.m_pMappedData;
7647 ++m_MapCount;
7648 return VK_SUCCESS;
7649 }
7650 else
7651 {
7652 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7653 return VK_ERROR_MEMORY_MAP_FAILED;
7654 }
7655 }
7656 else
7657 {
7658 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7659 hAllocator->m_hDevice,
7660 m_DedicatedAllocation.m_hMemory,
7661 0, // offset
7662 VK_WHOLE_SIZE,
7663 0, // flags
7664 ppData);
7665 if(result == VK_SUCCESS)
7666 {
7667 m_DedicatedAllocation.m_pMappedData = *ppData;
7668 m_MapCount = 1;
7669 }
7670 return result;
7671 }
7672 }
7673
DedicatedAllocUnmap(VmaAllocator hAllocator)7674 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7675 {
7676 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7677
7678 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7679 {
7680 --m_MapCount;
7681 if(m_MapCount == 0)
7682 {
7683 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7684 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7685 hAllocator->m_hDevice,
7686 m_DedicatedAllocation.m_hMemory);
7687 }
7688 }
7689 else
7690 {
7691 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7692 }
7693 }
7694
7695 #if VMA_STATS_STRING_ENABLED
7696
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)7697 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7698 {
7699 json.BeginObject();
7700
7701 json.WriteString("Blocks");
7702 json.WriteNumber(stat.blockCount);
7703
7704 json.WriteString("Allocations");
7705 json.WriteNumber(stat.allocationCount);
7706
7707 json.WriteString("UnusedRanges");
7708 json.WriteNumber(stat.unusedRangeCount);
7709
7710 json.WriteString("UsedBytes");
7711 json.WriteNumber(stat.usedBytes);
7712
7713 json.WriteString("UnusedBytes");
7714 json.WriteNumber(stat.unusedBytes);
7715
7716 if(stat.allocationCount > 1)
7717 {
7718 json.WriteString("AllocationSize");
7719 json.BeginObject(true);
7720 json.WriteString("Min");
7721 json.WriteNumber(stat.allocationSizeMin);
7722 json.WriteString("Avg");
7723 json.WriteNumber(stat.allocationSizeAvg);
7724 json.WriteString("Max");
7725 json.WriteNumber(stat.allocationSizeMax);
7726 json.EndObject();
7727 }
7728
7729 if(stat.unusedRangeCount > 1)
7730 {
7731 json.WriteString("UnusedRangeSize");
7732 json.BeginObject(true);
7733 json.WriteString("Min");
7734 json.WriteNumber(stat.unusedRangeSizeMin);
7735 json.WriteString("Avg");
7736 json.WriteNumber(stat.unusedRangeSizeAvg);
7737 json.WriteString("Max");
7738 json.WriteNumber(stat.unusedRangeSizeMax);
7739 json.EndObject();
7740 }
7741
7742 json.EndObject();
7743 }
7744
7745 #endif // #if VMA_STATS_STRING_ENABLED
7746
7747 struct VmaSuballocationItemSizeLess
7748 {
operatorVmaSuballocationItemSizeLess7749 bool operator()(
7750 const VmaSuballocationList::iterator lhs,
7751 const VmaSuballocationList::iterator rhs) const
7752 {
7753 return lhs->size < rhs->size;
7754 }
operatorVmaSuballocationItemSizeLess7755 bool operator()(
7756 const VmaSuballocationList::iterator lhs,
7757 VkDeviceSize rhsSize) const
7758 {
7759 return lhs->size < rhsSize;
7760 }
7761 };
7762
7763
7764 ////////////////////////////////////////////////////////////////////////////////
7765 // class VmaBlockMetadata
7766
VmaBlockMetadata(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)7767 VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
7768 m_Size(0),
7769 m_pAllocationCallbacks(pAllocationCallbacks),
7770 m_IsVirtual(isVirtual)
7771 {
7772 }
7773
7774 #if VMA_STATS_STRING_ENABLED
7775
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)7776 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7777 VkDeviceSize unusedBytes,
7778 size_t allocationCount,
7779 size_t unusedRangeCount) const
7780 {
7781 json.BeginObject();
7782
7783 json.WriteString("TotalBytes");
7784 json.WriteNumber(GetSize());
7785
7786 json.WriteString("UnusedBytes");
7787 json.WriteNumber(unusedBytes);
7788
7789 json.WriteString("Allocations");
7790 json.WriteNumber((uint64_t)allocationCount);
7791
7792 json.WriteString("UnusedRanges");
7793 json.WriteNumber((uint64_t)unusedRangeCount);
7794
7795 json.WriteString("Suballocations");
7796 json.BeginArray();
7797 }
7798
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size,void * userData)7799 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7800 VkDeviceSize offset, VkDeviceSize size, void* userData) const
7801 {
7802 json.BeginObject(true);
7803
7804 json.WriteString("Offset");
7805 json.WriteNumber(offset);
7806
7807 if(IsVirtual())
7808 {
7809 json.WriteString("Type");
7810 json.WriteString("VirtualAllocation");
7811
7812 json.WriteString("Size");
7813 json.WriteNumber(size);
7814
7815 if(userData != VMA_NULL)
7816 {
7817 json.WriteString("UserData");
7818 json.BeginString();
7819 json.ContinueString_Pointer(userData);
7820 json.EndString();
7821 }
7822 }
7823 else
7824 {
7825 ((VmaAllocation)userData)->PrintParameters(json);
7826 }
7827
7828 json.EndObject();
7829 }
7830
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)7831 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7832 VkDeviceSize offset,
7833 VkDeviceSize size) const
7834 {
7835 json.BeginObject(true);
7836
7837 json.WriteString("Offset");
7838 json.WriteNumber(offset);
7839
7840 json.WriteString("Type");
7841 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7842
7843 json.WriteString("Size");
7844 json.WriteNumber(size);
7845
7846 json.EndObject();
7847 }
7848
PrintDetailedMap_End(class VmaJsonWriter & json)7849 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7850 {
7851 json.EndArray();
7852 json.EndObject();
7853 }
7854
7855 #endif // #if VMA_STATS_STRING_ENABLED
7856
7857 ////////////////////////////////////////////////////////////////////////////////
7858 // class VmaBlockMetadata_Generic
7859
VmaBlockMetadata_Generic(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)7860 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
7861 VmaBlockMetadata(pAllocationCallbacks, isVirtual),
7862 m_FreeCount(0),
7863 m_SumFreeSize(0),
7864 m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7865 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks))
7866 {
7867 }
7868
~VmaBlockMetadata_Generic()7869 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7870 {
7871 }
7872
Init(VkDeviceSize size)7873 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7874 {
7875 VmaBlockMetadata::Init(size);
7876
7877 m_FreeCount = 1;
7878 m_SumFreeSize = size;
7879
7880 VmaSuballocation suballoc = {};
7881 suballoc.offset = 0;
7882 suballoc.size = size;
7883 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7884
7885 m_Suballocations.push_back(suballoc);
7886 m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
7887 }
7888
Validate()7889 bool VmaBlockMetadata_Generic::Validate() const
7890 {
7891 VMA_VALIDATE(!m_Suballocations.empty());
7892
7893 // Expected offset of new suballocation as calculated from previous ones.
7894 VkDeviceSize calculatedOffset = 0;
7895 // Expected number of free suballocations as calculated from traversing their list.
7896 uint32_t calculatedFreeCount = 0;
7897 // Expected sum size of free suballocations as calculated from traversing their list.
7898 VkDeviceSize calculatedSumFreeSize = 0;
7899 // Expected number of free suballocations that should be registered in
7900 // m_FreeSuballocationsBySize calculated from traversing their list.
7901 size_t freeSuballocationsToRegister = 0;
7902 // True if previous visited suballocation was free.
7903 bool prevFree = false;
7904
7905 const VkDeviceSize debugMargin = GetDebugMargin();
7906
7907 for(const auto& subAlloc : m_Suballocations)
7908 {
7909 // Actual offset of this suballocation doesn't match expected one.
7910 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7911
7912 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7913 // Two adjacent free suballocations are invalid. They should be merged.
7914 VMA_VALIDATE(!prevFree || !currFree);
7915
7916 VmaAllocation alloc = (VmaAllocation)subAlloc.userData;
7917 if(!IsVirtual())
7918 {
7919 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7920 }
7921
7922 if(currFree)
7923 {
7924 calculatedSumFreeSize += subAlloc.size;
7925 ++calculatedFreeCount;
7926 ++freeSuballocationsToRegister;
7927
7928 // Margin required between allocations - every free space must be at least that large.
7929 VMA_VALIDATE(subAlloc.size >= debugMargin);
7930 }
7931 else
7932 {
7933 if(!IsVirtual())
7934 {
7935 VMA_VALIDATE(alloc->GetOffset() == subAlloc.offset);
7936 VMA_VALIDATE(alloc->GetSize() == subAlloc.size);
7937 }
7938
7939 // Margin required between allocations - previous allocation must be free.
7940 VMA_VALIDATE(debugMargin == 0 || prevFree);
7941 }
7942
7943 calculatedOffset += subAlloc.size;
7944 prevFree = currFree;
7945 }
7946
7947 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7948 // match expected one.
7949 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7950
7951 VkDeviceSize lastSize = 0;
7952 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7953 {
7954 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7955
7956 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7957 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7958 // They must be sorted by size ascending.
7959 VMA_VALIDATE(suballocItem->size >= lastSize);
7960
7961 lastSize = suballocItem->size;
7962 }
7963
7964 // Check if totals match calculated values.
7965 VMA_VALIDATE(ValidateFreeSuballocationList());
7966 VMA_VALIDATE(calculatedOffset == GetSize());
7967 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7968 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7969
7970 return true;
7971 }
7972
GetUnusedRangeSizeMax()7973 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7974 {
7975 if(!m_FreeSuballocationsBySize.empty())
7976 {
7977 return m_FreeSuballocationsBySize.back()->size;
7978 }
7979 else
7980 {
7981 return 0;
7982 }
7983 }
7984
IsEmpty()7985 bool VmaBlockMetadata_Generic::IsEmpty() const
7986 {
7987 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7988 }
7989
CalcAllocationStatInfo(VmaStatInfo & outInfo)7990 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7991 {
7992 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7993 VmaInitStatInfo(outInfo);
7994 outInfo.blockCount = 1;
7995
7996 for(const auto& suballoc : m_Suballocations)
7997 {
7998 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7999 {
8000 VmaAddStatInfoAllocation(outInfo, suballoc.size);
8001 }
8002 else
8003 {
8004 VmaAddStatInfoUnusedRange(outInfo, suballoc.size);
8005 }
8006 }
8007 }
8008
AddPoolStats(VmaPoolStats & inoutStats)8009 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8010 {
8011 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8012
8013 inoutStats.size += GetSize();
8014 inoutStats.unusedSize += m_SumFreeSize;
8015 inoutStats.allocationCount += rangeCount - m_FreeCount;
8016 inoutStats.unusedRangeCount += m_FreeCount;
8017 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8018 }
8019
8020 #if VMA_STATS_STRING_ENABLED
8021
PrintDetailedMap(class VmaJsonWriter & json)8022 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8023 {
8024 PrintDetailedMap_Begin(json,
8025 m_SumFreeSize, // unusedBytes
8026 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
8027 m_FreeCount); // unusedRangeCount
8028
8029 for(const auto& suballoc : m_Suballocations)
8030 {
8031 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
8032 {
8033 PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
8034 }
8035 else
8036 {
8037 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8038 }
8039 }
8040
8041 PrintDetailedMap_End(json);
8042 }
8043
8044 #endif // #if VMA_STATS_STRING_ENABLED
8045
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8046 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
8047 uint32_t currentFrameIndex,
8048 uint32_t frameInUseCount,
8049 VkDeviceSize bufferImageGranularity,
8050 VkDeviceSize allocSize,
8051 VkDeviceSize allocAlignment,
8052 bool upperAddress,
8053 VmaSuballocationType allocType,
8054 bool canMakeOtherLost,
8055 uint32_t strategy,
8056 VmaAllocationRequest* pAllocationRequest)
8057 {
8058 VMA_ASSERT(allocSize > 0);
8059 VMA_ASSERT(!upperAddress);
8060 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8061 VMA_ASSERT(pAllocationRequest != VMA_NULL);
8062 VMA_HEAVY_ASSERT(Validate());
8063
8064 allocSize = AlignAllocationSize(allocSize);
8065
8066 pAllocationRequest->type = VmaAllocationRequestType::Normal;
8067 pAllocationRequest->size = allocSize;
8068
8069 const VkDeviceSize debugMargin = GetDebugMargin();
8070
8071 // There is not enough total free space in this block to fulfill the request: Early return.
8072 if(canMakeOtherLost == false &&
8073 m_SumFreeSize < allocSize + 2 * debugMargin)
8074 {
8075 return false;
8076 }
8077
8078 // New algorithm, efficiently searching freeSuballocationsBySize.
8079 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
8080 if(freeSuballocCount > 0)
8081 {
8082 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
8083 {
8084 // Find first free suballocation with size not less than allocSize + 2 * debugMargin.
8085 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8086 m_FreeSuballocationsBySize.data(),
8087 m_FreeSuballocationsBySize.data() + freeSuballocCount,
8088 allocSize + 2 * debugMargin,
8089 VmaSuballocationItemSizeLess());
8090 size_t index = it - m_FreeSuballocationsBySize.data();
8091 for(; index < freeSuballocCount; ++index)
8092 {
8093 if(CheckAllocation(
8094 currentFrameIndex,
8095 frameInUseCount,
8096 bufferImageGranularity,
8097 allocSize,
8098 allocAlignment,
8099 allocType,
8100 m_FreeSuballocationsBySize[index],
8101 false, // canMakeOtherLost
8102 &pAllocationRequest->offset,
8103 &pAllocationRequest->itemsToMakeLostCount,
8104 &pAllocationRequest->sumFreeSize,
8105 &pAllocationRequest->sumItemSize))
8106 {
8107 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
8108 return true;
8109 }
8110 }
8111 }
8112 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
8113 {
8114 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8115 it != m_Suballocations.end();
8116 ++it)
8117 {
8118 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
8119 currentFrameIndex,
8120 frameInUseCount,
8121 bufferImageGranularity,
8122 allocSize,
8123 allocAlignment,
8124 allocType,
8125 it,
8126 false, // canMakeOtherLost
8127 &pAllocationRequest->offset,
8128 &pAllocationRequest->itemsToMakeLostCount,
8129 &pAllocationRequest->sumFreeSize,
8130 &pAllocationRequest->sumItemSize))
8131 {
8132 pAllocationRequest->item = it;
8133 return true;
8134 }
8135 }
8136 }
8137 else // WORST_FIT, FIRST_FIT
8138 {
8139 // Search staring from biggest suballocations.
8140 for(size_t index = freeSuballocCount; index--; )
8141 {
8142 if(CheckAllocation(
8143 currentFrameIndex,
8144 frameInUseCount,
8145 bufferImageGranularity,
8146 allocSize,
8147 allocAlignment,
8148 allocType,
8149 m_FreeSuballocationsBySize[index],
8150 false, // canMakeOtherLost
8151 &pAllocationRequest->offset,
8152 &pAllocationRequest->itemsToMakeLostCount,
8153 &pAllocationRequest->sumFreeSize,
8154 &pAllocationRequest->sumItemSize))
8155 {
8156 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
8157 return true;
8158 }
8159 }
8160 }
8161 }
8162
8163 if(canMakeOtherLost)
8164 {
8165 VMA_ASSERT(!IsVirtual());
8166 // Brute-force algorithm. TODO: Come up with something better.
8167
8168 bool found = false;
8169 VmaAllocationRequest tmpAllocRequest = {};
8170 tmpAllocRequest.type = VmaAllocationRequestType::Normal;
8171 tmpAllocRequest.size = allocSize;
8172 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
8173 suballocIt != m_Suballocations.end();
8174 ++suballocIt)
8175 {
8176 VmaAllocation const alloc = (VmaAllocation)suballocIt->userData;
8177 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
8178 alloc->CanBecomeLost())
8179 {
8180 if(CheckAllocation(
8181 currentFrameIndex,
8182 frameInUseCount,
8183 bufferImageGranularity,
8184 allocSize,
8185 allocAlignment,
8186 allocType,
8187 suballocIt,
8188 canMakeOtherLost,
8189 &tmpAllocRequest.offset,
8190 &tmpAllocRequest.itemsToMakeLostCount,
8191 &tmpAllocRequest.sumFreeSize,
8192 &tmpAllocRequest.sumItemSize))
8193 {
8194 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
8195 {
8196 *pAllocationRequest = tmpAllocRequest;
8197 pAllocationRequest->item = suballocIt;
8198 break;
8199 }
8200 if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
8201 {
8202 *pAllocationRequest = tmpAllocRequest;
8203 pAllocationRequest->item = suballocIt;
8204 found = true;
8205 }
8206 }
8207 }
8208 }
8209
8210 return found;
8211 }
8212
8213 return false;
8214 }
8215
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)8216 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
8217 uint32_t currentFrameIndex,
8218 uint32_t frameInUseCount,
8219 VmaAllocationRequest* pAllocationRequest)
8220 {
8221 VMA_ASSERT(!IsVirtual());
8222 VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
8223
8224 while(pAllocationRequest->itemsToMakeLostCount > 0)
8225 {
8226 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
8227 {
8228 ++pAllocationRequest->item;
8229 }
8230 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8231 VmaAllocation const alloc = (VmaAllocation)pAllocationRequest->item->userData;
8232 VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());
8233 if(alloc->MakeLost(currentFrameIndex, frameInUseCount))
8234 {
8235 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
8236 --pAllocationRequest->itemsToMakeLostCount;
8237 }
8238 else
8239 {
8240 return false;
8241 }
8242 }
8243
8244 VMA_HEAVY_ASSERT(Validate());
8245 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8246 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8247
8248 return true;
8249 }
8250
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8251 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8252 {
8253 VMA_ASSERT(!IsVirtual());
8254 uint32_t lostAllocationCount = 0;
8255 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8256 it != m_Suballocations.end();
8257 ++it)
8258 {
8259 VmaAllocation const alloc = (VmaAllocation)it->userData;
8260 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8261 alloc->CanBecomeLost() &&
8262 alloc->MakeLost(currentFrameIndex, frameInUseCount))
8263 {
8264 it = FreeSuballocation(it);
8265 ++lostAllocationCount;
8266 }
8267 }
8268 return lostAllocationCount;
8269 }
8270
CheckCorruption(const void * pBlockData)8271 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8272 {
8273 for(auto& suballoc : m_Suballocations)
8274 {
8275 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8276 {
8277 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
8278 {
8279 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8280 return VK_ERROR_UNKNOWN;
8281 }
8282 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8283 {
8284 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8285 return VK_ERROR_UNKNOWN;
8286 }
8287 }
8288 }
8289
8290 return VK_SUCCESS;
8291 }
8292
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)8293 void VmaBlockMetadata_Generic::Alloc(
8294 const VmaAllocationRequest& request,
8295 VmaSuballocationType type,
8296 void* userData)
8297 {
8298 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
8299 VMA_ASSERT(request.item != m_Suballocations.end());
8300 VmaSuballocation& suballoc = *request.item;
8301 // Given suballocation is a free block.
8302 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8303 // Given offset is inside this suballocation.
8304 VMA_ASSERT(request.offset >= suballoc.offset);
8305 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8306 VMA_ASSERT(suballoc.size >= paddingBegin + request.size);
8307 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;
8308
8309 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8310 // it to become used.
8311 UnregisterFreeSuballocation(request.item);
8312
8313 suballoc.offset = request.offset;
8314 suballoc.size = request.size;
8315 suballoc.type = type;
8316 suballoc.userData = userData;
8317
8318 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8319 if(paddingEnd)
8320 {
8321 VmaSuballocation paddingSuballoc = {};
8322 paddingSuballoc.offset = request.offset + request.size;
8323 paddingSuballoc.size = paddingEnd;
8324 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8325 VmaSuballocationList::iterator next = request.item;
8326 ++next;
8327 const VmaSuballocationList::iterator paddingEndItem =
8328 m_Suballocations.insert(next, paddingSuballoc);
8329 RegisterFreeSuballocation(paddingEndItem);
8330 }
8331
8332 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8333 if(paddingBegin)
8334 {
8335 VmaSuballocation paddingSuballoc = {};
8336 paddingSuballoc.offset = request.offset - paddingBegin;
8337 paddingSuballoc.size = paddingBegin;
8338 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8339 const VmaSuballocationList::iterator paddingBeginItem =
8340 m_Suballocations.insert(request.item, paddingSuballoc);
8341 RegisterFreeSuballocation(paddingBeginItem);
8342 }
8343
8344 // Update totals.
8345 m_FreeCount = m_FreeCount - 1;
8346 if(paddingBegin > 0)
8347 {
8348 ++m_FreeCount;
8349 }
8350 if(paddingEnd > 0)
8351 {
8352 ++m_FreeCount;
8353 }
8354 m_SumFreeSize -= request.size;
8355 }
8356
FreeAtOffset(VkDeviceSize offset)8357 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8358 {
8359 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8360 suballocItem != m_Suballocations.end();
8361 ++suballocItem)
8362 {
8363 VmaSuballocation& suballoc = *suballocItem;
8364 if(suballoc.offset == offset)
8365 {
8366 FreeSuballocation(suballocItem);
8367 return;
8368 }
8369 }
8370 VMA_ASSERT(0 && "Not found!");
8371 }
8372
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)8373 void VmaBlockMetadata_Generic::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
8374 {
8375 for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.begin();
8376 suballocItem != m_Suballocations.end();
8377 ++suballocItem)
8378 {
8379 const VmaSuballocation& suballoc = *suballocItem;
8380 if (suballoc.offset == offset)
8381 {
8382 outInfo.size = suballoc.size;
8383 outInfo.pUserData = suballoc.userData;
8384 return;
8385 }
8386 }
8387 VMA_ASSERT(0 && "Not found!");
8388 }
8389
Clear()8390 void VmaBlockMetadata_Generic::Clear()
8391 {
8392 const VkDeviceSize size = GetSize();
8393
8394 VMA_ASSERT(IsVirtual());
8395 m_FreeCount = 1;
8396 m_SumFreeSize = size;
8397 m_Suballocations.clear();
8398 m_FreeSuballocationsBySize.clear();
8399
8400 VmaSuballocation suballoc = {};
8401 suballoc.offset = 0;
8402 suballoc.size = size;
8403 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8404 m_Suballocations.push_back(suballoc);
8405
8406 m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
8407 }
8408
SetAllocationUserData(VkDeviceSize offset,void * userData)8409 void VmaBlockMetadata_Generic::SetAllocationUserData(VkDeviceSize offset, void* userData)
8410 {
8411 for (VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8412 suballocItem != m_Suballocations.end();
8413 ++suballocItem)
8414 {
8415 VmaSuballocation& suballoc = *suballocItem;
8416 if (suballoc.offset == offset)
8417 {
8418 suballoc.userData = userData;
8419 return;
8420 }
8421 }
8422 VMA_ASSERT(0 && "Not found!");
8423 }
8424
ValidateFreeSuballocationList()8425 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8426 {
8427 VkDeviceSize lastSize = 0;
8428 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8429 {
8430 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8431
8432 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8433 VMA_VALIDATE(it->size >= lastSize);
8434 lastSize = it->size;
8435 }
8436 return true;
8437 }
8438
CheckAllocation(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaSuballocationList::const_iterator suballocItem,bool canMakeOtherLost,VkDeviceSize * pOffset,size_t * itemsToMakeLostCount,VkDeviceSize * pSumFreeSize,VkDeviceSize * pSumItemSize)8439 bool VmaBlockMetadata_Generic::CheckAllocation(
8440 uint32_t currentFrameIndex,
8441 uint32_t frameInUseCount,
8442 VkDeviceSize bufferImageGranularity,
8443 VkDeviceSize allocSize,
8444 VkDeviceSize allocAlignment,
8445 VmaSuballocationType allocType,
8446 VmaSuballocationList::const_iterator suballocItem,
8447 bool canMakeOtherLost,
8448 VkDeviceSize* pOffset,
8449 size_t* itemsToMakeLostCount,
8450 VkDeviceSize* pSumFreeSize,
8451 VkDeviceSize* pSumItemSize) const
8452 {
8453 VMA_ASSERT(allocSize > 0);
8454 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8455 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8456 VMA_ASSERT(pOffset != VMA_NULL);
8457
8458 *itemsToMakeLostCount = 0;
8459 *pSumFreeSize = 0;
8460 *pSumItemSize = 0;
8461
8462 const VkDeviceSize debugMargin = GetDebugMargin();
8463
8464 if(canMakeOtherLost)
8465 {
8466 VMA_ASSERT(!IsVirtual());
8467 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8468 {
8469 *pSumFreeSize = suballocItem->size;
8470 }
8471 else
8472 {
8473 VmaAllocation const alloc = (VmaAllocation)suballocItem->userData;
8474 if(alloc->CanBecomeLost() &&
8475 alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8476 {
8477 ++*itemsToMakeLostCount;
8478 *pSumItemSize = suballocItem->size;
8479 }
8480 else
8481 {
8482 return false;
8483 }
8484 }
8485
8486 // Remaining size is too small for this request: Early return.
8487 if(GetSize() - suballocItem->offset < allocSize)
8488 {
8489 return false;
8490 }
8491
8492 // Start from offset equal to beginning of this suballocation.
8493 *pOffset = suballocItem->offset;
8494
8495 // Apply debugMargin at the beginning.
8496 if(debugMargin > 0)
8497 {
8498 *pOffset += debugMargin;
8499 }
8500
8501 // Apply alignment.
8502 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8503
8504 // Check previous suballocations for BufferImageGranularity conflicts.
8505 // Make bigger alignment if necessary.
8506 if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
8507 {
8508 bool bufferImageGranularityConflict = false;
8509 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8510 while(prevSuballocItem != m_Suballocations.cbegin())
8511 {
8512 --prevSuballocItem;
8513 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8514 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8515 {
8516 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8517 {
8518 bufferImageGranularityConflict = true;
8519 break;
8520 }
8521 }
8522 else
8523 // Already on previous page.
8524 break;
8525 }
8526 if(bufferImageGranularityConflict)
8527 {
8528 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8529 }
8530 }
8531
8532 // Now that we have final *pOffset, check if we are past suballocItem.
8533 // If yes, return false - this function should be called for another suballocItem as starting point.
8534 if(*pOffset >= suballocItem->offset + suballocItem->size)
8535 {
8536 return false;
8537 }
8538
8539 // Calculate padding at the beginning based on current offset.
8540 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8541
8542 // Calculate required margin at the end.
8543 const VkDeviceSize requiredEndMargin = debugMargin;
8544
8545 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8546 // Another early return check.
8547 if(suballocItem->offset + totalSize > GetSize())
8548 {
8549 return false;
8550 }
8551
8552 // Advance lastSuballocItem until desired size is reached.
8553 // Update itemsToMakeLostCount.
8554 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8555 if(totalSize > suballocItem->size)
8556 {
8557 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8558 while(remainingSize > 0)
8559 {
8560 ++lastSuballocItem;
8561 if(lastSuballocItem == m_Suballocations.cend())
8562 {
8563 return false;
8564 }
8565 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8566 {
8567 *pSumFreeSize += lastSuballocItem->size;
8568 }
8569 else
8570 {
8571 VmaAllocation const lastSuballocAlloc = (VmaAllocation)lastSuballocItem->userData;
8572 VMA_ASSERT(lastSuballocAlloc != VK_NULL_HANDLE);
8573 if(lastSuballocAlloc->CanBecomeLost() &&
8574 lastSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8575 {
8576 ++*itemsToMakeLostCount;
8577 *pSumItemSize += lastSuballocItem->size;
8578 }
8579 else
8580 {
8581 return false;
8582 }
8583 }
8584 remainingSize = (lastSuballocItem->size < remainingSize) ?
8585 remainingSize - lastSuballocItem->size : 0;
8586 }
8587 }
8588
8589 // Check next suballocations for BufferImageGranularity conflicts.
8590 // If conflict exists, we must mark more allocations lost or fail.
8591 if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
8592 {
8593 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8594 ++nextSuballocItem;
8595 while(nextSuballocItem != m_Suballocations.cend())
8596 {
8597 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8598 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8599 {
8600 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8601 {
8602 VmaAllocation const nextSuballocAlloc = (VmaAllocation)nextSuballoc.userData;
8603 VMA_ASSERT(nextSuballocAlloc != VK_NULL_HANDLE);
8604 if(nextSuballocAlloc->CanBecomeLost() &&
8605 nextSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8606 {
8607 ++*itemsToMakeLostCount;
8608 }
8609 else
8610 {
8611 return false;
8612 }
8613 }
8614 }
8615 else
8616 {
8617 // Already on next page.
8618 break;
8619 }
8620 ++nextSuballocItem;
8621 }
8622 }
8623 }
8624 else
8625 {
8626 const VmaSuballocation& suballoc = *suballocItem;
8627 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8628
8629 *pSumFreeSize = suballoc.size;
8630
8631 // Size of this suballocation is too small for this request: Early return.
8632 if(suballoc.size < allocSize)
8633 {
8634 return false;
8635 }
8636
8637 // Start from offset equal to beginning of this suballocation.
8638 *pOffset = suballoc.offset;
8639
8640 // Apply debugMargin at the beginning.
8641 if(debugMargin > 0)
8642 {
8643 *pOffset += debugMargin;
8644 }
8645
8646 // Apply alignment.
8647 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8648
8649 // Check previous suballocations for BufferImageGranularity conflicts.
8650 // Make bigger alignment if necessary.
8651 if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
8652 {
8653 bool bufferImageGranularityConflict = false;
8654 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8655 while(prevSuballocItem != m_Suballocations.cbegin())
8656 {
8657 --prevSuballocItem;
8658 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8659 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8660 {
8661 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8662 {
8663 bufferImageGranularityConflict = true;
8664 break;
8665 }
8666 }
8667 else
8668 // Already on previous page.
8669 break;
8670 }
8671 if(bufferImageGranularityConflict)
8672 {
8673 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8674 }
8675 }
8676
8677 // Calculate padding at the beginning based on current offset.
8678 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8679
8680 // Calculate required margin at the end.
8681 const VkDeviceSize requiredEndMargin = debugMargin;
8682
8683 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8684 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8685 {
8686 return false;
8687 }
8688
8689 // Check next suballocations for BufferImageGranularity conflicts.
8690 // If conflict exists, allocation cannot be made here.
8691 if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
8692 {
8693 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8694 ++nextSuballocItem;
8695 while(nextSuballocItem != m_Suballocations.cend())
8696 {
8697 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8698 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8699 {
8700 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8701 {
8702 return false;
8703 }
8704 }
8705 else
8706 {
8707 // Already on next page.
8708 break;
8709 }
8710 ++nextSuballocItem;
8711 }
8712 }
8713 }
8714
8715 // All tests passed: Success. pOffset is already filled.
8716 return true;
8717 }
8718
MergeFreeWithNext(VmaSuballocationList::iterator item)8719 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8720 {
8721 VMA_ASSERT(item != m_Suballocations.end());
8722 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8723
8724 VmaSuballocationList::iterator nextItem = item;
8725 ++nextItem;
8726 VMA_ASSERT(nextItem != m_Suballocations.end());
8727 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8728
8729 item->size += nextItem->size;
8730 --m_FreeCount;
8731 m_Suballocations.erase(nextItem);
8732 }
8733
FreeSuballocation(VmaSuballocationList::iterator suballocItem)8734 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8735 {
8736 // Change this suballocation to be marked as free.
8737 VmaSuballocation& suballoc = *suballocItem;
8738 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8739 suballoc.userData = VMA_NULL;
8740
8741 // Update totals.
8742 ++m_FreeCount;
8743 m_SumFreeSize += suballoc.size;
8744
8745 // Merge with previous and/or next suballocation if it's also free.
8746 bool mergeWithNext = false;
8747 bool mergeWithPrev = false;
8748
8749 VmaSuballocationList::iterator nextItem = suballocItem;
8750 ++nextItem;
8751 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8752 {
8753 mergeWithNext = true;
8754 }
8755
8756 VmaSuballocationList::iterator prevItem = suballocItem;
8757 if(suballocItem != m_Suballocations.begin())
8758 {
8759 --prevItem;
8760 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8761 {
8762 mergeWithPrev = true;
8763 }
8764 }
8765
8766 if(mergeWithNext)
8767 {
8768 UnregisterFreeSuballocation(nextItem);
8769 MergeFreeWithNext(suballocItem);
8770 }
8771
8772 if(mergeWithPrev)
8773 {
8774 UnregisterFreeSuballocation(prevItem);
8775 MergeFreeWithNext(prevItem);
8776 RegisterFreeSuballocation(prevItem);
8777 return prevItem;
8778 }
8779 else
8780 {
8781 RegisterFreeSuballocation(suballocItem);
8782 return suballocItem;
8783 }
8784 }
8785
RegisterFreeSuballocation(VmaSuballocationList::iterator item)8786 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8787 {
8788 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8789 VMA_ASSERT(item->size > 0);
8790
8791 // You may want to enable this validation at the beginning or at the end of
8792 // this function, depending on what do you want to check.
8793 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8794
8795 if(m_FreeSuballocationsBySize.empty())
8796 {
8797 m_FreeSuballocationsBySize.push_back(item);
8798 }
8799 else
8800 {
8801 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8802 }
8803
8804 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8805 }
8806
8807
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)8808 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8809 {
8810 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8811 VMA_ASSERT(item->size > 0);
8812
8813 // You may want to enable this validation at the beginning or at the end of
8814 // this function, depending on what do you want to check.
8815 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8816
8817 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8818 m_FreeSuballocationsBySize.data(),
8819 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8820 item,
8821 VmaSuballocationItemSizeLess());
8822 for(size_t index = it - m_FreeSuballocationsBySize.data();
8823 index < m_FreeSuballocationsBySize.size();
8824 ++index)
8825 {
8826 if(m_FreeSuballocationsBySize[index] == item)
8827 {
8828 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8829 return;
8830 }
8831 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8832 }
8833 VMA_ASSERT(0 && "Not found.");
8834
8835 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8836 }
8837
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)8838 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8839 VkDeviceSize bufferImageGranularity,
8840 VmaSuballocationType& inOutPrevSuballocType) const
8841 {
8842 if(bufferImageGranularity == 1 || IsEmpty() || IsVirtual())
8843 {
8844 return false;
8845 }
8846
8847 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8848 bool typeConflictFound = false;
8849 for(const auto& suballoc : m_Suballocations)
8850 {
8851 const VmaSuballocationType suballocType = suballoc.type;
8852 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8853 {
8854 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8855 minAlignment = VMA_MIN(minAlignment, alloc->GetAlignment());
8856 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8857 {
8858 typeConflictFound = true;
8859 }
8860 inOutPrevSuballocType = suballocType;
8861 }
8862 }
8863
8864 return typeConflictFound || minAlignment >= bufferImageGranularity;
8865 }
8866
8867 ////////////////////////////////////////////////////////////////////////////////
8868 // class VmaBlockMetadata_Linear
8869
VmaBlockMetadata_Linear(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)8870 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
8871 VmaBlockMetadata(pAllocationCallbacks, isVirtual),
8872 m_SumFreeSize(0),
8873 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
8874 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
8875 m_1stVectorIndex(0),
8876 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8877 m_1stNullItemsBeginCount(0),
8878 m_1stNullItemsMiddleCount(0),
8879 m_2ndNullItemsCount(0)
8880 {
8881 }
8882
~VmaBlockMetadata_Linear()8883 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8884 {
8885 }
8886
Init(VkDeviceSize size)8887 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8888 {
8889 VmaBlockMetadata::Init(size);
8890 m_SumFreeSize = size;
8891 }
8892
Validate()8893 bool VmaBlockMetadata_Linear::Validate() const
8894 {
8895 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8896 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8897
8898 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8899 VMA_VALIDATE(!suballocations1st.empty() ||
8900 suballocations2nd.empty() ||
8901 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8902
8903 if(!suballocations1st.empty())
8904 {
8905 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8906 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
8907 // Null item at the end should be just pop_back().
8908 VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
8909 }
8910 if(!suballocations2nd.empty())
8911 {
8912 // Null item at the end should be just pop_back().
8913 VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
8914 }
8915
8916 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8917 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8918
8919 VkDeviceSize sumUsedSize = 0;
8920 const size_t suballoc1stCount = suballocations1st.size();
8921 const VkDeviceSize debugMargin = GetDebugMargin();
8922 VkDeviceSize offset = debugMargin;
8923
8924 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8925 {
8926 const size_t suballoc2ndCount = suballocations2nd.size();
8927 size_t nullItem2ndCount = 0;
8928 for(size_t i = 0; i < suballoc2ndCount; ++i)
8929 {
8930 const VmaSuballocation& suballoc = suballocations2nd[i];
8931 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8932
8933 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8934 if(!IsVirtual())
8935 {
8936 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
8937 }
8938 VMA_VALIDATE(suballoc.offset >= offset);
8939
8940 if(!currFree)
8941 {
8942 if(!IsVirtual())
8943 {
8944 VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
8945 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
8946 }
8947 sumUsedSize += suballoc.size;
8948 }
8949 else
8950 {
8951 ++nullItem2ndCount;
8952 }
8953
8954 offset = suballoc.offset + suballoc.size + debugMargin;
8955 }
8956
8957 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8958 }
8959
8960 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8961 {
8962 const VmaSuballocation& suballoc = suballocations1st[i];
8963 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8964 suballoc.userData == VMA_NULL);
8965 }
8966
8967 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8968
8969 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8970 {
8971 const VmaSuballocation& suballoc = suballocations1st[i];
8972 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8973
8974 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8975 if(!IsVirtual())
8976 {
8977 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
8978 }
8979 VMA_VALIDATE(suballoc.offset >= offset);
8980 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8981
8982 if(!currFree)
8983 {
8984 if(!IsVirtual())
8985 {
8986 VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
8987 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
8988 }
8989 sumUsedSize += suballoc.size;
8990 }
8991 else
8992 {
8993 ++nullItem1stCount;
8994 }
8995
8996 offset = suballoc.offset + suballoc.size + debugMargin;
8997 }
8998 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8999
9000 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9001 {
9002 const size_t suballoc2ndCount = suballocations2nd.size();
9003 size_t nullItem2ndCount = 0;
9004 for(size_t i = suballoc2ndCount; i--; )
9005 {
9006 const VmaSuballocation& suballoc = suballocations2nd[i];
9007 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9008
9009 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
9010 if(!IsVirtual())
9011 {
9012 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
9013 }
9014 VMA_VALIDATE(suballoc.offset >= offset);
9015
9016 if(!currFree)
9017 {
9018 if(!IsVirtual())
9019 {
9020 VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
9021 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
9022 }
9023 sumUsedSize += suballoc.size;
9024 }
9025 else
9026 {
9027 ++nullItem2ndCount;
9028 }
9029
9030 offset = suballoc.offset + suballoc.size + debugMargin;
9031 }
9032
9033 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9034 }
9035
9036 VMA_VALIDATE(offset <= GetSize());
9037 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9038
9039 return true;
9040 }
9041
GetAllocationCount()9042 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9043 {
9044 return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
9045 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9046 }
9047
GetUnusedRangeSizeMax()9048 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9049 {
9050 const VkDeviceSize size = GetSize();
9051
9052 /*
9053 We don't consider gaps inside allocation vectors with freed allocations because
9054 they are not suitable for reuse in linear allocator. We consider only space that
9055 is available for new allocations.
9056 */
9057 if(IsEmpty())
9058 {
9059 return size;
9060 }
9061
9062 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9063
9064 switch(m_2ndVectorMode)
9065 {
9066 case SECOND_VECTOR_EMPTY:
9067 /*
9068 Available space is after end of 1st, as well as before beginning of 1st (which
9069 would make it a ring buffer).
9070 */
9071 {
9072 const size_t suballocations1stCount = suballocations1st.size();
9073 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9074 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9075 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
9076 return VMA_MAX(
9077 firstSuballoc.offset,
9078 size - (lastSuballoc.offset + lastSuballoc.size));
9079 }
9080 break;
9081
9082 case SECOND_VECTOR_RING_BUFFER:
9083 /*
9084 Available space is only between end of 2nd and beginning of 1st.
9085 */
9086 {
9087 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9088 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
9089 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
9090 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
9091 }
9092 break;
9093
9094 case SECOND_VECTOR_DOUBLE_STACK:
9095 /*
9096 Available space is only between end of 1st and top of 2nd.
9097 */
9098 {
9099 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9100 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
9101 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
9102 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
9103 }
9104 break;
9105
9106 default:
9107 VMA_ASSERT(0);
9108 return 0;
9109 }
9110 }
9111
CalcAllocationStatInfo(VmaStatInfo & outInfo)9112 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9113 {
9114 const VkDeviceSize size = GetSize();
9115 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9116 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9117 const size_t suballoc1stCount = suballocations1st.size();
9118 const size_t suballoc2ndCount = suballocations2nd.size();
9119
9120 VmaInitStatInfo(outInfo);
9121 outInfo.blockCount = 1;
9122
9123 VkDeviceSize lastOffset = 0;
9124
9125 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9126 {
9127 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9128 size_t nextAlloc2ndIndex = 0;
9129 while(lastOffset < freeSpace2ndTo1stEnd)
9130 {
9131 // Find next non-null allocation or move nextAllocIndex to the end.
9132 while(nextAlloc2ndIndex < suballoc2ndCount &&
9133 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9134 {
9135 ++nextAlloc2ndIndex;
9136 }
9137
9138 // Found non-null allocation.
9139 if(nextAlloc2ndIndex < suballoc2ndCount)
9140 {
9141 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9142
9143 // 1. Process free space before this allocation.
9144 if(lastOffset < suballoc.offset)
9145 {
9146 // There is free space from lastOffset to suballoc.offset.
9147 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9148 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9149 }
9150
9151 // 2. Process this allocation.
9152 // There is allocation with suballoc.offset, suballoc.size.
9153 VmaAddStatInfoAllocation(outInfo, suballoc.size);
9154
9155 // 3. Prepare for next iteration.
9156 lastOffset = suballoc.offset + suballoc.size;
9157 ++nextAlloc2ndIndex;
9158 }
9159 // We are at the end.
9160 else
9161 {
9162 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9163 if(lastOffset < freeSpace2ndTo1stEnd)
9164 {
9165 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9166 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9167 }
9168
9169 // End of loop.
9170 lastOffset = freeSpace2ndTo1stEnd;
9171 }
9172 }
9173 }
9174
9175 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9176 const VkDeviceSize freeSpace1stTo2ndEnd =
9177 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9178 while(lastOffset < freeSpace1stTo2ndEnd)
9179 {
9180 // Find next non-null allocation or move nextAllocIndex to the end.
9181 while(nextAlloc1stIndex < suballoc1stCount &&
9182 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9183 {
9184 ++nextAlloc1stIndex;
9185 }
9186
9187 // Found non-null allocation.
9188 if(nextAlloc1stIndex < suballoc1stCount)
9189 {
9190 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9191
9192 // 1. Process free space before this allocation.
9193 if(lastOffset < suballoc.offset)
9194 {
9195 // There is free space from lastOffset to suballoc.offset.
9196 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9197 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9198 }
9199
9200 // 2. Process this allocation.
9201 // There is allocation with suballoc.offset, suballoc.size.
9202 VmaAddStatInfoAllocation(outInfo, suballoc.size);
9203
9204 // 3. Prepare for next iteration.
9205 lastOffset = suballoc.offset + suballoc.size;
9206 ++nextAlloc1stIndex;
9207 }
9208 // We are at the end.
9209 else
9210 {
9211 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9212 if(lastOffset < freeSpace1stTo2ndEnd)
9213 {
9214 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9215 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9216 }
9217
9218 // End of loop.
9219 lastOffset = freeSpace1stTo2ndEnd;
9220 }
9221 }
9222
9223 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9224 {
9225 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9226 while(lastOffset < size)
9227 {
9228 // Find next non-null allocation or move nextAllocIndex to the end.
9229 while(nextAlloc2ndIndex != SIZE_MAX &&
9230 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9231 {
9232 --nextAlloc2ndIndex;
9233 }
9234
9235 // Found non-null allocation.
9236 if(nextAlloc2ndIndex != SIZE_MAX)
9237 {
9238 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9239
9240 // 1. Process free space before this allocation.
9241 if(lastOffset < suballoc.offset)
9242 {
9243 // There is free space from lastOffset to suballoc.offset.
9244 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9245 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9246 }
9247
9248 // 2. Process this allocation.
9249 // There is allocation with suballoc.offset, suballoc.size.
9250 VmaAddStatInfoAllocation(outInfo, suballoc.size);
9251
9252 // 3. Prepare for next iteration.
9253 lastOffset = suballoc.offset + suballoc.size;
9254 --nextAlloc2ndIndex;
9255 }
9256 // We are at the end.
9257 else
9258 {
9259 // There is free space from lastOffset to size.
9260 if(lastOffset < size)
9261 {
9262 const VkDeviceSize unusedRangeSize = size - lastOffset;
9263 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9264 }
9265
9266 // End of loop.
9267 lastOffset = size;
9268 }
9269 }
9270 }
9271
9272 outInfo.unusedBytes = size - outInfo.usedBytes;
9273 }
9274
AddPoolStats(VmaPoolStats & inoutStats)9275 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9276 {
9277 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9278 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9279 const VkDeviceSize size = GetSize();
9280 const size_t suballoc1stCount = suballocations1st.size();
9281 const size_t suballoc2ndCount = suballocations2nd.size();
9282
9283 inoutStats.size += size;
9284
9285 VkDeviceSize lastOffset = 0;
9286
9287 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9288 {
9289 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9290 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9291 while(lastOffset < freeSpace2ndTo1stEnd)
9292 {
9293 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9294 while(nextAlloc2ndIndex < suballoc2ndCount &&
9295 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9296 {
9297 ++nextAlloc2ndIndex;
9298 }
9299
9300 // Found non-null allocation.
9301 if(nextAlloc2ndIndex < suballoc2ndCount)
9302 {
9303 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9304
9305 // 1. Process free space before this allocation.
9306 if(lastOffset < suballoc.offset)
9307 {
9308 // There is free space from lastOffset to suballoc.offset.
9309 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9310 inoutStats.unusedSize += unusedRangeSize;
9311 ++inoutStats.unusedRangeCount;
9312 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9313 }
9314
9315 // 2. Process this allocation.
9316 // There is allocation with suballoc.offset, suballoc.size.
9317 ++inoutStats.allocationCount;
9318
9319 // 3. Prepare for next iteration.
9320 lastOffset = suballoc.offset + suballoc.size;
9321 ++nextAlloc2ndIndex;
9322 }
9323 // We are at the end.
9324 else
9325 {
9326 if(lastOffset < freeSpace2ndTo1stEnd)
9327 {
9328 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9329 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9330 inoutStats.unusedSize += unusedRangeSize;
9331 ++inoutStats.unusedRangeCount;
9332 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9333 }
9334
9335 // End of loop.
9336 lastOffset = freeSpace2ndTo1stEnd;
9337 }
9338 }
9339 }
9340
9341 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9342 const VkDeviceSize freeSpace1stTo2ndEnd =
9343 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9344 while(lastOffset < freeSpace1stTo2ndEnd)
9345 {
9346 // Find next non-null allocation or move nextAllocIndex to the end.
9347 while(nextAlloc1stIndex < suballoc1stCount &&
9348 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9349 {
9350 ++nextAlloc1stIndex;
9351 }
9352
9353 // Found non-null allocation.
9354 if(nextAlloc1stIndex < suballoc1stCount)
9355 {
9356 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9357
9358 // 1. Process free space before this allocation.
9359 if(lastOffset < suballoc.offset)
9360 {
9361 // There is free space from lastOffset to suballoc.offset.
9362 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9363 inoutStats.unusedSize += unusedRangeSize;
9364 ++inoutStats.unusedRangeCount;
9365 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9366 }
9367
9368 // 2. Process this allocation.
9369 // There is allocation with suballoc.offset, suballoc.size.
9370 ++inoutStats.allocationCount;
9371
9372 // 3. Prepare for next iteration.
9373 lastOffset = suballoc.offset + suballoc.size;
9374 ++nextAlloc1stIndex;
9375 }
9376 // We are at the end.
9377 else
9378 {
9379 if(lastOffset < freeSpace1stTo2ndEnd)
9380 {
9381 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9382 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9383 inoutStats.unusedSize += unusedRangeSize;
9384 ++inoutStats.unusedRangeCount;
9385 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9386 }
9387
9388 // End of loop.
9389 lastOffset = freeSpace1stTo2ndEnd;
9390 }
9391 }
9392
9393 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9394 {
9395 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9396 while(lastOffset < size)
9397 {
9398 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9399 while(nextAlloc2ndIndex != SIZE_MAX &&
9400 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9401 {
9402 --nextAlloc2ndIndex;
9403 }
9404
9405 // Found non-null allocation.
9406 if(nextAlloc2ndIndex != SIZE_MAX)
9407 {
9408 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9409
9410 // 1. Process free space before this allocation.
9411 if(lastOffset < suballoc.offset)
9412 {
9413 // There is free space from lastOffset to suballoc.offset.
9414 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9415 inoutStats.unusedSize += unusedRangeSize;
9416 ++inoutStats.unusedRangeCount;
9417 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9418 }
9419
9420 // 2. Process this allocation.
9421 // There is allocation with suballoc.offset, suballoc.size.
9422 ++inoutStats.allocationCount;
9423
9424 // 3. Prepare for next iteration.
9425 lastOffset = suballoc.offset + suballoc.size;
9426 --nextAlloc2ndIndex;
9427 }
9428 // We are at the end.
9429 else
9430 {
9431 if(lastOffset < size)
9432 {
9433 // There is free space from lastOffset to size.
9434 const VkDeviceSize unusedRangeSize = size - lastOffset;
9435 inoutStats.unusedSize += unusedRangeSize;
9436 ++inoutStats.unusedRangeCount;
9437 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9438 }
9439
9440 // End of loop.
9441 lastOffset = size;
9442 }
9443 }
9444 }
9445 }
9446
9447 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)9448 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9449 {
9450 const VkDeviceSize size = GetSize();
9451 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9452 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9453 const size_t suballoc1stCount = suballocations1st.size();
9454 const size_t suballoc2ndCount = suballocations2nd.size();
9455
9456 // FIRST PASS
9457
9458 size_t unusedRangeCount = 0;
9459 VkDeviceSize usedBytes = 0;
9460
9461 VkDeviceSize lastOffset = 0;
9462
9463 size_t alloc2ndCount = 0;
9464 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9465 {
9466 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9467 size_t nextAlloc2ndIndex = 0;
9468 while(lastOffset < freeSpace2ndTo1stEnd)
9469 {
9470 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9471 while(nextAlloc2ndIndex < suballoc2ndCount &&
9472 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9473 {
9474 ++nextAlloc2ndIndex;
9475 }
9476
9477 // Found non-null allocation.
9478 if(nextAlloc2ndIndex < suballoc2ndCount)
9479 {
9480 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9481
9482 // 1. Process free space before this allocation.
9483 if(lastOffset < suballoc.offset)
9484 {
9485 // There is free space from lastOffset to suballoc.offset.
9486 ++unusedRangeCount;
9487 }
9488
9489 // 2. Process this allocation.
9490 // There is allocation with suballoc.offset, suballoc.size.
9491 ++alloc2ndCount;
9492 usedBytes += suballoc.size;
9493
9494 // 3. Prepare for next iteration.
9495 lastOffset = suballoc.offset + suballoc.size;
9496 ++nextAlloc2ndIndex;
9497 }
9498 // We are at the end.
9499 else
9500 {
9501 if(lastOffset < freeSpace2ndTo1stEnd)
9502 {
9503 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9504 ++unusedRangeCount;
9505 }
9506
9507 // End of loop.
9508 lastOffset = freeSpace2ndTo1stEnd;
9509 }
9510 }
9511 }
9512
9513 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9514 size_t alloc1stCount = 0;
9515 const VkDeviceSize freeSpace1stTo2ndEnd =
9516 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9517 while(lastOffset < freeSpace1stTo2ndEnd)
9518 {
9519 // Find next non-null allocation or move nextAllocIndex to the end.
9520 while(nextAlloc1stIndex < suballoc1stCount &&
9521 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9522 {
9523 ++nextAlloc1stIndex;
9524 }
9525
9526 // Found non-null allocation.
9527 if(nextAlloc1stIndex < suballoc1stCount)
9528 {
9529 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9530
9531 // 1. Process free space before this allocation.
9532 if(lastOffset < suballoc.offset)
9533 {
9534 // There is free space from lastOffset to suballoc.offset.
9535 ++unusedRangeCount;
9536 }
9537
9538 // 2. Process this allocation.
9539 // There is allocation with suballoc.offset, suballoc.size.
9540 ++alloc1stCount;
9541 usedBytes += suballoc.size;
9542
9543 // 3. Prepare for next iteration.
9544 lastOffset = suballoc.offset + suballoc.size;
9545 ++nextAlloc1stIndex;
9546 }
9547 // We are at the end.
9548 else
9549 {
9550 if(lastOffset < size)
9551 {
9552 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9553 ++unusedRangeCount;
9554 }
9555
9556 // End of loop.
9557 lastOffset = freeSpace1stTo2ndEnd;
9558 }
9559 }
9560
9561 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9562 {
9563 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9564 while(lastOffset < size)
9565 {
9566 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9567 while(nextAlloc2ndIndex != SIZE_MAX &&
9568 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9569 {
9570 --nextAlloc2ndIndex;
9571 }
9572
9573 // Found non-null allocation.
9574 if(nextAlloc2ndIndex != SIZE_MAX)
9575 {
9576 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9577
9578 // 1. Process free space before this allocation.
9579 if(lastOffset < suballoc.offset)
9580 {
9581 // There is free space from lastOffset to suballoc.offset.
9582 ++unusedRangeCount;
9583 }
9584
9585 // 2. Process this allocation.
9586 // There is allocation with suballoc.offset, suballoc.size.
9587 ++alloc2ndCount;
9588 usedBytes += suballoc.size;
9589
9590 // 3. Prepare for next iteration.
9591 lastOffset = suballoc.offset + suballoc.size;
9592 --nextAlloc2ndIndex;
9593 }
9594 // We are at the end.
9595 else
9596 {
9597 if(lastOffset < size)
9598 {
9599 // There is free space from lastOffset to size.
9600 ++unusedRangeCount;
9601 }
9602
9603 // End of loop.
9604 lastOffset = size;
9605 }
9606 }
9607 }
9608
9609 const VkDeviceSize unusedBytes = size - usedBytes;
9610 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9611
9612 // SECOND PASS
9613 lastOffset = 0;
9614
9615 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9616 {
9617 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9618 size_t nextAlloc2ndIndex = 0;
9619 while(lastOffset < freeSpace2ndTo1stEnd)
9620 {
9621 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9622 while(nextAlloc2ndIndex < suballoc2ndCount &&
9623 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9624 {
9625 ++nextAlloc2ndIndex;
9626 }
9627
9628 // Found non-null allocation.
9629 if(nextAlloc2ndIndex < suballoc2ndCount)
9630 {
9631 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9632
9633 // 1. Process free space before this allocation.
9634 if(lastOffset < suballoc.offset)
9635 {
9636 // There is free space from lastOffset to suballoc.offset.
9637 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9638 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9639 }
9640
9641 // 2. Process this allocation.
9642 // There is allocation with suballoc.offset, suballoc.size.
9643 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9644
9645 // 3. Prepare for next iteration.
9646 lastOffset = suballoc.offset + suballoc.size;
9647 ++nextAlloc2ndIndex;
9648 }
9649 // We are at the end.
9650 else
9651 {
9652 if(lastOffset < freeSpace2ndTo1stEnd)
9653 {
9654 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9655 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9656 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9657 }
9658
9659 // End of loop.
9660 lastOffset = freeSpace2ndTo1stEnd;
9661 }
9662 }
9663 }
9664
9665 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9666 while(lastOffset < freeSpace1stTo2ndEnd)
9667 {
9668 // Find next non-null allocation or move nextAllocIndex to the end.
9669 while(nextAlloc1stIndex < suballoc1stCount &&
9670 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9671 {
9672 ++nextAlloc1stIndex;
9673 }
9674
9675 // Found non-null allocation.
9676 if(nextAlloc1stIndex < suballoc1stCount)
9677 {
9678 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9679
9680 // 1. Process free space before this allocation.
9681 if(lastOffset < suballoc.offset)
9682 {
9683 // There is free space from lastOffset to suballoc.offset.
9684 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9685 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9686 }
9687
9688 // 2. Process this allocation.
9689 // There is allocation with suballoc.offset, suballoc.size.
9690 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9691
9692 // 3. Prepare for next iteration.
9693 lastOffset = suballoc.offset + suballoc.size;
9694 ++nextAlloc1stIndex;
9695 }
9696 // We are at the end.
9697 else
9698 {
9699 if(lastOffset < freeSpace1stTo2ndEnd)
9700 {
9701 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9702 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9703 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9704 }
9705
9706 // End of loop.
9707 lastOffset = freeSpace1stTo2ndEnd;
9708 }
9709 }
9710
9711 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9712 {
9713 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9714 while(lastOffset < size)
9715 {
9716 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9717 while(nextAlloc2ndIndex != SIZE_MAX &&
9718 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9719 {
9720 --nextAlloc2ndIndex;
9721 }
9722
9723 // Found non-null allocation.
9724 if(nextAlloc2ndIndex != SIZE_MAX)
9725 {
9726 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9727
9728 // 1. Process free space before this allocation.
9729 if(lastOffset < suballoc.offset)
9730 {
9731 // There is free space from lastOffset to suballoc.offset.
9732 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9733 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9734 }
9735
9736 // 2. Process this allocation.
9737 // There is allocation with suballoc.offset, suballoc.size.
9738 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9739
9740 // 3. Prepare for next iteration.
9741 lastOffset = suballoc.offset + suballoc.size;
9742 --nextAlloc2ndIndex;
9743 }
9744 // We are at the end.
9745 else
9746 {
9747 if(lastOffset < size)
9748 {
9749 // There is free space from lastOffset to size.
9750 const VkDeviceSize unusedRangeSize = size - lastOffset;
9751 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9752 }
9753
9754 // End of loop.
9755 lastOffset = size;
9756 }
9757 }
9758 }
9759
9760 PrintDetailedMap_End(json);
9761 }
9762 #endif // #if VMA_STATS_STRING_ENABLED
9763
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9764 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9765 uint32_t currentFrameIndex,
9766 uint32_t frameInUseCount,
9767 VkDeviceSize bufferImageGranularity,
9768 VkDeviceSize allocSize,
9769 VkDeviceSize allocAlignment,
9770 bool upperAddress,
9771 VmaSuballocationType allocType,
9772 bool canMakeOtherLost,
9773 uint32_t strategy,
9774 VmaAllocationRequest* pAllocationRequest)
9775 {
9776 VMA_ASSERT(allocSize > 0);
9777 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9778 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9779 VMA_HEAVY_ASSERT(Validate());
9780 pAllocationRequest->size = allocSize;
9781 return upperAddress ?
9782 CreateAllocationRequest_UpperAddress(
9783 currentFrameIndex, frameInUseCount, bufferImageGranularity,
9784 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
9785 CreateAllocationRequest_LowerAddress(
9786 currentFrameIndex, frameInUseCount, bufferImageGranularity,
9787 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
9788 }
9789
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9790 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
9791 uint32_t currentFrameIndex,
9792 uint32_t frameInUseCount,
9793 VkDeviceSize bufferImageGranularity,
9794 VkDeviceSize allocSize,
9795 VkDeviceSize allocAlignment,
9796 VmaSuballocationType allocType,
9797 bool canMakeOtherLost,
9798 uint32_t strategy,
9799 VmaAllocationRequest* pAllocationRequest)
9800 {
9801 const VkDeviceSize blockSize = GetSize();
9802 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9803 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9804
9805 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9806 {
9807 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9808 return false;
9809 }
9810
9811 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9812 if(allocSize > blockSize)
9813 {
9814 return false;
9815 }
9816 VkDeviceSize resultBaseOffset = blockSize - allocSize;
9817 if(!suballocations2nd.empty())
9818 {
9819 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9820 resultBaseOffset = lastSuballoc.offset - allocSize;
9821 if(allocSize > lastSuballoc.offset)
9822 {
9823 return false;
9824 }
9825 }
9826
9827 // Start from offset equal to end of free space.
9828 VkDeviceSize resultOffset = resultBaseOffset;
9829
9830 const VkDeviceSize debugMargin = GetDebugMargin();
9831
9832 // Apply debugMargin at the end.
9833 if(debugMargin > 0)
9834 {
9835 if(resultOffset < debugMargin)
9836 {
9837 return false;
9838 }
9839 resultOffset -= debugMargin;
9840 }
9841
9842 // Apply alignment.
9843 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9844
9845 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9846 // Make bigger alignment if necessary.
9847 if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9848 {
9849 bool bufferImageGranularityConflict = false;
9850 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9851 {
9852 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9853 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9854 {
9855 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9856 {
9857 bufferImageGranularityConflict = true;
9858 break;
9859 }
9860 }
9861 else
9862 // Already on previous page.
9863 break;
9864 }
9865 if(bufferImageGranularityConflict)
9866 {
9867 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9868 }
9869 }
9870
9871 // There is enough free space.
9872 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9873 suballocations1st.back().offset + suballocations1st.back().size :
9874 0;
9875 if(endOf1st + debugMargin <= resultOffset)
9876 {
9877 // Check previous suballocations for BufferImageGranularity conflicts.
9878 // If conflict exists, allocation cannot be made here.
9879 if(bufferImageGranularity > 1)
9880 {
9881 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9882 {
9883 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9884 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9885 {
9886 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9887 {
9888 return false;
9889 }
9890 }
9891 else
9892 {
9893 // Already on next page.
9894 break;
9895 }
9896 }
9897 }
9898
9899 // All tests passed: Success.
9900 pAllocationRequest->offset = resultOffset;
9901 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9902 pAllocationRequest->sumItemSize = 0;
9903 // pAllocationRequest->item unused.
9904 pAllocationRequest->itemsToMakeLostCount = 0;
9905 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
9906 return true;
9907 }
9908
9909 return false;
9910 }
9911
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9912 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
9913 uint32_t currentFrameIndex,
9914 uint32_t frameInUseCount,
9915 VkDeviceSize bufferImageGranularity,
9916 VkDeviceSize allocSize,
9917 VkDeviceSize allocAlignment,
9918 VmaSuballocationType allocType,
9919 bool canMakeOtherLost,
9920 uint32_t strategy,
9921 VmaAllocationRequest* pAllocationRequest)
9922 {
9923 const VkDeviceSize blockSize = GetSize();
9924 const VkDeviceSize debugMargin = GetDebugMargin();
9925 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9926 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9927
9928 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9929 {
9930 // Try to allocate at the end of 1st vector.
9931
9932 VkDeviceSize resultBaseOffset = 0;
9933 if(!suballocations1st.empty())
9934 {
9935 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9936 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9937 }
9938
9939 // Start from offset equal to beginning of free space.
9940 VkDeviceSize resultOffset = resultBaseOffset;
9941
9942 // Apply debugMargin at the beginning.
9943 if(debugMargin > 0)
9944 {
9945 resultOffset += debugMargin;
9946 }
9947
9948 // Apply alignment.
9949 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9950
9951 // Check previous suballocations for BufferImageGranularity conflicts.
9952 // Make bigger alignment if necessary.
9953 if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
9954 {
9955 bool bufferImageGranularityConflict = false;
9956 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9957 {
9958 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9959 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9960 {
9961 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9962 {
9963 bufferImageGranularityConflict = true;
9964 break;
9965 }
9966 }
9967 else
9968 // Already on previous page.
9969 break;
9970 }
9971 if(bufferImageGranularityConflict)
9972 {
9973 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9974 }
9975 }
9976
9977 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9978 suballocations2nd.back().offset : blockSize;
9979
9980 // There is enough free space at the end after alignment.
9981 if(resultOffset + allocSize + debugMargin <= freeSpaceEnd)
9982 {
9983 // Check next suballocations for BufferImageGranularity conflicts.
9984 // If conflict exists, allocation cannot be made here.
9985 if((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9986 {
9987 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9988 {
9989 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9990 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9991 {
9992 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9993 {
9994 return false;
9995 }
9996 }
9997 else
9998 {
9999 // Already on previous page.
10000 break;
10001 }
10002 }
10003 }
10004
10005 // All tests passed: Success.
10006 pAllocationRequest->offset = resultOffset;
10007 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10008 pAllocationRequest->sumItemSize = 0;
10009 // pAllocationRequest->item, customData unused.
10010 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10011 pAllocationRequest->itemsToMakeLostCount = 0;
10012 return true;
10013 }
10014 }
10015
10016 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10017 // beginning of 1st vector as the end of free space.
10018 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10019 {
10020 VMA_ASSERT(!suballocations1st.empty());
10021
10022 VkDeviceSize resultBaseOffset = 0;
10023 if(!suballocations2nd.empty())
10024 {
10025 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10026 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10027 }
10028
10029 // Start from offset equal to beginning of free space.
10030 VkDeviceSize resultOffset = resultBaseOffset;
10031
10032 // Apply debugMargin at the beginning.
10033 if(debugMargin > 0)
10034 {
10035 resultOffset += debugMargin;
10036 }
10037
10038 // Apply alignment.
10039 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10040
10041 // Check previous suballocations for BufferImageGranularity conflicts.
10042 // Make bigger alignment if necessary.
10043 if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
10044 {
10045 bool bufferImageGranularityConflict = false;
10046 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10047 {
10048 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10049 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10050 {
10051 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10052 {
10053 bufferImageGranularityConflict = true;
10054 break;
10055 }
10056 }
10057 else
10058 // Already on previous page.
10059 break;
10060 }
10061 if(bufferImageGranularityConflict)
10062 {
10063 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10064 }
10065 }
10066
10067 pAllocationRequest->itemsToMakeLostCount = 0;
10068 pAllocationRequest->sumItemSize = 0;
10069 size_t index1st = m_1stNullItemsBeginCount;
10070
10071 if(canMakeOtherLost)
10072 {
10073 VMA_ASSERT(!IsVirtual());
10074 while(index1st < suballocations1st.size() &&
10075 resultOffset + allocSize + debugMargin > suballocations1st[index1st].offset)
10076 {
10077 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
10078 const VmaSuballocation& suballoc = suballocations1st[index1st];
10079 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
10080 {
10081 // No problem.
10082 }
10083 else
10084 {
10085 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10086 VMA_ASSERT(alloc != VK_NULL_HANDLE);
10087 if(alloc->CanBecomeLost() &&
10088 alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10089 {
10090 ++pAllocationRequest->itemsToMakeLostCount;
10091 pAllocationRequest->sumItemSize += suballoc.size;
10092 }
10093 else
10094 {
10095 return false;
10096 }
10097 }
10098 ++index1st;
10099 }
10100
10101 // Check next suballocations for BufferImageGranularity conflicts.
10102 // If conflict exists, we must mark more allocations lost or fail.
10103 if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
10104 {
10105 while(index1st < suballocations1st.size())
10106 {
10107 const VmaSuballocation& suballoc = suballocations1st[index1st];
10108 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
10109 {
10110 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10111 if (alloc != VK_NULL_HANDLE)
10112 {
10113 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
10114 if(alloc->CanBecomeLost() &&
10115 alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10116 {
10117 ++pAllocationRequest->itemsToMakeLostCount;
10118 pAllocationRequest->sumItemSize += suballoc.size;
10119 }
10120 else
10121 {
10122 return false;
10123 }
10124 }
10125 }
10126 else
10127 {
10128 // Already on next page.
10129 break;
10130 }
10131 ++index1st;
10132 }
10133 }
10134
10135 // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
10136 if(index1st == suballocations1st.size() &&
10137 resultOffset + allocSize + debugMargin > blockSize)
10138 {
10139 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
10140 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
10141 }
10142 }
10143
10144 // There is enough free space at the end after alignment.
10145 if((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
10146 (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
10147 {
10148 // Check next suballocations for BufferImageGranularity conflicts.
10149 // If conflict exists, allocation cannot be made here.
10150 if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
10151 {
10152 for(size_t nextSuballocIndex = index1st;
10153 nextSuballocIndex < suballocations1st.size();
10154 nextSuballocIndex++)
10155 {
10156 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
10157 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10158 {
10159 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10160 {
10161 return false;
10162 }
10163 }
10164 else
10165 {
10166 // Already on next page.
10167 break;
10168 }
10169 }
10170 }
10171
10172 // All tests passed: Success.
10173 pAllocationRequest->offset = resultOffset;
10174 pAllocationRequest->sumFreeSize =
10175 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : blockSize)
10176 - resultBaseOffset
10177 - pAllocationRequest->sumItemSize;
10178 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
10179 // pAllocationRequest->item, customData unused.
10180 return true;
10181 }
10182 }
10183
10184 return false;
10185 }
10186
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10187 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10188 uint32_t currentFrameIndex,
10189 uint32_t frameInUseCount,
10190 VmaAllocationRequest* pAllocationRequest)
10191 {
10192 VMA_ASSERT(!IsVirtual());
10193
10194 if(pAllocationRequest->itemsToMakeLostCount == 0)
10195 {
10196 return true;
10197 }
10198
10199 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10200
10201 // We always start from 1st.
10202 SuballocationVectorType* suballocations = &AccessSuballocations1st();
10203 size_t index = m_1stNullItemsBeginCount;
10204 size_t madeLostCount = 0;
10205 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10206 {
10207 if(index == suballocations->size())
10208 {
10209 index = 0;
10210 // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
10211 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10212 {
10213 suballocations = &AccessSuballocations2nd();
10214 }
10215 // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
10216 // suballocations continues pointing at AccessSuballocations1st().
10217 VMA_ASSERT(!suballocations->empty());
10218 }
10219 VmaSuballocation& suballoc = (*suballocations)[index];
10220 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10221 {
10222 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10223 VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());
10224 if(alloc->MakeLost(currentFrameIndex, frameInUseCount))
10225 {
10226 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10227 suballoc.userData = VMA_NULL;
10228 m_SumFreeSize += suballoc.size;
10229 if(suballocations == &AccessSuballocations1st())
10230 {
10231 ++m_1stNullItemsMiddleCount;
10232 }
10233 else
10234 {
10235 ++m_2ndNullItemsCount;
10236 }
10237 ++madeLostCount;
10238 }
10239 else
10240 {
10241 return false;
10242 }
10243 }
10244 ++index;
10245 }
10246
10247 CleanupAfterFree();
10248 //VMA_HEAVY_ASSERT(Validate()); // Already called by CleanupAfterFree().
10249
10250 return true;
10251 }
10252
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10253 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10254 {
10255 VMA_ASSERT(!IsVirtual());
10256
10257 uint32_t lostAllocationCount = 0;
10258
10259 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10260 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10261 {
10262 VmaSuballocation& suballoc = suballocations1st[i];
10263 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10264 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10265 alloc->CanBecomeLost() &&
10266 alloc->MakeLost(currentFrameIndex, frameInUseCount))
10267 {
10268 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10269 suballoc.userData = VMA_NULL;
10270 ++m_1stNullItemsMiddleCount;
10271 m_SumFreeSize += suballoc.size;
10272 ++lostAllocationCount;
10273 }
10274 }
10275
10276 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10277 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10278 {
10279 VmaSuballocation& suballoc = suballocations2nd[i];
10280 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10281 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10282 alloc->CanBecomeLost() &&
10283 alloc->MakeLost(currentFrameIndex, frameInUseCount))
10284 {
10285 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10286 suballoc.userData = VMA_NULL;
10287 ++m_2ndNullItemsCount;
10288 m_SumFreeSize += suballoc.size;
10289 ++lostAllocationCount;
10290 }
10291 }
10292
10293 if(lostAllocationCount)
10294 {
10295 CleanupAfterFree();
10296 }
10297
10298 return lostAllocationCount;
10299 }
10300
CheckCorruption(const void * pBlockData)10301 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10302 {
10303 VMA_ASSERT(!IsVirtual());
10304 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10305 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10306 {
10307 const VmaSuballocation& suballoc = suballocations1st[i];
10308 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10309 {
10310 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
10311 {
10312 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10313 return VK_ERROR_UNKNOWN;
10314 }
10315 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10316 {
10317 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10318 return VK_ERROR_UNKNOWN;
10319 }
10320 }
10321 }
10322
10323 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10324 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10325 {
10326 const VmaSuballocation& suballoc = suballocations2nd[i];
10327 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10328 {
10329 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
10330 {
10331 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10332 return VK_ERROR_UNKNOWN;
10333 }
10334 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10335 {
10336 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10337 return VK_ERROR_UNKNOWN;
10338 }
10339 }
10340 }
10341
10342 return VK_SUCCESS;
10343 }
10344
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10345 void VmaBlockMetadata_Linear::Alloc(
10346 const VmaAllocationRequest& request,
10347 VmaSuballocationType type,
10348 void* userData)
10349 {
10350 const VmaSuballocation newSuballoc = { request.offset, request.size, userData, type };
10351
10352 switch(request.type)
10353 {
10354 case VmaAllocationRequestType::UpperAddress:
10355 {
10356 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10357 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10358 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10359 suballocations2nd.push_back(newSuballoc);
10360 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10361 }
10362 break;
10363 case VmaAllocationRequestType::EndOf1st:
10364 {
10365 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10366
10367 VMA_ASSERT(suballocations1st.empty() ||
10368 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
10369 // Check if it fits before the end of the block.
10370 VMA_ASSERT(request.offset + request.size <= GetSize());
10371
10372 suballocations1st.push_back(newSuballoc);
10373 }
10374 break;
10375 case VmaAllocationRequestType::EndOf2nd:
10376 {
10377 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10378 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10379 VMA_ASSERT(!suballocations1st.empty() &&
10380 request.offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
10381 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10382
10383 switch(m_2ndVectorMode)
10384 {
10385 case SECOND_VECTOR_EMPTY:
10386 // First allocation from second part ring buffer.
10387 VMA_ASSERT(suballocations2nd.empty());
10388 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10389 break;
10390 case SECOND_VECTOR_RING_BUFFER:
10391 // 2-part ring buffer is already started.
10392 VMA_ASSERT(!suballocations2nd.empty());
10393 break;
10394 case SECOND_VECTOR_DOUBLE_STACK:
10395 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10396 break;
10397 default:
10398 VMA_ASSERT(0);
10399 }
10400
10401 suballocations2nd.push_back(newSuballoc);
10402 }
10403 break;
10404 default:
10405 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10406 }
10407
10408 m_SumFreeSize -= newSuballoc.size;
10409 }
10410
FreeAtOffset(VkDeviceSize offset)10411 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10412 {
10413 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10414 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10415
10416 if(!suballocations1st.empty())
10417 {
10418 // First allocation: Mark it as next empty at the beginning.
10419 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10420 if(firstSuballoc.offset == offset)
10421 {
10422 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10423 firstSuballoc.userData = VMA_NULL;
10424 m_SumFreeSize += firstSuballoc.size;
10425 ++m_1stNullItemsBeginCount;
10426 CleanupAfterFree();
10427 return;
10428 }
10429 }
10430
10431 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10432 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10433 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10434 {
10435 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10436 if(lastSuballoc.offset == offset)
10437 {
10438 m_SumFreeSize += lastSuballoc.size;
10439 suballocations2nd.pop_back();
10440 CleanupAfterFree();
10441 return;
10442 }
10443 }
10444 // Last allocation in 1st vector.
10445 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10446 {
10447 VmaSuballocation& lastSuballoc = suballocations1st.back();
10448 if(lastSuballoc.offset == offset)
10449 {
10450 m_SumFreeSize += lastSuballoc.size;
10451 suballocations1st.pop_back();
10452 CleanupAfterFree();
10453 return;
10454 }
10455 }
10456
10457 VmaSuballocation refSuballoc;
10458 refSuballoc.offset = offset;
10459 // Rest of members stays uninitialized intentionally for better performance.
10460
10461 // Item from the middle of 1st vector.
10462 {
10463 const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
10464 suballocations1st.begin() + m_1stNullItemsBeginCount,
10465 suballocations1st.end(),
10466 refSuballoc,
10467 VmaSuballocationOffsetLess());
10468 if(it != suballocations1st.end())
10469 {
10470 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10471 it->userData = VMA_NULL;
10472 ++m_1stNullItemsMiddleCount;
10473 m_SumFreeSize += it->size;
10474 CleanupAfterFree();
10475 return;
10476 }
10477 }
10478
10479 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10480 {
10481 // Item from the middle of 2nd vector.
10482 const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10483 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
10484 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
10485 if(it != suballocations2nd.end())
10486 {
10487 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10488 it->userData = VMA_NULL;
10489 ++m_2ndNullItemsCount;
10490 m_SumFreeSize += it->size;
10491 CleanupAfterFree();
10492 return;
10493 }
10494 }
10495
10496 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10497 }
10498
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)10499 void VmaBlockMetadata_Linear::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
10500 {
10501 VmaSuballocation& suballoc = FindSuballocation(offset);
10502 outInfo.size = suballoc.size;
10503 outInfo.pUserData = suballoc.userData;
10504 }
10505
Clear()10506 void VmaBlockMetadata_Linear::Clear()
10507 {
10508 m_SumFreeSize = GetSize();
10509 m_Suballocations0.clear();
10510 m_Suballocations1.clear();
10511 // Leaving m_1stVectorIndex unchanged - it doesn't matter.
10512 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10513 m_1stNullItemsBeginCount = 0;
10514 m_1stNullItemsMiddleCount = 0;
10515 m_2ndNullItemsCount = 0;
10516 }
10517
SetAllocationUserData(VkDeviceSize offset,void * userData)10518 void VmaBlockMetadata_Linear::SetAllocationUserData(VkDeviceSize offset, void* userData)
10519 {
10520 VmaSuballocation& suballoc = FindSuballocation(offset);
10521 suballoc.userData = userData;
10522 }
10523
FindSuballocation(VkDeviceSize offset)10524 VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset)
10525 {
10526 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10527 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10528
10529 VmaSuballocation refSuballoc;
10530 refSuballoc.offset = offset;
10531 // Rest of members stays uninitialized intentionally for better performance.
10532
10533 // Item from the 1st vector.
10534 {
10535 const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
10536 suballocations1st.begin() + m_1stNullItemsBeginCount,
10537 suballocations1st.end(),
10538 refSuballoc,
10539 VmaSuballocationOffsetLess());
10540 if(it != suballocations1st.end())
10541 {
10542 return *it;
10543 }
10544 }
10545
10546 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10547 {
10548 // Rest of members stays uninitialized intentionally for better performance.
10549 const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10550 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
10551 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
10552 if(it != suballocations2nd.end())
10553 {
10554 return *it;
10555 }
10556 }
10557
10558 VMA_ASSERT(0 && "Allocation not found in linear allocator!");
10559 return suballocations1st.back(); // Should never occur.
10560 }
10561
ShouldCompact1st()10562 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10563 {
10564 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10565 const size_t suballocCount = AccessSuballocations1st().size();
10566 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10567 }
10568
CleanupAfterFree()10569 void VmaBlockMetadata_Linear::CleanupAfterFree()
10570 {
10571 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10572 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10573
10574 if(IsEmpty())
10575 {
10576 suballocations1st.clear();
10577 suballocations2nd.clear();
10578 m_1stNullItemsBeginCount = 0;
10579 m_1stNullItemsMiddleCount = 0;
10580 m_2ndNullItemsCount = 0;
10581 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10582 }
10583 else
10584 {
10585 const size_t suballoc1stCount = suballocations1st.size();
10586 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10587 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10588
10589 // Find more null items at the beginning of 1st vector.
10590 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10591 suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
10592 {
10593 ++m_1stNullItemsBeginCount;
10594 --m_1stNullItemsMiddleCount;
10595 }
10596
10597 // Find more null items at the end of 1st vector.
10598 while(m_1stNullItemsMiddleCount > 0 &&
10599 suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
10600 {
10601 --m_1stNullItemsMiddleCount;
10602 suballocations1st.pop_back();
10603 }
10604
10605 // Find more null items at the end of 2nd vector.
10606 while(m_2ndNullItemsCount > 0 &&
10607 suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
10608 {
10609 --m_2ndNullItemsCount;
10610 suballocations2nd.pop_back();
10611 }
10612
10613 // Find more null items at the beginning of 2nd vector.
10614 while(m_2ndNullItemsCount > 0 &&
10615 suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
10616 {
10617 --m_2ndNullItemsCount;
10618 VmaVectorRemove(suballocations2nd, 0);
10619 }
10620
10621 if(ShouldCompact1st())
10622 {
10623 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10624 size_t srcIndex = m_1stNullItemsBeginCount;
10625 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10626 {
10627 while(suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
10628 {
10629 ++srcIndex;
10630 }
10631 if(dstIndex != srcIndex)
10632 {
10633 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10634 }
10635 ++srcIndex;
10636 }
10637 suballocations1st.resize(nonNullItemCount);
10638 m_1stNullItemsBeginCount = 0;
10639 m_1stNullItemsMiddleCount = 0;
10640 }
10641
10642 // 2nd vector became empty.
10643 if(suballocations2nd.empty())
10644 {
10645 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10646 }
10647
10648 // 1st vector became empty.
10649 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10650 {
10651 suballocations1st.clear();
10652 m_1stNullItemsBeginCount = 0;
10653
10654 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10655 {
10656 // Swap 1st with 2nd. Now 2nd is empty.
10657 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10658 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10659 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10660 suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
10661 {
10662 ++m_1stNullItemsBeginCount;
10663 --m_1stNullItemsMiddleCount;
10664 }
10665 m_2ndNullItemsCount = 0;
10666 m_1stVectorIndex ^= 1;
10667 }
10668 }
10669 }
10670
10671 VMA_HEAVY_ASSERT(Validate());
10672 }
10673
10674
10675 ////////////////////////////////////////////////////////////////////////////////
10676 // class VmaBlockMetadata_Buddy
10677
VmaBlockMetadata_Buddy(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)10678 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
10679 VmaBlockMetadata(pAllocationCallbacks, isVirtual),
10680 m_NodeAllocator(pAllocationCallbacks,
10681 32), // firstBlockCapacity
10682 m_Root(VMA_NULL),
10683 m_AllocationCount(0),
10684 m_FreeCount(1),
10685 m_SumFreeSize(0)
10686 {
10687 memset(m_FreeList, 0, sizeof(m_FreeList));
10688 }
10689
~VmaBlockMetadata_Buddy()10690 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10691 {
10692 DeleteNodeChildren(m_Root);
10693 m_NodeAllocator.Free(m_Root);
10694 }
10695
Init(VkDeviceSize size)10696 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10697 {
10698 VmaBlockMetadata::Init(size);
10699
10700 m_UsableSize = VmaPrevPow2(size);
10701 m_SumFreeSize = m_UsableSize;
10702
10703 // Calculate m_LevelCount.
10704 const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;
10705 m_LevelCount = 1;
10706 while(m_LevelCount < MAX_LEVELS &&
10707 LevelToNodeSize(m_LevelCount) >= minNodeSize)
10708 {
10709 ++m_LevelCount;
10710 }
10711
10712 Node* rootNode = m_NodeAllocator.Alloc();
10713 rootNode->offset = 0;
10714 rootNode->type = Node::TYPE_FREE;
10715 rootNode->parent = VMA_NULL;
10716 rootNode->buddy = VMA_NULL;
10717
10718 m_Root = rootNode;
10719 AddToFreeListFront(0, rootNode);
10720 }
10721
Validate()10722 bool VmaBlockMetadata_Buddy::Validate() const
10723 {
10724 // Validate tree.
10725 ValidationContext ctx;
10726 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10727 {
10728 VMA_VALIDATE(false && "ValidateNode failed.");
10729 }
10730 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10731 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10732
10733 // Validate free node lists.
10734 for(uint32_t level = 0; level < m_LevelCount; ++level)
10735 {
10736 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10737 m_FreeList[level].front->free.prev == VMA_NULL);
10738
10739 for(Node* node = m_FreeList[level].front;
10740 node != VMA_NULL;
10741 node = node->free.next)
10742 {
10743 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10744
10745 if(node->free.next == VMA_NULL)
10746 {
10747 VMA_VALIDATE(m_FreeList[level].back == node);
10748 }
10749 else
10750 {
10751 VMA_VALIDATE(node->free.next->free.prev == node);
10752 }
10753 }
10754 }
10755
10756 // Validate that free lists ar higher levels are empty.
10757 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10758 {
10759 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10760 }
10761
10762 return true;
10763 }
10764
GetUnusedRangeSizeMax()10765 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10766 {
10767 for(uint32_t level = 0; level < m_LevelCount; ++level)
10768 {
10769 if(m_FreeList[level].front != VMA_NULL)
10770 {
10771 return LevelToNodeSize(level);
10772 }
10773 }
10774 return 0;
10775 }
10776
CalcAllocationStatInfo(VmaStatInfo & outInfo)10777 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10778 {
10779 VmaInitStatInfo(outInfo);
10780 outInfo.blockCount = 1;
10781
10782 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10783
10784 const VkDeviceSize unusableSize = GetUnusableSize();
10785 if(unusableSize > 0)
10786 {
10787 VmaAddStatInfoUnusedRange(outInfo, unusableSize);
10788 }
10789 }
10790
AddPoolStats(VmaPoolStats & inoutStats)10791 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10792 {
10793 const VkDeviceSize unusableSize = GetUnusableSize();
10794
10795 inoutStats.size += GetSize();
10796 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10797 inoutStats.allocationCount += m_AllocationCount;
10798 inoutStats.unusedRangeCount += m_FreeCount;
10799 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10800
10801 if(unusableSize > 0)
10802 {
10803 ++inoutStats.unusedRangeCount;
10804 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10805 }
10806 }
10807
10808 #if VMA_STATS_STRING_ENABLED
10809
PrintDetailedMap(class VmaJsonWriter & json)10810 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10811 {
10812 VmaStatInfo stat;
10813 CalcAllocationStatInfo(stat);
10814
10815 PrintDetailedMap_Begin(
10816 json,
10817 stat.unusedBytes,
10818 stat.allocationCount,
10819 stat.unusedRangeCount);
10820
10821 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10822
10823 const VkDeviceSize unusableSize = GetUnusableSize();
10824 if(unusableSize > 0)
10825 {
10826 PrintDetailedMap_UnusedRange(json,
10827 m_UsableSize, // offset
10828 unusableSize); // size
10829 }
10830
10831 PrintDetailedMap_End(json);
10832 }
10833
10834 #endif // #if VMA_STATS_STRING_ENABLED
10835
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10836 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10837 uint32_t currentFrameIndex,
10838 uint32_t frameInUseCount,
10839 VkDeviceSize bufferImageGranularity,
10840 VkDeviceSize allocSize,
10841 VkDeviceSize allocAlignment,
10842 bool upperAddress,
10843 VmaSuballocationType allocType,
10844 bool canMakeOtherLost,
10845 uint32_t strategy,
10846 VmaAllocationRequest* pAllocationRequest)
10847 {
10848 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10849
10850 allocSize = AlignAllocationSize(allocSize);
10851
10852 // Simple way to respect bufferImageGranularity. May be optimized some day.
10853 // Whenever it might be an OPTIMAL image...
10854 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10855 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10856 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10857 {
10858 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10859 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10860 }
10861
10862 if(allocSize > m_UsableSize)
10863 {
10864 return false;
10865 }
10866
10867 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10868 for(uint32_t level = targetLevel; level--; )
10869 {
10870 for(Node* freeNode = m_FreeList[level].front;
10871 freeNode != VMA_NULL;
10872 freeNode = freeNode->free.next)
10873 {
10874 if(freeNode->offset % allocAlignment == 0)
10875 {
10876 pAllocationRequest->type = VmaAllocationRequestType::Normal;
10877 pAllocationRequest->offset = freeNode->offset;
10878 pAllocationRequest->size = allocSize;
10879 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10880 pAllocationRequest->sumItemSize = 0;
10881 pAllocationRequest->itemsToMakeLostCount = 0;
10882 pAllocationRequest->customData = (void*)(uintptr_t)level;
10883 return true;
10884 }
10885 }
10886 }
10887
10888 return false;
10889 }
10890
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10891 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10892 uint32_t currentFrameIndex,
10893 uint32_t frameInUseCount,
10894 VmaAllocationRequest* pAllocationRequest)
10895 {
10896 /*
10897 Lost allocations are not supported in buddy allocator at the moment.
10898 Support might be added in the future.
10899 */
10900 return pAllocationRequest->itemsToMakeLostCount == 0;
10901 }
10902
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10903 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10904 {
10905 /*
10906 Lost allocations are not supported in buddy allocator at the moment.
10907 Support might be added in the future.
10908 */
10909 return 0;
10910 }
10911
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10912 void VmaBlockMetadata_Buddy::Alloc(
10913 const VmaAllocationRequest& request,
10914 VmaSuballocationType type,
10915 void* userData)
10916 {
10917 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
10918
10919 const uint32_t targetLevel = AllocSizeToLevel(request.size);
10920 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10921
10922 Node* currNode = m_FreeList[currLevel].front;
10923 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10924 while(currNode->offset != request.offset)
10925 {
10926 currNode = currNode->free.next;
10927 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10928 }
10929
10930 // Go down, splitting free nodes.
10931 while(currLevel < targetLevel)
10932 {
10933 // currNode is already first free node at currLevel.
10934 // Remove it from list of free nodes at this currLevel.
10935 RemoveFromFreeList(currLevel, currNode);
10936
10937 const uint32_t childrenLevel = currLevel + 1;
10938
10939 // Create two free sub-nodes.
10940 Node* leftChild = m_NodeAllocator.Alloc();
10941 Node* rightChild = m_NodeAllocator.Alloc();
10942
10943 leftChild->offset = currNode->offset;
10944 leftChild->type = Node::TYPE_FREE;
10945 leftChild->parent = currNode;
10946 leftChild->buddy = rightChild;
10947
10948 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10949 rightChild->type = Node::TYPE_FREE;
10950 rightChild->parent = currNode;
10951 rightChild->buddy = leftChild;
10952
10953 // Convert current currNode to split type.
10954 currNode->type = Node::TYPE_SPLIT;
10955 currNode->split.leftChild = leftChild;
10956
10957 // Add child nodes to free list. Order is important!
10958 AddToFreeListFront(childrenLevel, rightChild);
10959 AddToFreeListFront(childrenLevel, leftChild);
10960
10961 ++m_FreeCount;
10962 ++currLevel;
10963 currNode = m_FreeList[currLevel].front;
10964
10965 /*
10966 We can be sure that currNode, as left child of node previously split,
10967 also fulfills the alignment requirement.
10968 */
10969 }
10970
10971 // Remove from free list.
10972 VMA_ASSERT(currLevel == targetLevel &&
10973 currNode != VMA_NULL &&
10974 currNode->type == Node::TYPE_FREE);
10975 RemoveFromFreeList(currLevel, currNode);
10976
10977 // Convert to allocation node.
10978 currNode->type = Node::TYPE_ALLOCATION;
10979 currNode->allocation.userData = userData;
10980
10981 ++m_AllocationCount;
10982 --m_FreeCount;
10983 m_SumFreeSize -= request.size;
10984 }
10985
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)10986 void VmaBlockMetadata_Buddy::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
10987 {
10988 uint32_t level = 0;
10989 const Node* const node = FindAllocationNode(offset, level);
10990 outInfo.size = LevelToNodeSize(level);
10991 outInfo.pUserData = node->allocation.userData;
10992 }
10993
DeleteNodeChildren(Node * node)10994 void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)
10995 {
10996 if(node->type == Node::TYPE_SPLIT)
10997 {
10998 DeleteNodeChildren(node->split.leftChild->buddy);
10999 DeleteNodeChildren(node->split.leftChild);
11000 const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();
11001 m_NodeAllocator.Free(node->split.leftChild->buddy);
11002 m_NodeAllocator.Free(node->split.leftChild);
11003 }
11004 }
11005
Clear()11006 void VmaBlockMetadata_Buddy::Clear()
11007 {
11008 DeleteNodeChildren(m_Root);
11009 m_Root->type = Node::TYPE_FREE;
11010 m_AllocationCount = 0;
11011 m_FreeCount = 1;
11012 m_SumFreeSize = m_UsableSize;
11013 }
11014
SetAllocationUserData(VkDeviceSize offset,void * userData)11015 void VmaBlockMetadata_Buddy::SetAllocationUserData(VkDeviceSize offset, void* userData)
11016 {
11017 uint32_t level = 0;
11018 Node* const node = FindAllocationNode(offset, level);
11019 node->allocation.userData = userData;
11020 }
11021
FindAllocationNode(VkDeviceSize offset,uint32_t & outLevel)11022 VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel)
11023 {
11024 Node* node = m_Root;
11025 VkDeviceSize nodeOffset = 0;
11026 outLevel = 0;
11027 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11028 while(node->type == Node::TYPE_SPLIT)
11029 {
11030 const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;
11031 if(offset < nodeOffset + nextLevelNodeSize)
11032 {
11033 node = node->split.leftChild;
11034 }
11035 else
11036 {
11037 node = node->split.leftChild->buddy;
11038 nodeOffset += nextLevelNodeSize;
11039 }
11040 ++outLevel;
11041 levelNodeSize = nextLevelNodeSize;
11042 }
11043
11044 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11045 return node;
11046 }
11047
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11048 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11049 {
11050 VMA_VALIDATE(level < m_LevelCount);
11051 VMA_VALIDATE(curr->parent == parent);
11052 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11053 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11054 switch(curr->type)
11055 {
11056 case Node::TYPE_FREE:
11057 // curr->free.prev, next are validated separately.
11058 ctx.calculatedSumFreeSize += levelNodeSize;
11059 ++ctx.calculatedFreeCount;
11060 break;
11061 case Node::TYPE_ALLOCATION:
11062 ++ctx.calculatedAllocationCount;
11063 if(!IsVirtual())
11064 {
11065 VMA_VALIDATE(curr->allocation.userData != VMA_NULL);
11066 }
11067 break;
11068 case Node::TYPE_SPLIT:
11069 {
11070 const uint32_t childrenLevel = level + 1;
11071 const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;
11072 const Node* const leftChild = curr->split.leftChild;
11073 VMA_VALIDATE(leftChild != VMA_NULL);
11074 VMA_VALIDATE(leftChild->offset == curr->offset);
11075 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11076 {
11077 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11078 }
11079 const Node* const rightChild = leftChild->buddy;
11080 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11081 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11082 {
11083 VMA_VALIDATE(false && "ValidateNode for right child failed.");
11084 }
11085 }
11086 break;
11087 default:
11088 return false;
11089 }
11090
11091 return true;
11092 }
11093
AllocSizeToLevel(VkDeviceSize allocSize)11094 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11095 {
11096 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11097 uint32_t level = 0;
11098 VkDeviceSize currLevelNodeSize = m_UsableSize;
11099 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11100 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11101 {
11102 ++level;
11103 currLevelNodeSize >>= 1;
11104 nextLevelNodeSize >>= 1;
11105 }
11106 return level;
11107 }
11108
FreeAtOffset(VkDeviceSize offset)11109 void VmaBlockMetadata_Buddy::FreeAtOffset(VkDeviceSize offset)
11110 {
11111 uint32_t level = 0;
11112 Node* node = FindAllocationNode(offset, level);
11113
11114 ++m_FreeCount;
11115 --m_AllocationCount;
11116 m_SumFreeSize += LevelToNodeSize(level);
11117
11118 node->type = Node::TYPE_FREE;
11119
11120 // Join free nodes if possible.
11121 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
11122 {
11123 RemoveFromFreeList(level, node->buddy);
11124 Node* const parent = node->parent;
11125
11126 m_NodeAllocator.Free(node->buddy);
11127 m_NodeAllocator.Free(node);
11128 parent->type = Node::TYPE_FREE;
11129
11130 node = parent;
11131 --level;
11132 --m_FreeCount;
11133 }
11134
11135 AddToFreeListFront(level, node);
11136 }
11137
CalcAllocationStatInfoNode(VmaStatInfo & inoutInfo,const Node * node,VkDeviceSize levelNodeSize)11138 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const
11139 {
11140 switch(node->type)
11141 {
11142 case Node::TYPE_FREE:
11143 VmaAddStatInfoUnusedRange(inoutInfo, levelNodeSize);
11144 break;
11145 case Node::TYPE_ALLOCATION:
11146 VmaAddStatInfoAllocation(inoutInfo, levelNodeSize);
11147 break;
11148 case Node::TYPE_SPLIT:
11149 {
11150 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
11151 const Node* const leftChild = node->split.leftChild;
11152 CalcAllocationStatInfoNode(inoutInfo, leftChild, childrenNodeSize);
11153 const Node* const rightChild = leftChild->buddy;
11154 CalcAllocationStatInfoNode(inoutInfo, rightChild, childrenNodeSize);
11155 }
11156 break;
11157 default:
11158 VMA_ASSERT(0);
11159 }
11160 }
11161
AddToFreeListFront(uint32_t level,Node * node)11162 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
11163 {
11164 VMA_ASSERT(node->type == Node::TYPE_FREE);
11165
11166 // List is empty.
11167 Node* const frontNode = m_FreeList[level].front;
11168 if(frontNode == VMA_NULL)
11169 {
11170 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
11171 node->free.prev = node->free.next = VMA_NULL;
11172 m_FreeList[level].front = m_FreeList[level].back = node;
11173 }
11174 else
11175 {
11176 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
11177 node->free.prev = VMA_NULL;
11178 node->free.next = frontNode;
11179 frontNode->free.prev = node;
11180 m_FreeList[level].front = node;
11181 }
11182 }
11183
RemoveFromFreeList(uint32_t level,Node * node)11184 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
11185 {
11186 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
11187
11188 // It is at the front.
11189 if(node->free.prev == VMA_NULL)
11190 {
11191 VMA_ASSERT(m_FreeList[level].front == node);
11192 m_FreeList[level].front = node->free.next;
11193 }
11194 else
11195 {
11196 Node* const prevFreeNode = node->free.prev;
11197 VMA_ASSERT(prevFreeNode->free.next == node);
11198 prevFreeNode->free.next = node->free.next;
11199 }
11200
11201 // It is at the back.
11202 if(node->free.next == VMA_NULL)
11203 {
11204 VMA_ASSERT(m_FreeList[level].back == node);
11205 m_FreeList[level].back = node->free.prev;
11206 }
11207 else
11208 {
11209 Node* const nextFreeNode = node->free.next;
11210 VMA_ASSERT(nextFreeNode->free.prev == node);
11211 nextFreeNode->free.prev = node->free.prev;
11212 }
11213 }
11214
11215 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)11216 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
11217 {
11218 switch(node->type)
11219 {
11220 case Node::TYPE_FREE:
11221 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
11222 break;
11223 case Node::TYPE_ALLOCATION:
11224 PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);
11225 break;
11226 case Node::TYPE_SPLIT:
11227 {
11228 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
11229 const Node* const leftChild = node->split.leftChild;
11230 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
11231 const Node* const rightChild = leftChild->buddy;
11232 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
11233 }
11234 break;
11235 default:
11236 VMA_ASSERT(0);
11237 }
11238 }
11239 #endif // #if VMA_STATS_STRING_ENABLED
11240
11241
11242 ////////////////////////////////////////////////////////////////////////////////
11243 // class VmaDeviceMemoryBlock
11244
VmaDeviceMemoryBlock(VmaAllocator hAllocator)11245 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
11246 m_pMetadata(VMA_NULL),
11247 m_MemoryTypeIndex(UINT32_MAX),
11248 m_Id(0),
11249 m_hMemory(VK_NULL_HANDLE),
11250 m_MapCount(0),
11251 m_pMappedData(VMA_NULL)
11252 {
11253 }
11254
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)11255 void VmaDeviceMemoryBlock::Init(
11256 VmaAllocator hAllocator,
11257 VmaPool hParentPool,
11258 uint32_t newMemoryTypeIndex,
11259 VkDeviceMemory newMemory,
11260 VkDeviceSize newSize,
11261 uint32_t id,
11262 uint32_t algorithm)
11263 {
11264 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11265
11266 m_hParentPool = hParentPool;
11267 m_MemoryTypeIndex = newMemoryTypeIndex;
11268 m_Id = id;
11269 m_hMemory = newMemory;
11270
11271 switch(algorithm)
11272 {
11273 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11274 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
11275 false); // isVirtual
11276 break;
11277 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11278 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator->GetAllocationCallbacks(),
11279 false); // isVirtual
11280 break;
11281 default:
11282 VMA_ASSERT(0);
11283 case 0:
11284 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator->GetAllocationCallbacks(),
11285 false); // isVirtual
11286 }
11287 m_pMetadata->Init(newSize);
11288 }
11289
Destroy(VmaAllocator allocator)11290 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11291 {
11292 // This is the most important assert in the entire library.
11293 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11294 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11295
11296 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11297 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11298 m_hMemory = VK_NULL_HANDLE;
11299
11300 vma_delete(allocator, m_pMetadata);
11301 m_pMetadata = VMA_NULL;
11302 }
11303
Validate()11304 bool VmaDeviceMemoryBlock::Validate() const
11305 {
11306 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11307 (m_pMetadata->GetSize() != 0));
11308
11309 return m_pMetadata->Validate();
11310 }
11311
CheckCorruption(VmaAllocator hAllocator)11312 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11313 {
11314 void* pData = nullptr;
11315 VkResult res = Map(hAllocator, 1, &pData);
11316 if(res != VK_SUCCESS)
11317 {
11318 return res;
11319 }
11320
11321 res = m_pMetadata->CheckCorruption(pData);
11322
11323 Unmap(hAllocator, 1);
11324
11325 return res;
11326 }
11327
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11328 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11329 {
11330 if(count == 0)
11331 {
11332 return VK_SUCCESS;
11333 }
11334
11335 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11336 if(m_MapCount != 0)
11337 {
11338 m_MapCount += count;
11339 VMA_ASSERT(m_pMappedData != VMA_NULL);
11340 if(ppData != VMA_NULL)
11341 {
11342 *ppData = m_pMappedData;
11343 }
11344 return VK_SUCCESS;
11345 }
11346 else
11347 {
11348 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11349 hAllocator->m_hDevice,
11350 m_hMemory,
11351 0, // offset
11352 VK_WHOLE_SIZE,
11353 0, // flags
11354 &m_pMappedData);
11355 if(result == VK_SUCCESS)
11356 {
11357 if(ppData != VMA_NULL)
11358 {
11359 *ppData = m_pMappedData;
11360 }
11361 m_MapCount = count;
11362 }
11363 return result;
11364 }
11365 }
11366
Unmap(VmaAllocator hAllocator,uint32_t count)11367 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11368 {
11369 if(count == 0)
11370 {
11371 return;
11372 }
11373
11374 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11375 if(m_MapCount >= count)
11376 {
11377 m_MapCount -= count;
11378 if(m_MapCount == 0)
11379 {
11380 m_pMappedData = VMA_NULL;
11381 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11382 }
11383 }
11384 else
11385 {
11386 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11387 }
11388 }
11389
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11390 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11391 {
11392 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11393 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11394
11395 void* pData;
11396 VkResult res = Map(hAllocator, 1, &pData);
11397 if(res != VK_SUCCESS)
11398 {
11399 return res;
11400 }
11401
11402 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11403 VmaWriteMagicValue(pData, allocOffset + allocSize);
11404
11405 Unmap(hAllocator, 1);
11406
11407 return VK_SUCCESS;
11408 }
11409
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11410 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11411 {
11412 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11413 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11414
11415 void* pData;
11416 VkResult res = Map(hAllocator, 1, &pData);
11417 if(res != VK_SUCCESS)
11418 {
11419 return res;
11420 }
11421
11422 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11423 {
11424 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11425 }
11426 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11427 {
11428 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11429 }
11430
11431 Unmap(hAllocator, 1);
11432
11433 return VK_SUCCESS;
11434 }
11435
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)11436 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11437 const VmaAllocator hAllocator,
11438 const VmaAllocation hAllocation,
11439 VkDeviceSize allocationLocalOffset,
11440 VkBuffer hBuffer,
11441 const void* pNext)
11442 {
11443 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11444 hAllocation->GetBlock() == this);
11445 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11446 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11447 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11448 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11449 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11450 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
11451 }
11452
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)11453 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11454 const VmaAllocator hAllocator,
11455 const VmaAllocation hAllocation,
11456 VkDeviceSize allocationLocalOffset,
11457 VkImage hImage,
11458 const void* pNext)
11459 {
11460 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11461 hAllocation->GetBlock() == this);
11462 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11463 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11464 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11465 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11466 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11467 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
11468 }
11469
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)11470 VmaPool_T::VmaPool_T(
11471 VmaAllocator hAllocator,
11472 const VmaPoolCreateInfo& createInfo,
11473 VkDeviceSize preferredBlockSize) :
11474 m_BlockVector(
11475 hAllocator,
11476 this, // hParentPool
11477 createInfo.memoryTypeIndex,
11478 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11479 createInfo.minBlockCount,
11480 createInfo.maxBlockCount,
11481 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11482 createInfo.frameInUseCount,
11483 createInfo.blockSize != 0, // explicitBlockSize
11484 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
11485 createInfo.priority,
11486 VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
11487 createInfo.pMemoryAllocateNext),
11488 m_Id(0),
11489 m_Name(VMA_NULL)
11490 {
11491 }
11492
~VmaPool_T()11493 VmaPool_T::~VmaPool_T()
11494 {
11495 VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
11496 }
11497
SetName(const char * pName)11498 void VmaPool_T::SetName(const char* pName)
11499 {
11500 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
11501 VmaFreeString(allocs, m_Name);
11502
11503 if(pName != VMA_NULL)
11504 {
11505 m_Name = VmaCreateStringCopy(allocs, pName);
11506 }
11507 else
11508 {
11509 m_Name = VMA_NULL;
11510 }
11511 }
11512
11513 #if VMA_STATS_STRING_ENABLED
11514
11515 #endif // #if VMA_STATS_STRING_ENABLED
11516
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool explicitBlockSize,uint32_t algorithm,float priority,VkDeviceSize minAllocationAlignment,void * pMemoryAllocateNext)11517 VmaBlockVector::VmaBlockVector(
11518 VmaAllocator hAllocator,
11519 VmaPool hParentPool,
11520 uint32_t memoryTypeIndex,
11521 VkDeviceSize preferredBlockSize,
11522 size_t minBlockCount,
11523 size_t maxBlockCount,
11524 VkDeviceSize bufferImageGranularity,
11525 uint32_t frameInUseCount,
11526 bool explicitBlockSize,
11527 uint32_t algorithm,
11528 float priority,
11529 VkDeviceSize minAllocationAlignment,
11530 void* pMemoryAllocateNext) :
11531 m_hAllocator(hAllocator),
11532 m_hParentPool(hParentPool),
11533 m_MemoryTypeIndex(memoryTypeIndex),
11534 m_PreferredBlockSize(preferredBlockSize),
11535 m_MinBlockCount(minBlockCount),
11536 m_MaxBlockCount(maxBlockCount),
11537 m_BufferImageGranularity(bufferImageGranularity),
11538 m_FrameInUseCount(frameInUseCount),
11539 m_ExplicitBlockSize(explicitBlockSize),
11540 m_Algorithm(algorithm),
11541 m_Priority(priority),
11542 m_MinAllocationAlignment(minAllocationAlignment),
11543 m_pMemoryAllocateNext(pMemoryAllocateNext),
11544 m_HasEmptyBlock(false),
11545 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11546 m_NextBlockId(0)
11547 {
11548 }
11549
~VmaBlockVector()11550 VmaBlockVector::~VmaBlockVector()
11551 {
11552 for(size_t i = m_Blocks.size(); i--; )
11553 {
11554 m_Blocks[i]->Destroy(m_hAllocator);
11555 vma_delete(m_hAllocator, m_Blocks[i]);
11556 }
11557 }
11558
CreateMinBlocks()11559 VkResult VmaBlockVector::CreateMinBlocks()
11560 {
11561 for(size_t i = 0; i < m_MinBlockCount; ++i)
11562 {
11563 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11564 if(res != VK_SUCCESS)
11565 {
11566 return res;
11567 }
11568 }
11569 return VK_SUCCESS;
11570 }
11571
GetPoolStats(VmaPoolStats * pStats)11572 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11573 {
11574 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11575
11576 const size_t blockCount = m_Blocks.size();
11577
11578 pStats->size = 0;
11579 pStats->unusedSize = 0;
11580 pStats->allocationCount = 0;
11581 pStats->unusedRangeCount = 0;
11582 pStats->unusedRangeSizeMax = 0;
11583 pStats->blockCount = blockCount;
11584
11585 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11586 {
11587 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11588 VMA_ASSERT(pBlock);
11589 VMA_HEAVY_ASSERT(pBlock->Validate());
11590 pBlock->m_pMetadata->AddPoolStats(*pStats);
11591 }
11592 }
11593
IsEmpty()11594 bool VmaBlockVector::IsEmpty()
11595 {
11596 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11597 return m_Blocks.empty();
11598 }
11599
IsCorruptionDetectionEnabled()11600 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11601 {
11602 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11603 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11604 (VMA_DEBUG_MARGIN > 0) &&
11605 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
11606 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11607 }
11608
11609 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11610
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)11611 VkResult VmaBlockVector::Allocate(
11612 uint32_t currentFrameIndex,
11613 VkDeviceSize size,
11614 VkDeviceSize alignment,
11615 const VmaAllocationCreateInfo& createInfo,
11616 VmaSuballocationType suballocType,
11617 size_t allocationCount,
11618 VmaAllocation* pAllocations)
11619 {
11620 size_t allocIndex;
11621 VkResult res = VK_SUCCESS;
11622
11623 alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
11624
11625 if(IsCorruptionDetectionEnabled())
11626 {
11627 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11628 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11629 }
11630
11631 {
11632 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11633 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11634 {
11635 res = AllocatePage(
11636 currentFrameIndex,
11637 size,
11638 alignment,
11639 createInfo,
11640 suballocType,
11641 pAllocations + allocIndex);
11642 if(res != VK_SUCCESS)
11643 {
11644 break;
11645 }
11646 }
11647 }
11648
11649 if(res != VK_SUCCESS)
11650 {
11651 // Free all already created allocations.
11652 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11653 while(allocIndex--)
11654 {
11655 VmaAllocation_T* const alloc = pAllocations[allocIndex];
11656 const VkDeviceSize allocSize = alloc->GetSize();
11657 Free(alloc);
11658 m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize);
11659 }
11660 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11661 }
11662
11663 return res;
11664 }
11665
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)11666 VkResult VmaBlockVector::AllocatePage(
11667 uint32_t currentFrameIndex,
11668 VkDeviceSize size,
11669 VkDeviceSize alignment,
11670 const VmaAllocationCreateInfo& createInfo,
11671 VmaSuballocationType suballocType,
11672 VmaAllocation* pAllocation)
11673 {
11674 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11675 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11676 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11677 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11678
11679 VkDeviceSize freeMemory;
11680 {
11681 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11682 VmaBudget heapBudget = {};
11683 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
11684 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
11685 }
11686
11687 const bool canFallbackToDedicated = !IsCustomPool();
11688 const bool canCreateNewBlock =
11689 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11690 (m_Blocks.size() < m_MaxBlockCount) &&
11691 (freeMemory >= size || !canFallbackToDedicated);
11692 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11693
11694 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11695 // Which in turn is available only when maxBlockCount = 1.
11696 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11697 {
11698 canMakeOtherLost = false;
11699 }
11700
11701 // Upper address can only be used with linear allocator and within single memory block.
11702 if(isUpperAddress &&
11703 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11704 {
11705 return VK_ERROR_FEATURE_NOT_PRESENT;
11706 }
11707
11708 // Validate strategy.
11709 switch(strategy)
11710 {
11711 case 0:
11712 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11713 break;
11714 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11715 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11716 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11717 break;
11718 default:
11719 return VK_ERROR_FEATURE_NOT_PRESENT;
11720 }
11721
11722 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11723 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11724 {
11725 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11726 }
11727
11728 /*
11729 Under certain condition, this whole section can be skipped for optimization, so
11730 we move on directly to trying to allocate with canMakeOtherLost. That is the case
11731 e.g. for custom pools with linear algorithm.
11732 */
11733 if(!canMakeOtherLost || canCreateNewBlock)
11734 {
11735 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11736 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11737 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11738
11739 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11740 {
11741 // Use only last block.
11742 if(!m_Blocks.empty())
11743 {
11744 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11745 VMA_ASSERT(pCurrBlock);
11746 VkResult res = AllocateFromBlock(
11747 pCurrBlock,
11748 currentFrameIndex,
11749 size,
11750 alignment,
11751 allocFlagsCopy,
11752 createInfo.pUserData,
11753 suballocType,
11754 strategy,
11755 pAllocation);
11756 if(res == VK_SUCCESS)
11757 {
11758 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
11759 return VK_SUCCESS;
11760 }
11761 }
11762 }
11763 else
11764 {
11765 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11766 {
11767 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11768 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11769 {
11770 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11771 VMA_ASSERT(pCurrBlock);
11772 VkResult res = AllocateFromBlock(
11773 pCurrBlock,
11774 currentFrameIndex,
11775 size,
11776 alignment,
11777 allocFlagsCopy,
11778 createInfo.pUserData,
11779 suballocType,
11780 strategy,
11781 pAllocation);
11782 if(res == VK_SUCCESS)
11783 {
11784 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
11785 return VK_SUCCESS;
11786 }
11787 }
11788 }
11789 else // WORST_FIT, FIRST_FIT
11790 {
11791 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11792 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11793 {
11794 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11795 VMA_ASSERT(pCurrBlock);
11796 VkResult res = AllocateFromBlock(
11797 pCurrBlock,
11798 currentFrameIndex,
11799 size,
11800 alignment,
11801 allocFlagsCopy,
11802 createInfo.pUserData,
11803 suballocType,
11804 strategy,
11805 pAllocation);
11806 if(res == VK_SUCCESS)
11807 {
11808 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
11809 return VK_SUCCESS;
11810 }
11811 }
11812 }
11813 }
11814
11815 // 2. Try to create new block.
11816 if(canCreateNewBlock)
11817 {
11818 // Calculate optimal size for new block.
11819 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11820 uint32_t newBlockSizeShift = 0;
11821 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11822
11823 if(!m_ExplicitBlockSize)
11824 {
11825 // Allocate 1/8, 1/4, 1/2 as first blocks.
11826 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11827 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11828 {
11829 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11830 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11831 {
11832 newBlockSize = smallerNewBlockSize;
11833 ++newBlockSizeShift;
11834 }
11835 else
11836 {
11837 break;
11838 }
11839 }
11840 }
11841
11842 size_t newBlockIndex = 0;
11843 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11844 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11845 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11846 if(!m_ExplicitBlockSize)
11847 {
11848 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11849 {
11850 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11851 if(smallerNewBlockSize >= size)
11852 {
11853 newBlockSize = smallerNewBlockSize;
11854 ++newBlockSizeShift;
11855 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11856 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11857 }
11858 else
11859 {
11860 break;
11861 }
11862 }
11863 }
11864
11865 if(res == VK_SUCCESS)
11866 {
11867 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11868 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11869
11870 res = AllocateFromBlock(
11871 pBlock,
11872 currentFrameIndex,
11873 size,
11874 alignment,
11875 allocFlagsCopy,
11876 createInfo.pUserData,
11877 suballocType,
11878 strategy,
11879 pAllocation);
11880 if(res == VK_SUCCESS)
11881 {
11882 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
11883 return VK_SUCCESS;
11884 }
11885 else
11886 {
11887 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11888 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11889 }
11890 }
11891 }
11892 }
11893
11894 // 3. Try to allocate from existing blocks with making other allocations lost.
11895 if(canMakeOtherLost)
11896 {
11897 uint32_t tryIndex = 0;
11898 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11899 {
11900 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11901 VmaAllocationRequest bestRequest = {};
11902 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11903
11904 // 1. Search existing allocations.
11905 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11906 {
11907 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11908 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11909 {
11910 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11911 VMA_ASSERT(pCurrBlock);
11912 VmaAllocationRequest currRequest = {};
11913 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11914 currentFrameIndex,
11915 m_FrameInUseCount,
11916 m_BufferImageGranularity,
11917 size,
11918 alignment,
11919 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11920 suballocType,
11921 canMakeOtherLost,
11922 strategy,
11923 &currRequest))
11924 {
11925 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11926 if(pBestRequestBlock == VMA_NULL ||
11927 currRequestCost < bestRequestCost)
11928 {
11929 pBestRequestBlock = pCurrBlock;
11930 bestRequest = currRequest;
11931 bestRequestCost = currRequestCost;
11932
11933 if(bestRequestCost == 0)
11934 {
11935 break;
11936 }
11937 }
11938 }
11939 }
11940 }
11941 else // WORST_FIT, FIRST_FIT
11942 {
11943 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11944 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11945 {
11946 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11947 VMA_ASSERT(pCurrBlock);
11948 VmaAllocationRequest currRequest = {};
11949 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11950 currentFrameIndex,
11951 m_FrameInUseCount,
11952 m_BufferImageGranularity,
11953 size,
11954 alignment,
11955 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11956 suballocType,
11957 canMakeOtherLost,
11958 strategy,
11959 &currRequest))
11960 {
11961 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11962 if(pBestRequestBlock == VMA_NULL ||
11963 currRequestCost < bestRequestCost ||
11964 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11965 {
11966 pBestRequestBlock = pCurrBlock;
11967 bestRequest = currRequest;
11968 bestRequestCost = currRequestCost;
11969
11970 if(bestRequestCost == 0 ||
11971 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11972 {
11973 break;
11974 }
11975 }
11976 }
11977 }
11978 }
11979
11980 if(pBestRequestBlock != VMA_NULL)
11981 {
11982 if(mapped)
11983 {
11984 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11985 if(res != VK_SUCCESS)
11986 {
11987 return res;
11988 }
11989 }
11990
11991 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11992 currentFrameIndex,
11993 m_FrameInUseCount,
11994 &bestRequest))
11995 {
11996 // Allocate from this pBlock.
11997 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
11998 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, *pAllocation);
11999 UpdateHasEmptyBlock();
12000 (*pAllocation)->InitBlockAllocation(
12001 pBestRequestBlock,
12002 bestRequest.offset,
12003 alignment,
12004 bestRequest.size, // Not size, as actual allocation size may be larger than requested!
12005 m_MemoryTypeIndex,
12006 suballocType,
12007 mapped,
12008 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12009 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12010 VMA_DEBUG_LOG(" Returned from existing block #%u", pBestRequestBlock->GetId());
12011 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12012 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), bestRequest.size);
12013 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12014 {
12015 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12016 }
12017 if(IsCorruptionDetectionEnabled())
12018 {
12019 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, bestRequest.size);
12020 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12021 }
12022 return VK_SUCCESS;
12023 }
12024 // else: Some allocations must have been touched while we are here. Next try.
12025 }
12026 else
12027 {
12028 // Could not find place in any of the blocks - break outer loop.
12029 break;
12030 }
12031 }
12032 /* Maximum number of tries exceeded - a very unlike event when many other
12033 threads are simultaneously touching allocations making it impossible to make
12034 lost at the same time as we try to allocate. */
12035 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
12036 {
12037 return VK_ERROR_TOO_MANY_OBJECTS;
12038 }
12039 }
12040
12041 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12042 }
12043
Free(const VmaAllocation hAllocation)12044 void VmaBlockVector::Free(
12045 const VmaAllocation hAllocation)
12046 {
12047 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12048
12049 bool budgetExceeded = false;
12050 {
12051 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12052 VmaBudget heapBudget = {};
12053 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12054 budgetExceeded = heapBudget.usage >= heapBudget.budget;
12055 }
12056
12057 // Scope for lock.
12058 {
12059 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12060
12061 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12062
12063 if(IsCorruptionDetectionEnabled())
12064 {
12065 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12066 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12067 }
12068
12069 if(hAllocation->IsPersistentMap())
12070 {
12071 pBlock->Unmap(m_hAllocator, 1);
12072 }
12073
12074 pBlock->m_pMetadata->FreeAtOffset(hAllocation->GetOffset());
12075 VMA_HEAVY_ASSERT(pBlock->Validate());
12076
12077 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12078
12079 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12080 // pBlock became empty after this deallocation.
12081 if(pBlock->m_pMetadata->IsEmpty())
12082 {
12083 // Already has empty block. We don't want to have two, so delete this one.
12084 if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12085 {
12086 pBlockToDelete = pBlock;
12087 Remove(pBlock);
12088 }
12089 // else: We now have an empty block - leave it.
12090 }
12091 // pBlock didn't become empty, but we have another empty block - find and free that one.
12092 // (This is optional, heuristics.)
12093 else if(m_HasEmptyBlock && canDeleteBlock)
12094 {
12095 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12096 if(pLastBlock->m_pMetadata->IsEmpty())
12097 {
12098 pBlockToDelete = pLastBlock;
12099 m_Blocks.pop_back();
12100 }
12101 }
12102
12103 UpdateHasEmptyBlock();
12104 IncrementallySortBlocks();
12105 }
12106
12107 // Destruction of a free block. Deferred until this point, outside of mutex
12108 // lock, for performance reason.
12109 if(pBlockToDelete != VMA_NULL)
12110 {
12111 VMA_DEBUG_LOG(" Deleted empty block #%u", pBlockToDelete->GetId());
12112 pBlockToDelete->Destroy(m_hAllocator);
12113 vma_delete(m_hAllocator, pBlockToDelete);
12114 }
12115 }
12116
CalcMaxBlockSize()12117 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12118 {
12119 VkDeviceSize result = 0;
12120 for(size_t i = m_Blocks.size(); i--; )
12121 {
12122 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
12123 if(result >= m_PreferredBlockSize)
12124 {
12125 break;
12126 }
12127 }
12128 return result;
12129 }
12130
Remove(VmaDeviceMemoryBlock * pBlock)12131 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
12132 {
12133 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12134 {
12135 if(m_Blocks[blockIndex] == pBlock)
12136 {
12137 VmaVectorRemove(m_Blocks, blockIndex);
12138 return;
12139 }
12140 }
12141 VMA_ASSERT(0);
12142 }
12143
IncrementallySortBlocks()12144 void VmaBlockVector::IncrementallySortBlocks()
12145 {
12146 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12147 {
12148 // Bubble sort only until first swap.
12149 for(size_t i = 1; i < m_Blocks.size(); ++i)
12150 {
12151 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
12152 {
12153 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
12154 return;
12155 }
12156 }
12157 }
12158 }
12159
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)12160 VkResult VmaBlockVector::AllocateFromBlock(
12161 VmaDeviceMemoryBlock* pBlock,
12162 uint32_t currentFrameIndex,
12163 VkDeviceSize size,
12164 VkDeviceSize alignment,
12165 VmaAllocationCreateFlags allocFlags,
12166 void* pUserData,
12167 VmaSuballocationType suballocType,
12168 uint32_t strategy,
12169 VmaAllocation* pAllocation)
12170 {
12171 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
12172 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12173 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12174 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12175
12176 VmaAllocationRequest currRequest = {};
12177 if(pBlock->m_pMetadata->CreateAllocationRequest(
12178 currentFrameIndex,
12179 m_FrameInUseCount,
12180 m_BufferImageGranularity,
12181 size,
12182 alignment,
12183 isUpperAddress,
12184 suballocType,
12185 false, // canMakeOtherLost
12186 strategy,
12187 &currRequest))
12188 {
12189 // Allocate from pCurrBlock.
12190 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
12191
12192 if(mapped)
12193 {
12194 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
12195 if(res != VK_SUCCESS)
12196 {
12197 return res;
12198 }
12199 }
12200
12201 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12202 pBlock->m_pMetadata->Alloc(currRequest, suballocType, *pAllocation);
12203 UpdateHasEmptyBlock();
12204 (*pAllocation)->InitBlockAllocation(
12205 pBlock,
12206 currRequest.offset,
12207 alignment,
12208 currRequest.size, // Not size, as actual allocation size may be larger than requested!
12209 m_MemoryTypeIndex,
12210 suballocType,
12211 mapped,
12212 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12213 VMA_HEAVY_ASSERT(pBlock->Validate());
12214 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
12215 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), currRequest.size);
12216 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12217 {
12218 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12219 }
12220 if(IsCorruptionDetectionEnabled())
12221 {
12222 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, currRequest.size);
12223 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12224 }
12225 return VK_SUCCESS;
12226 }
12227 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12228 }
12229
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)12230 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
12231 {
12232 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
12233 allocInfo.pNext = m_pMemoryAllocateNext;
12234 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
12235 allocInfo.allocationSize = blockSize;
12236
12237 #if VMA_BUFFER_DEVICE_ADDRESS
12238 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
12239 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
12240 if(m_hAllocator->m_UseKhrBufferDeviceAddress)
12241 {
12242 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
12243 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
12244 }
12245 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
12246
12247 #if VMA_MEMORY_PRIORITY
12248 VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
12249 if(m_hAllocator->m_UseExtMemoryPriority)
12250 {
12251 priorityInfo.priority = m_Priority;
12252 VmaPnextChainPushFront(&allocInfo, &priorityInfo);
12253 }
12254 #endif // #if VMA_MEMORY_PRIORITY
12255
12256 #if VMA_EXTERNAL_MEMORY
12257 // Attach VkExportMemoryAllocateInfoKHR if necessary.
12258 VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
12259 exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
12260 if(exportMemoryAllocInfo.handleTypes != 0)
12261 {
12262 VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
12263 }
12264 #endif // #if VMA_EXTERNAL_MEMORY
12265
12266 VkDeviceMemory mem = VK_NULL_HANDLE;
12267 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
12268 if(res < 0)
12269 {
12270 return res;
12271 }
12272
12273 // New VkDeviceMemory successfully created.
12274
12275 // Create new Allocation for it.
12276 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
12277 pBlock->Init(
12278 m_hAllocator,
12279 m_hParentPool,
12280 m_MemoryTypeIndex,
12281 mem,
12282 allocInfo.allocationSize,
12283 m_NextBlockId++,
12284 m_Algorithm);
12285
12286 m_Blocks.push_back(pBlock);
12287 if(pNewBlockIndex != VMA_NULL)
12288 {
12289 *pNewBlockIndex = m_Blocks.size() - 1;
12290 }
12291
12292 return VK_SUCCESS;
12293 }
12294
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)12295 void VmaBlockVector::ApplyDefragmentationMovesCpu(
12296 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12297 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
12298 {
12299 const size_t blockCount = m_Blocks.size();
12300 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
12301
12302 enum BLOCK_FLAG
12303 {
12304 BLOCK_FLAG_USED = 0x00000001,
12305 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
12306 };
12307
12308 struct BlockInfo
12309 {
12310 uint32_t flags;
12311 void* pMappedData;
12312 };
12313 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
12314 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
12315 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
12316
12317 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12318 const size_t moveCount = moves.size();
12319 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12320 {
12321 const VmaDefragmentationMove& move = moves[moveIndex];
12322 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
12323 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
12324 }
12325
12326 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12327
12328 // Go over all blocks. Get mapped pointer or map if necessary.
12329 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12330 {
12331 BlockInfo& currBlockInfo = blockInfo[blockIndex];
12332 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12333 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12334 {
12335 currBlockInfo.pMappedData = pBlock->GetMappedData();
12336 // It is not originally mapped - map it.
12337 if(currBlockInfo.pMappedData == VMA_NULL)
12338 {
12339 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12340 if(pDefragCtx->res == VK_SUCCESS)
12341 {
12342 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12343 }
12344 }
12345 }
12346 }
12347
12348 // Go over all moves. Do actual data transfer.
12349 if(pDefragCtx->res == VK_SUCCESS)
12350 {
12351 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12352 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12353
12354 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12355 {
12356 const VmaDefragmentationMove& move = moves[moveIndex];
12357
12358 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12359 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12360
12361 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12362
12363 // Invalidate source.
12364 if(isNonCoherent)
12365 {
12366 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12367 memRange.memory = pSrcBlock->GetDeviceMemory();
12368 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12369 memRange.size = VMA_MIN(
12370 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12371 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12372 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12373 }
12374
12375 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12376 memmove(
12377 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12378 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12379 static_cast<size_t>(move.size));
12380
12381 if(IsCorruptionDetectionEnabled())
12382 {
12383 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12384 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12385 }
12386
12387 // Flush destination.
12388 if(isNonCoherent)
12389 {
12390 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12391 memRange.memory = pDstBlock->GetDeviceMemory();
12392 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12393 memRange.size = VMA_MIN(
12394 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12395 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12396 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12397 }
12398 }
12399 }
12400
12401 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12402 // Regardless of pCtx->res == VK_SUCCESS.
12403 for(size_t blockIndex = blockCount; blockIndex--; )
12404 {
12405 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12406 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12407 {
12408 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12409 pBlock->Unmap(m_hAllocator, 1);
12410 }
12411 }
12412 }
12413
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)12414 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12415 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12416 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12417 VkCommandBuffer commandBuffer)
12418 {
12419 const size_t blockCount = m_Blocks.size();
12420
12421 pDefragCtx->blockContexts.resize(blockCount);
12422 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12423
12424 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12425 const size_t moveCount = moves.size();
12426 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12427 {
12428 const VmaDefragmentationMove& move = moves[moveIndex];
12429
12430 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
12431 {
12432 // Old school move still require us to map the whole block
12433 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12434 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12435 }
12436 }
12437
12438 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12439
12440 // Go over all blocks. Create and bind buffer for whole block if necessary.
12441 {
12442 VkBufferCreateInfo bufCreateInfo;
12443 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
12444
12445 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12446 {
12447 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12448 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12449 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12450 {
12451 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12452 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12453 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12454 if(pDefragCtx->res == VK_SUCCESS)
12455 {
12456 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12457 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12458 }
12459 }
12460 }
12461 }
12462
12463 // Go over all moves. Post data transfer commands to command buffer.
12464 if(pDefragCtx->res == VK_SUCCESS)
12465 {
12466 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12467 {
12468 const VmaDefragmentationMove& move = moves[moveIndex];
12469
12470 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12471 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12472
12473 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12474
12475 VkBufferCopy region = {
12476 move.srcOffset,
12477 move.dstOffset,
12478 move.size };
12479 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12480 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
12481 }
12482 }
12483
12484 // Save buffers to defrag context for later destruction.
12485 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12486 {
12487 pDefragCtx->res = VK_NOT_READY;
12488 }
12489 }
12490
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)12491 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12492 {
12493 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12494 {
12495 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12496 if(pBlock->m_pMetadata->IsEmpty())
12497 {
12498 if(m_Blocks.size() > m_MinBlockCount)
12499 {
12500 if(pDefragmentationStats != VMA_NULL)
12501 {
12502 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12503 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12504 }
12505
12506 VmaVectorRemove(m_Blocks, blockIndex);
12507 pBlock->Destroy(m_hAllocator);
12508 vma_delete(m_hAllocator, pBlock);
12509 }
12510 else
12511 {
12512 break;
12513 }
12514 }
12515 }
12516 UpdateHasEmptyBlock();
12517 }
12518
UpdateHasEmptyBlock()12519 void VmaBlockVector::UpdateHasEmptyBlock()
12520 {
12521 m_HasEmptyBlock = false;
12522 for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
12523 {
12524 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
12525 if(pBlock->m_pMetadata->IsEmpty())
12526 {
12527 m_HasEmptyBlock = true;
12528 break;
12529 }
12530 }
12531 }
12532
12533 #if VMA_STATS_STRING_ENABLED
12534
PrintDetailedMap(class VmaJsonWriter & json)12535 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12536 {
12537 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12538
12539 json.BeginObject();
12540
12541 if(IsCustomPool())
12542 {
12543 const char* poolName = m_hParentPool->GetName();
12544 if(poolName != VMA_NULL && poolName[0] != '\0')
12545 {
12546 json.WriteString("Name");
12547 json.WriteString(poolName);
12548 }
12549
12550 json.WriteString("MemoryTypeIndex");
12551 json.WriteNumber(m_MemoryTypeIndex);
12552
12553 json.WriteString("BlockSize");
12554 json.WriteNumber(m_PreferredBlockSize);
12555
12556 json.WriteString("BlockCount");
12557 json.BeginObject(true);
12558 if(m_MinBlockCount > 0)
12559 {
12560 json.WriteString("Min");
12561 json.WriteNumber((uint64_t)m_MinBlockCount);
12562 }
12563 if(m_MaxBlockCount < SIZE_MAX)
12564 {
12565 json.WriteString("Max");
12566 json.WriteNumber((uint64_t)m_MaxBlockCount);
12567 }
12568 json.WriteString("Cur");
12569 json.WriteNumber((uint64_t)m_Blocks.size());
12570 json.EndObject();
12571
12572 if(m_FrameInUseCount > 0)
12573 {
12574 json.WriteString("FrameInUseCount");
12575 json.WriteNumber(m_FrameInUseCount);
12576 }
12577
12578 if(m_Algorithm != 0)
12579 {
12580 json.WriteString("Algorithm");
12581 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12582 }
12583 }
12584 else
12585 {
12586 json.WriteString("PreferredBlockSize");
12587 json.WriteNumber(m_PreferredBlockSize);
12588 }
12589
12590 json.WriteString("Blocks");
12591 json.BeginObject();
12592 for(size_t i = 0; i < m_Blocks.size(); ++i)
12593 {
12594 json.BeginString();
12595 json.ContinueString(m_Blocks[i]->GetId());
12596 json.EndString();
12597
12598 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12599 }
12600 json.EndObject();
12601
12602 json.EndObject();
12603 }
12604
12605 #endif // #if VMA_STATS_STRING_ENABLED
12606
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)12607 void VmaBlockVector::Defragment(
12608 class VmaBlockVectorDefragmentationContext* pCtx,
12609 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
12610 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12611 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12612 VkCommandBuffer commandBuffer)
12613 {
12614 pCtx->res = VK_SUCCESS;
12615
12616 const VkMemoryPropertyFlags memPropFlags =
12617 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12618 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12619
12620 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12621 isHostVisible;
12622 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12623 !IsCorruptionDetectionEnabled() &&
12624 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
12625
12626 // There are options to defragment this memory type.
12627 if(canDefragmentOnCpu || canDefragmentOnGpu)
12628 {
12629 bool defragmentOnGpu;
12630 // There is only one option to defragment this memory type.
12631 if(canDefragmentOnGpu != canDefragmentOnCpu)
12632 {
12633 defragmentOnGpu = canDefragmentOnGpu;
12634 }
12635 // Both options are available: Heuristics to choose the best one.
12636 else
12637 {
12638 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12639 m_hAllocator->IsIntegratedGpu();
12640 }
12641
12642 bool overlappingMoveSupported = !defragmentOnGpu;
12643
12644 if(m_hAllocator->m_UseMutex)
12645 {
12646 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
12647 {
12648 if(!m_Mutex.TryLockWrite())
12649 {
12650 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
12651 return;
12652 }
12653 }
12654 else
12655 {
12656 m_Mutex.LockWrite();
12657 pCtx->mutexLocked = true;
12658 }
12659 }
12660
12661 pCtx->Begin(overlappingMoveSupported, flags);
12662
12663 // Defragment.
12664
12665 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12666 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12667 VmaDefragmentationAlgorithm* algo = pCtx->GetAlgorithm();
12668 pCtx->res = algo->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
12669
12670 // Accumulate statistics.
12671 if(pStats != VMA_NULL)
12672 {
12673 const VkDeviceSize bytesMoved = algo->GetBytesMoved();
12674 const uint32_t allocationsMoved = algo->GetAllocationsMoved();
12675 pStats->bytesMoved += bytesMoved;
12676 pStats->allocationsMoved += allocationsMoved;
12677 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12678 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12679 if(defragmentOnGpu)
12680 {
12681 maxGpuBytesToMove -= bytesMoved;
12682 maxGpuAllocationsToMove -= allocationsMoved;
12683 }
12684 else
12685 {
12686 maxCpuBytesToMove -= bytesMoved;
12687 maxCpuAllocationsToMove -= allocationsMoved;
12688 }
12689 }
12690
12691 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
12692 {
12693 if(m_hAllocator->m_UseMutex)
12694 m_Mutex.UnlockWrite();
12695
12696 if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
12697 pCtx->res = VK_NOT_READY;
12698
12699 return;
12700 }
12701
12702 if(pCtx->res >= VK_SUCCESS)
12703 {
12704 if(defragmentOnGpu)
12705 {
12706 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
12707 }
12708 else
12709 {
12710 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
12711 }
12712 }
12713 }
12714 }
12715
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)12716 void VmaBlockVector::DefragmentationEnd(
12717 class VmaBlockVectorDefragmentationContext* pCtx,
12718 uint32_t flags,
12719 VmaDefragmentationStats* pStats)
12720 {
12721 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
12722 {
12723 VMA_ASSERT(pCtx->mutexLocked == false);
12724
12725 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
12726 // lock protecting us. Since we mutate state here, we have to take the lock out now
12727 m_Mutex.LockWrite();
12728 pCtx->mutexLocked = true;
12729 }
12730
12731 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
12732 if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
12733 {
12734 // Destroy buffers.
12735 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
12736 {
12737 VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
12738 if(blockCtx.hBuffer)
12739 {
12740 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12741 }
12742 }
12743
12744 if(pCtx->res >= VK_SUCCESS)
12745 {
12746 FreeEmptyBlocks(pStats);
12747 }
12748 }
12749
12750 if(pCtx->mutexLocked)
12751 {
12752 VMA_ASSERT(m_hAllocator->m_UseMutex);
12753 m_Mutex.UnlockWrite();
12754 }
12755 }
12756
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)12757 uint32_t VmaBlockVector::ProcessDefragmentations(
12758 class VmaBlockVectorDefragmentationContext *pCtx,
12759 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
12760 {
12761 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12762
12763 const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
12764
12765 for(uint32_t i = 0; i < moveCount; ++ i)
12766 {
12767 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
12768
12769 pMove->allocation = move.hAllocation;
12770 pMove->memory = move.pDstBlock->GetDeviceMemory();
12771 pMove->offset = move.dstOffset;
12772
12773 ++ pMove;
12774 }
12775
12776 pCtx->defragmentationMovesProcessed += moveCount;
12777
12778 return moveCount;
12779 }
12780
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)12781 void VmaBlockVector::CommitDefragmentations(
12782 class VmaBlockVectorDefragmentationContext *pCtx,
12783 VmaDefragmentationStats* pStats)
12784 {
12785 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12786
12787 for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
12788 {
12789 const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
12790
12791 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
12792 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
12793 }
12794
12795 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
12796 FreeEmptyBlocks(pStats);
12797 }
12798
CalcAllocationCount()12799 size_t VmaBlockVector::CalcAllocationCount() const
12800 {
12801 size_t result = 0;
12802 for(size_t i = 0; i < m_Blocks.size(); ++i)
12803 {
12804 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12805 }
12806 return result;
12807 }
12808
IsBufferImageGranularityConflictPossible()12809 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12810 {
12811 if(m_BufferImageGranularity == 1)
12812 {
12813 return false;
12814 }
12815 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12816 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12817 {
12818 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12819 VMA_ASSERT(m_Algorithm == 0);
12820 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12821 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12822 {
12823 return true;
12824 }
12825 }
12826 return false;
12827 }
12828
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)12829 void VmaBlockVector::MakePoolAllocationsLost(
12830 uint32_t currentFrameIndex,
12831 size_t* pLostAllocationCount)
12832 {
12833 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12834 size_t lostAllocationCount = 0;
12835 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12836 {
12837 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12838 VMA_ASSERT(pBlock);
12839 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12840 }
12841 if(pLostAllocationCount != VMA_NULL)
12842 {
12843 *pLostAllocationCount = lostAllocationCount;
12844 }
12845 }
12846
CheckCorruption()12847 VkResult VmaBlockVector::CheckCorruption()
12848 {
12849 if(!IsCorruptionDetectionEnabled())
12850 {
12851 return VK_ERROR_FEATURE_NOT_PRESENT;
12852 }
12853
12854 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12855 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12856 {
12857 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12858 VMA_ASSERT(pBlock);
12859 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12860 if(res != VK_SUCCESS)
12861 {
12862 return res;
12863 }
12864 }
12865 return VK_SUCCESS;
12866 }
12867
AddStats(VmaStats * pStats)12868 void VmaBlockVector::AddStats(VmaStats* pStats)
12869 {
12870 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12871 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12872
12873 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12874
12875 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12876 {
12877 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12878 VMA_ASSERT(pBlock);
12879 VMA_HEAVY_ASSERT(pBlock->Validate());
12880 VmaStatInfo allocationStatInfo;
12881 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12882 VmaAddStatInfo(pStats->total, allocationStatInfo);
12883 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12884 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12885 }
12886 }
12887
12888 ////////////////////////////////////////////////////////////////////////////////
12889 // VmaDefragmentationAlgorithm_Generic members definition
12890
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12891 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12892 VmaAllocator hAllocator,
12893 VmaBlockVector* pBlockVector,
12894 uint32_t currentFrameIndex,
12895 bool overlappingMoveSupported) :
12896 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12897 m_AllocationCount(0),
12898 m_AllAllocations(false),
12899 m_BytesMoved(0),
12900 m_AllocationsMoved(0),
12901 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12902 {
12903 // Create block info for each block.
12904 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12905 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12906 {
12907 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12908 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12909 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12910 m_Blocks.push_back(pBlockInfo);
12911 }
12912
12913 // Sort them by m_pBlock pointer value.
12914 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12915 }
12916
~VmaDefragmentationAlgorithm_Generic()12917 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12918 {
12919 for(size_t i = m_Blocks.size(); i--; )
12920 {
12921 vma_delete(m_hAllocator, m_Blocks[i]);
12922 }
12923 }
12924
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)12925 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12926 {
12927 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12928 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12929 {
12930 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12931 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12932 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12933 {
12934 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12935 (*it)->m_Allocations.push_back(allocInfo);
12936 }
12937 else
12938 {
12939 VMA_ASSERT(0);
12940 }
12941
12942 ++m_AllocationCount;
12943 }
12944 }
12945
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)12946 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12947 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12948 VkDeviceSize maxBytesToMove,
12949 uint32_t maxAllocationsToMove,
12950 bool freeOldAllocations)
12951 {
12952 if(m_Blocks.empty())
12953 {
12954 return VK_SUCCESS;
12955 }
12956
12957 // This is a choice based on research.
12958 // Option 1:
12959 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12960 // Option 2:
12961 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12962 // Option 3:
12963 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12964
12965 size_t srcBlockMinIndex = 0;
12966 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12967 /*
12968 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12969 {
12970 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12971 if(blocksWithNonMovableCount > 0)
12972 {
12973 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12974 }
12975 }
12976 */
12977
12978 size_t srcBlockIndex = m_Blocks.size() - 1;
12979 size_t srcAllocIndex = SIZE_MAX;
12980 for(;;)
12981 {
12982 // 1. Find next allocation to move.
12983 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12984 // 1.2. Then start from last to first m_Allocations.
12985 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12986 {
12987 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12988 {
12989 // Finished: no more allocations to process.
12990 if(srcBlockIndex == srcBlockMinIndex)
12991 {
12992 return VK_SUCCESS;
12993 }
12994 else
12995 {
12996 --srcBlockIndex;
12997 srcAllocIndex = SIZE_MAX;
12998 }
12999 }
13000 else
13001 {
13002 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13003 }
13004 }
13005
13006 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13007 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13008
13009 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13010 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13011 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13012 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13013
13014 // 2. Try to find new place for this allocation in preceding or current block.
13015 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13016 {
13017 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13018 VmaAllocationRequest dstAllocRequest;
13019 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13020 m_CurrentFrameIndex,
13021 m_pBlockVector->GetFrameInUseCount(),
13022 m_pBlockVector->GetBufferImageGranularity(),
13023 size,
13024 alignment,
13025 false, // upperAddress
13026 suballocType,
13027 false, // canMakeOtherLost
13028 strategy,
13029 &dstAllocRequest) &&
13030 MoveMakesSense(
13031 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13032 {
13033 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13034
13035 // Reached limit on number of allocations or bytes to move.
13036 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13037 (m_BytesMoved + size > maxBytesToMove))
13038 {
13039 return VK_SUCCESS;
13040 }
13041
13042 VmaDefragmentationMove move = {};
13043 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13044 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13045 move.srcOffset = srcOffset;
13046 move.dstOffset = dstAllocRequest.offset;
13047 move.size = size;
13048 move.hAllocation = allocInfo.m_hAllocation;
13049 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13050 move.pDstBlock = pDstBlockInfo->m_pBlock;
13051
13052 moves.push_back(move);
13053
13054 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(dstAllocRequest, suballocType, allocInfo.m_hAllocation);
13055
13056 if(freeOldAllocations)
13057 {
13058 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13059 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13060 }
13061
13062 if(allocInfo.m_pChanged != VMA_NULL)
13063 {
13064 *allocInfo.m_pChanged = VK_TRUE;
13065 }
13066
13067 ++m_AllocationsMoved;
13068 m_BytesMoved += size;
13069
13070 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13071
13072 break;
13073 }
13074 }
13075
13076 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13077
13078 if(srcAllocIndex > 0)
13079 {
13080 --srcAllocIndex;
13081 }
13082 else
13083 {
13084 if(srcBlockIndex > 0)
13085 {
13086 --srcBlockIndex;
13087 srcAllocIndex = SIZE_MAX;
13088 }
13089 else
13090 {
13091 return VK_SUCCESS;
13092 }
13093 }
13094 }
13095 }
13096
CalcBlocksWithNonMovableCount()13097 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13098 {
13099 size_t result = 0;
13100 for(size_t i = 0; i < m_Blocks.size(); ++i)
13101 {
13102 if(m_Blocks[i]->m_HasNonMovableAllocations)
13103 {
13104 ++result;
13105 }
13106 }
13107 return result;
13108 }
13109
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13110 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13111 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13112 VkDeviceSize maxBytesToMove,
13113 uint32_t maxAllocationsToMove,
13114 VmaDefragmentationFlags flags)
13115 {
13116 if(!m_AllAllocations && m_AllocationCount == 0)
13117 {
13118 return VK_SUCCESS;
13119 }
13120
13121 const size_t blockCount = m_Blocks.size();
13122 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13123 {
13124 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13125
13126 if(m_AllAllocations)
13127 {
13128 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13129 VMA_ASSERT(!pMetadata->IsVirtual());
13130 for (VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13131 it != pMetadata->m_Suballocations.end();
13132 ++it)
13133 {
13134 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
13135 {
13136 AllocationInfo allocInfo = AllocationInfo((VmaAllocation)it->userData, VMA_NULL);
13137 pBlockInfo->m_Allocations.push_back(allocInfo);
13138 }
13139 }
13140 }
13141
13142 pBlockInfo->CalcHasNonMovableAllocations();
13143
13144 // This is a choice based on research.
13145 // Option 1:
13146 pBlockInfo->SortAllocationsByOffsetDescending();
13147 // Option 2:
13148 //pBlockInfo->SortAllocationsBySizeDescending();
13149 }
13150
13151 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
13152 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
13153
13154 // This is a choice based on research.
13155 const uint32_t roundCount = 2;
13156
13157 // Execute defragmentation rounds (the main part).
13158 VkResult result = VK_SUCCESS;
13159 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
13160 {
13161 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
13162 }
13163
13164 return result;
13165 }
13166
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)13167 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
13168 size_t dstBlockIndex, VkDeviceSize dstOffset,
13169 size_t srcBlockIndex, VkDeviceSize srcOffset)
13170 {
13171 if(dstBlockIndex < srcBlockIndex)
13172 {
13173 return true;
13174 }
13175 if(dstBlockIndex > srcBlockIndex)
13176 {
13177 return false;
13178 }
13179 if(dstOffset < srcOffset)
13180 {
13181 return true;
13182 }
13183 return false;
13184 }
13185
13186 ////////////////////////////////////////////////////////////////////////////////
13187 // VmaDefragmentationAlgorithm_Fast
13188
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13189 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
13190 VmaAllocator hAllocator,
13191 VmaBlockVector* pBlockVector,
13192 uint32_t currentFrameIndex,
13193 bool overlappingMoveSupported) :
13194 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13195 m_OverlappingMoveSupported(overlappingMoveSupported),
13196 m_AllocationCount(0),
13197 m_AllAllocations(false),
13198 m_BytesMoved(0),
13199 m_AllocationsMoved(0),
13200 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
13201 {
13202 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
13203
13204 }
13205
~VmaDefragmentationAlgorithm_Fast()13206 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
13207 {
13208 }
13209
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13210 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
13211 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13212 VkDeviceSize maxBytesToMove,
13213 uint32_t maxAllocationsToMove,
13214 VmaDefragmentationFlags flags)
13215 {
13216 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
13217
13218 const size_t blockCount = m_pBlockVector->GetBlockCount();
13219 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
13220 {
13221 return VK_SUCCESS;
13222 }
13223
13224 PreprocessMetadata();
13225
13226 // Sort blocks in order from most destination.
13227
13228 m_BlockInfos.resize(blockCount);
13229 for(size_t i = 0; i < blockCount; ++i)
13230 {
13231 m_BlockInfos[i].origBlockIndex = i;
13232 }
13233
13234 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
13235 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
13236 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
13237 });
13238
13239 // THE MAIN ALGORITHM
13240
13241 FreeSpaceDatabase freeSpaceDb;
13242
13243 size_t dstBlockInfoIndex = 0;
13244 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
13245 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
13246 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
13247 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
13248 VkDeviceSize dstOffset = 0;
13249
13250 bool end = false;
13251 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
13252 {
13253 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
13254 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
13255 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
13256 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
13257 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
13258 {
13259 VmaAllocation const pAlloc = (VmaAllocation)srcSuballocIt->userData;
13260 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
13261 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
13262 if(m_AllocationsMoved == maxAllocationsToMove ||
13263 m_BytesMoved + srcAllocSize > maxBytesToMove)
13264 {
13265 end = true;
13266 break;
13267 }
13268 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
13269
13270 VmaDefragmentationMove move = {};
13271 // Try to place it in one of free spaces from the database.
13272 size_t freeSpaceInfoIndex;
13273 VkDeviceSize dstAllocOffset;
13274 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
13275 freeSpaceInfoIndex, dstAllocOffset))
13276 {
13277 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
13278 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
13279 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
13280
13281 // Same block
13282 if(freeSpaceInfoIndex == srcBlockInfoIndex)
13283 {
13284 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
13285
13286 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
13287
13288 VmaSuballocation suballoc = *srcSuballocIt;
13289 suballoc.offset = dstAllocOffset;
13290 ((VmaAllocation)(suballoc.userData))->ChangeOffset(dstAllocOffset);
13291 m_BytesMoved += srcAllocSize;
13292 ++m_AllocationsMoved;
13293
13294 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13295 ++nextSuballocIt;
13296 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13297 srcSuballocIt = nextSuballocIt;
13298
13299 InsertSuballoc(pFreeSpaceMetadata, suballoc);
13300
13301 move.srcBlockIndex = srcOrigBlockIndex;
13302 move.dstBlockIndex = freeSpaceOrigBlockIndex;
13303 move.srcOffset = srcAllocOffset;
13304 move.dstOffset = dstAllocOffset;
13305 move.size = srcAllocSize;
13306
13307 moves.push_back(move);
13308 }
13309 // Different block
13310 else
13311 {
13312 // MOVE OPTION 2: Move the allocation to a different block.
13313
13314 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
13315
13316 VmaSuballocation suballoc = *srcSuballocIt;
13317 suballoc.offset = dstAllocOffset;
13318 ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
13319 m_BytesMoved += srcAllocSize;
13320 ++m_AllocationsMoved;
13321
13322 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13323 ++nextSuballocIt;
13324 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13325 srcSuballocIt = nextSuballocIt;
13326
13327 InsertSuballoc(pFreeSpaceMetadata, suballoc);
13328
13329 move.srcBlockIndex = srcOrigBlockIndex;
13330 move.dstBlockIndex = freeSpaceOrigBlockIndex;
13331 move.srcOffset = srcAllocOffset;
13332 move.dstOffset = dstAllocOffset;
13333 move.size = srcAllocSize;
13334
13335 moves.push_back(move);
13336 }
13337 }
13338 else
13339 {
13340 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
13341
13342 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
13343 while(dstBlockInfoIndex < srcBlockInfoIndex &&
13344 dstAllocOffset + srcAllocSize > dstBlockSize)
13345 {
13346 // But before that, register remaining free space at the end of dst block.
13347 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
13348
13349 ++dstBlockInfoIndex;
13350 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
13351 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
13352 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
13353 dstBlockSize = pDstMetadata->GetSize();
13354 dstOffset = 0;
13355 dstAllocOffset = 0;
13356 }
13357
13358 // Same block
13359 if(dstBlockInfoIndex == srcBlockInfoIndex)
13360 {
13361 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
13362
13363 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
13364
13365 bool skipOver = overlap;
13366 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
13367 {
13368 // If destination and source place overlap, skip if it would move it
13369 // by only < 1/64 of its size.
13370 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
13371 }
13372
13373 if(skipOver)
13374 {
13375 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
13376
13377 dstOffset = srcAllocOffset + srcAllocSize;
13378 ++srcSuballocIt;
13379 }
13380 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
13381 else
13382 {
13383 srcSuballocIt->offset = dstAllocOffset;
13384 ((VmaAllocation)(srcSuballocIt->userData))->ChangeOffset(dstAllocOffset);
13385 dstOffset = dstAllocOffset + srcAllocSize;
13386 m_BytesMoved += srcAllocSize;
13387 ++m_AllocationsMoved;
13388 ++srcSuballocIt;
13389
13390 move.srcBlockIndex = srcOrigBlockIndex;
13391 move.dstBlockIndex = dstOrigBlockIndex;
13392 move.srcOffset = srcAllocOffset;
13393 move.dstOffset = dstAllocOffset;
13394 move.size = srcAllocSize;
13395
13396 moves.push_back(move);
13397 }
13398 }
13399 // Different block
13400 else
13401 {
13402 // MOVE OPTION 2: Move the allocation to a different block.
13403
13404 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
13405 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
13406
13407 VmaSuballocation suballoc = *srcSuballocIt;
13408 suballoc.offset = dstAllocOffset;
13409 ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
13410 dstOffset = dstAllocOffset + srcAllocSize;
13411 m_BytesMoved += srcAllocSize;
13412 ++m_AllocationsMoved;
13413
13414 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13415 ++nextSuballocIt;
13416 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13417 srcSuballocIt = nextSuballocIt;
13418
13419 pDstMetadata->m_Suballocations.push_back(suballoc);
13420
13421 move.srcBlockIndex = srcOrigBlockIndex;
13422 move.dstBlockIndex = dstOrigBlockIndex;
13423 move.srcOffset = srcAllocOffset;
13424 move.dstOffset = dstAllocOffset;
13425 move.size = srcAllocSize;
13426
13427 moves.push_back(move);
13428 }
13429 }
13430 }
13431 }
13432
13433 m_BlockInfos.clear();
13434
13435 PostprocessMetadata();
13436
13437 return VK_SUCCESS;
13438 }
13439
PreprocessMetadata()13440 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13441 {
13442 const size_t blockCount = m_pBlockVector->GetBlockCount();
13443 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13444 {
13445 VmaBlockMetadata_Generic* const pMetadata =
13446 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13447 pMetadata->m_FreeCount = 0;
13448 pMetadata->m_SumFreeSize = pMetadata->GetSize();
13449 pMetadata->m_FreeSuballocationsBySize.clear();
13450 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13451 it != pMetadata->m_Suballocations.end(); )
13452 {
13453 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13454 {
13455 VmaSuballocationList::iterator nextIt = it;
13456 ++nextIt;
13457 pMetadata->m_Suballocations.erase(it);
13458 it = nextIt;
13459 }
13460 else
13461 {
13462 ++it;
13463 }
13464 }
13465 }
13466 }
13467
PostprocessMetadata()13468 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13469 {
13470 const size_t blockCount = m_pBlockVector->GetBlockCount();
13471 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13472 {
13473 VmaBlockMetadata_Generic* const pMetadata =
13474 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13475 const VkDeviceSize blockSize = pMetadata->GetSize();
13476
13477 // No allocations in this block - entire area is free.
13478 if(pMetadata->m_Suballocations.empty())
13479 {
13480 pMetadata->m_FreeCount = 1;
13481 //pMetadata->m_SumFreeSize is already set to blockSize.
13482 VmaSuballocation suballoc = {
13483 0, // offset
13484 blockSize, // size
13485 VMA_NULL, // hAllocation
13486 VMA_SUBALLOCATION_TYPE_FREE };
13487 pMetadata->m_Suballocations.push_back(suballoc);
13488 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13489 }
13490 // There are some allocations in this block.
13491 else
13492 {
13493 VkDeviceSize offset = 0;
13494 VmaSuballocationList::iterator it;
13495 for(it = pMetadata->m_Suballocations.begin();
13496 it != pMetadata->m_Suballocations.end();
13497 ++it)
13498 {
13499 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13500 VMA_ASSERT(it->offset >= offset);
13501
13502 // Need to insert preceding free space.
13503 if(it->offset > offset)
13504 {
13505 ++pMetadata->m_FreeCount;
13506 const VkDeviceSize freeSize = it->offset - offset;
13507 VmaSuballocation suballoc = {
13508 offset, // offset
13509 freeSize, // size
13510 VMA_NULL, // hAllocation
13511 VMA_SUBALLOCATION_TYPE_FREE };
13512 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13513 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13514 }
13515
13516 pMetadata->m_SumFreeSize -= it->size;
13517 offset = it->offset + it->size;
13518 }
13519
13520 // Need to insert trailing free space.
13521 if(offset < blockSize)
13522 {
13523 ++pMetadata->m_FreeCount;
13524 const VkDeviceSize freeSize = blockSize - offset;
13525 VmaSuballocation suballoc = {
13526 offset, // offset
13527 freeSize, // size
13528 VMA_NULL, // hAllocation
13529 VMA_SUBALLOCATION_TYPE_FREE };
13530 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13531 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13532 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13533 }
13534
13535 VMA_SORT(
13536 pMetadata->m_FreeSuballocationsBySize.begin(),
13537 pMetadata->m_FreeSuballocationsBySize.end(),
13538 VmaSuballocationItemSizeLess());
13539 }
13540
13541 VMA_HEAVY_ASSERT(pMetadata->Validate());
13542 }
13543 }
13544
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)13545 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13546 {
13547 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13548 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13549 while(it != pMetadata->m_Suballocations.end())
13550 {
13551 if(it->offset < suballoc.offset)
13552 {
13553 ++it;
13554 }
13555 }
13556 pMetadata->m_Suballocations.insert(it, suballoc);
13557 }
13558
13559 ////////////////////////////////////////////////////////////////////////////////
13560 // VmaBlockVectorDefragmentationContext
13561
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)13562 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13563 VmaAllocator hAllocator,
13564 VmaPool hCustomPool,
13565 VmaBlockVector* pBlockVector,
13566 uint32_t currFrameIndex) :
13567 res(VK_SUCCESS),
13568 mutexLocked(false),
13569 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13570 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
13571 defragmentationMovesProcessed(0),
13572 defragmentationMovesCommitted(0),
13573 hasDefragmentationPlan(0),
13574 m_hAllocator(hAllocator),
13575 m_hCustomPool(hCustomPool),
13576 m_pBlockVector(pBlockVector),
13577 m_CurrFrameIndex(currFrameIndex),
13578 m_pAlgorithm(VMA_NULL),
13579 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13580 m_AllAllocations(false)
13581 {
13582 }
13583
~VmaBlockVectorDefragmentationContext()13584 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13585 {
13586 vma_delete(m_hAllocator, m_pAlgorithm);
13587 }
13588
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13589 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13590 {
13591 AllocInfo info = { hAlloc, pChanged };
13592 m_Allocations.push_back(info);
13593 }
13594
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)13595 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
13596 {
13597 const bool allAllocations = m_AllAllocations ||
13598 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13599
13600 /********************************
13601 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13602 ********************************/
13603
13604 /*
13605 Fast algorithm is supported only when certain criteria are met:
13606 - VMA_DEBUG_MARGIN is 0.
13607 - All allocations in this block vector are movable.
13608 - There is no possibility of image/buffer granularity conflict.
13609 - The defragmentation is not incremental
13610 */
13611 if(VMA_DEBUG_MARGIN == 0 &&
13612 allAllocations &&
13613 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
13614 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
13615 {
13616 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13617 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13618 }
13619 else
13620 {
13621 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13622 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13623 }
13624
13625 if(allAllocations)
13626 {
13627 m_pAlgorithm->AddAll();
13628 }
13629 else
13630 {
13631 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13632 {
13633 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13634 }
13635 }
13636 }
13637
13638 ////////////////////////////////////////////////////////////////////////////////
13639 // VmaDefragmentationContext
13640
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)13641 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13642 VmaAllocator hAllocator,
13643 uint32_t currFrameIndex,
13644 uint32_t flags,
13645 VmaDefragmentationStats* pStats) :
13646 m_hAllocator(hAllocator),
13647 m_CurrFrameIndex(currFrameIndex),
13648 m_Flags(flags),
13649 m_pStats(pStats),
13650 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13651 {
13652 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13653 }
13654
~VmaDefragmentationContext_T()13655 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13656 {
13657 for(size_t i = m_CustomPoolContexts.size(); i--; )
13658 {
13659 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13660 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
13661 vma_delete(m_hAllocator, pBlockVectorCtx);
13662 }
13663 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13664 {
13665 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13666 if(pBlockVectorCtx)
13667 {
13668 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
13669 vma_delete(m_hAllocator, pBlockVectorCtx);
13670 }
13671 }
13672 }
13673
AddPools(uint32_t poolCount,const VmaPool * pPools)13674 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
13675 {
13676 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13677 {
13678 VmaPool pool = pPools[poolIndex];
13679 VMA_ASSERT(pool);
13680 // Pools with algorithm other than default are not defragmented.
13681 if(pool->m_BlockVector.GetAlgorithm() == 0)
13682 {
13683 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13684
13685 for(size_t i = m_CustomPoolContexts.size(); i--; )
13686 {
13687 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13688 {
13689 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13690 break;
13691 }
13692 }
13693
13694 if(!pBlockVectorDefragCtx)
13695 {
13696 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13697 m_hAllocator,
13698 pool,
13699 &pool->m_BlockVector,
13700 m_CurrFrameIndex);
13701 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13702 }
13703
13704 pBlockVectorDefragCtx->AddAll();
13705 }
13706 }
13707 }
13708
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)13709 void VmaDefragmentationContext_T::AddAllocations(
13710 uint32_t allocationCount,
13711 const VmaAllocation* pAllocations,
13712 VkBool32* pAllocationsChanged)
13713 {
13714 // Dispatch pAllocations among defragmentators. Create them when necessary.
13715 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13716 {
13717 const VmaAllocation hAlloc = pAllocations[allocIndex];
13718 VMA_ASSERT(hAlloc);
13719 // DedicatedAlloc cannot be defragmented.
13720 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13721 // Lost allocation cannot be defragmented.
13722 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13723 {
13724 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13725
13726 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
13727 // This allocation belongs to custom pool.
13728 if(hAllocPool != VK_NULL_HANDLE)
13729 {
13730 // Pools with algorithm other than default are not defragmented.
13731 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13732 {
13733 for(size_t i = m_CustomPoolContexts.size(); i--; )
13734 {
13735 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13736 {
13737 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13738 break;
13739 }
13740 }
13741 if(!pBlockVectorDefragCtx)
13742 {
13743 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13744 m_hAllocator,
13745 hAllocPool,
13746 &hAllocPool->m_BlockVector,
13747 m_CurrFrameIndex);
13748 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13749 }
13750 }
13751 }
13752 // This allocation belongs to default pool.
13753 else
13754 {
13755 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13756 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13757 if(!pBlockVectorDefragCtx)
13758 {
13759 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13760 m_hAllocator,
13761 VMA_NULL, // hCustomPool
13762 m_hAllocator->m_pBlockVectors[memTypeIndex],
13763 m_CurrFrameIndex);
13764 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13765 }
13766 }
13767
13768 if(pBlockVectorDefragCtx)
13769 {
13770 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13771 &pAllocationsChanged[allocIndex] : VMA_NULL;
13772 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13773 }
13774 }
13775 }
13776 }
13777
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)13778 VkResult VmaDefragmentationContext_T::Defragment(
13779 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13780 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13781 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
13782 {
13783 if(pStats)
13784 {
13785 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13786 }
13787
13788 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13789 {
13790 // For incremental defragmetnations, we just earmark how much we can move
13791 // The real meat is in the defragmentation steps
13792 m_MaxCpuBytesToMove = maxCpuBytesToMove;
13793 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
13794
13795 m_MaxGpuBytesToMove = maxGpuBytesToMove;
13796 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
13797
13798 if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
13799 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
13800 return VK_SUCCESS;
13801
13802 return VK_NOT_READY;
13803 }
13804
13805 if(commandBuffer == VK_NULL_HANDLE)
13806 {
13807 maxGpuBytesToMove = 0;
13808 maxGpuAllocationsToMove = 0;
13809 }
13810
13811 VkResult res = VK_SUCCESS;
13812
13813 // Process default pools.
13814 for(uint32_t memTypeIndex = 0;
13815 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13816 ++memTypeIndex)
13817 {
13818 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13819 if(pBlockVectorCtx)
13820 {
13821 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13822 pBlockVectorCtx->GetBlockVector()->Defragment(
13823 pBlockVectorCtx,
13824 pStats, flags,
13825 maxCpuBytesToMove, maxCpuAllocationsToMove,
13826 maxGpuBytesToMove, maxGpuAllocationsToMove,
13827 commandBuffer);
13828 if(pBlockVectorCtx->res != VK_SUCCESS)
13829 {
13830 res = pBlockVectorCtx->res;
13831 }
13832 }
13833 }
13834
13835 // Process custom pools.
13836 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13837 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13838 ++customCtxIndex)
13839 {
13840 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13841 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13842 pBlockVectorCtx->GetBlockVector()->Defragment(
13843 pBlockVectorCtx,
13844 pStats, flags,
13845 maxCpuBytesToMove, maxCpuAllocationsToMove,
13846 maxGpuBytesToMove, maxGpuAllocationsToMove,
13847 commandBuffer);
13848 if(pBlockVectorCtx->res != VK_SUCCESS)
13849 {
13850 res = pBlockVectorCtx->res;
13851 }
13852 }
13853
13854 return res;
13855 }
13856
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)13857 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
13858 {
13859 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
13860 uint32_t movesLeft = pInfo->moveCount;
13861
13862 // Process default pools.
13863 for(uint32_t memTypeIndex = 0;
13864 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
13865 ++memTypeIndex)
13866 {
13867 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13868 if(pBlockVectorCtx)
13869 {
13870 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13871
13872 if(!pBlockVectorCtx->hasDefragmentationPlan)
13873 {
13874 pBlockVectorCtx->GetBlockVector()->Defragment(
13875 pBlockVectorCtx,
13876 m_pStats, m_Flags,
13877 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
13878 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
13879 VK_NULL_HANDLE);
13880
13881 if(pBlockVectorCtx->res < VK_SUCCESS)
13882 continue;
13883
13884 pBlockVectorCtx->hasDefragmentationPlan = true;
13885 }
13886
13887 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
13888 pBlockVectorCtx,
13889 pCurrentMove, movesLeft);
13890
13891 movesLeft -= processed;
13892 pCurrentMove += processed;
13893 }
13894 }
13895
13896 // Process custom pools.
13897 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13898 customCtxIndex < customCtxCount;
13899 ++customCtxIndex)
13900 {
13901 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13902 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13903
13904 if(!pBlockVectorCtx->hasDefragmentationPlan)
13905 {
13906 pBlockVectorCtx->GetBlockVector()->Defragment(
13907 pBlockVectorCtx,
13908 m_pStats, m_Flags,
13909 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
13910 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
13911 VK_NULL_HANDLE);
13912
13913 if(pBlockVectorCtx->res < VK_SUCCESS)
13914 continue;
13915
13916 pBlockVectorCtx->hasDefragmentationPlan = true;
13917 }
13918
13919 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
13920 pBlockVectorCtx,
13921 pCurrentMove, movesLeft);
13922
13923 movesLeft -= processed;
13924 pCurrentMove += processed;
13925 }
13926
13927 pInfo->moveCount = pInfo->moveCount - movesLeft;
13928
13929 return VK_SUCCESS;
13930 }
DefragmentPassEnd()13931 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
13932 {
13933 VkResult res = VK_SUCCESS;
13934
13935 // Process default pools.
13936 for(uint32_t memTypeIndex = 0;
13937 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
13938 ++memTypeIndex)
13939 {
13940 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13941 if(pBlockVectorCtx)
13942 {
13943 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13944
13945 if(!pBlockVectorCtx->hasDefragmentationPlan)
13946 {
13947 res = VK_NOT_READY;
13948 continue;
13949 }
13950
13951 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
13952 pBlockVectorCtx, m_pStats);
13953
13954 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
13955 res = VK_NOT_READY;
13956 }
13957 }
13958
13959 // Process custom pools.
13960 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13961 customCtxIndex < customCtxCount;
13962 ++customCtxIndex)
13963 {
13964 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13965 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13966
13967 if(!pBlockVectorCtx->hasDefragmentationPlan)
13968 {
13969 res = VK_NOT_READY;
13970 continue;
13971 }
13972
13973 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
13974 pBlockVectorCtx, m_pStats);
13975
13976 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
13977 res = VK_NOT_READY;
13978 }
13979
13980 return res;
13981 }
13982
13983 ////////////////////////////////////////////////////////////////////////////////
13984 // VmaRecorder
13985
13986 #if VMA_RECORDING_ENABLED
13987
VmaRecorder()13988 VmaRecorder::VmaRecorder() :
13989 m_UseMutex(true),
13990 m_Flags(0),
13991 m_File(VMA_NULL),
13992 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
13993 {
13994 }
13995
Init(const VmaRecordSettings & settings,bool useMutex)13996 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13997 {
13998 m_UseMutex = useMutex;
13999 m_Flags = settings.flags;
14000
14001 #if defined(_WIN32)
14002 // Open file for writing.
14003 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14004
14005 if(err != 0)
14006 {
14007 return VK_ERROR_INITIALIZATION_FAILED;
14008 }
14009 #else
14010 // Open file for writing.
14011 m_File = fopen(settings.pFilePath, "wb");
14012
14013 if(m_File == 0)
14014 {
14015 return VK_ERROR_INITIALIZATION_FAILED;
14016 }
14017 #endif
14018
14019 // Write header.
14020 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14021 fprintf(m_File, "%s\n", "1,8");
14022
14023 return VK_SUCCESS;
14024 }
14025
~VmaRecorder()14026 VmaRecorder::~VmaRecorder()
14027 {
14028 if(m_File != VMA_NULL)
14029 {
14030 fclose(m_File);
14031 }
14032 }
14033
RecordCreateAllocator(uint32_t frameIndex)14034 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14035 {
14036 CallParams callParams;
14037 GetBasicParams(callParams);
14038
14039 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14040 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14041 Flush();
14042 }
14043
RecordDestroyAllocator(uint32_t frameIndex)14044 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14045 {
14046 CallParams callParams;
14047 GetBasicParams(callParams);
14048
14049 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14050 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14051 Flush();
14052 }
14053
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)14054 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14055 {
14056 CallParams callParams;
14057 GetBasicParams(callParams);
14058
14059 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14060 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14061 createInfo.memoryTypeIndex,
14062 createInfo.flags,
14063 createInfo.blockSize,
14064 (uint64_t)createInfo.minBlockCount,
14065 (uint64_t)createInfo.maxBlockCount,
14066 createInfo.frameInUseCount,
14067 pool);
14068 Flush();
14069 }
14070
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)14071 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14072 {
14073 CallParams callParams;
14074 GetBasicParams(callParams);
14075
14076 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14077 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14078 pool);
14079 Flush();
14080 }
14081
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14082 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14083 const VkMemoryRequirements& vkMemReq,
14084 const VmaAllocationCreateInfo& createInfo,
14085 VmaAllocation allocation)
14086 {
14087 CallParams callParams;
14088 GetBasicParams(callParams);
14089
14090 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14091 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14092 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14093 vkMemReq.size,
14094 vkMemReq.alignment,
14095 vkMemReq.memoryTypeBits,
14096 createInfo.flags,
14097 createInfo.usage,
14098 createInfo.requiredFlags,
14099 createInfo.preferredFlags,
14100 createInfo.memoryTypeBits,
14101 createInfo.pool,
14102 allocation,
14103 userDataStr.GetString());
14104 Flush();
14105 }
14106
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)14107 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14108 const VkMemoryRequirements& vkMemReq,
14109 const VmaAllocationCreateInfo& createInfo,
14110 uint64_t allocationCount,
14111 const VmaAllocation* pAllocations)
14112 {
14113 CallParams callParams;
14114 GetBasicParams(callParams);
14115
14116 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14117 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14118 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14119 vkMemReq.size,
14120 vkMemReq.alignment,
14121 vkMemReq.memoryTypeBits,
14122 createInfo.flags,
14123 createInfo.usage,
14124 createInfo.requiredFlags,
14125 createInfo.preferredFlags,
14126 createInfo.memoryTypeBits,
14127 createInfo.pool);
14128 PrintPointerList(allocationCount, pAllocations);
14129 fprintf(m_File, ",%s\n", userDataStr.GetString());
14130 Flush();
14131 }
14132
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14133 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
14134 const VkMemoryRequirements& vkMemReq,
14135 bool requiresDedicatedAllocation,
14136 bool prefersDedicatedAllocation,
14137 const VmaAllocationCreateInfo& createInfo,
14138 VmaAllocation allocation)
14139 {
14140 CallParams callParams;
14141 GetBasicParams(callParams);
14142
14143 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14144 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14145 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14146 vkMemReq.size,
14147 vkMemReq.alignment,
14148 vkMemReq.memoryTypeBits,
14149 requiresDedicatedAllocation ? 1 : 0,
14150 prefersDedicatedAllocation ? 1 : 0,
14151 createInfo.flags,
14152 createInfo.usage,
14153 createInfo.requiredFlags,
14154 createInfo.preferredFlags,
14155 createInfo.memoryTypeBits,
14156 createInfo.pool,
14157 allocation,
14158 userDataStr.GetString());
14159 Flush();
14160 }
14161
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14162 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
14163 const VkMemoryRequirements& vkMemReq,
14164 bool requiresDedicatedAllocation,
14165 bool prefersDedicatedAllocation,
14166 const VmaAllocationCreateInfo& createInfo,
14167 VmaAllocation allocation)
14168 {
14169 CallParams callParams;
14170 GetBasicParams(callParams);
14171
14172 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14173 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14174 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14175 vkMemReq.size,
14176 vkMemReq.alignment,
14177 vkMemReq.memoryTypeBits,
14178 requiresDedicatedAllocation ? 1 : 0,
14179 prefersDedicatedAllocation ? 1 : 0,
14180 createInfo.flags,
14181 createInfo.usage,
14182 createInfo.requiredFlags,
14183 createInfo.preferredFlags,
14184 createInfo.memoryTypeBits,
14185 createInfo.pool,
14186 allocation,
14187 userDataStr.GetString());
14188 Flush();
14189 }
14190
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)14191 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
14192 VmaAllocation allocation)
14193 {
14194 CallParams callParams;
14195 GetBasicParams(callParams);
14196
14197 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14198 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14199 allocation);
14200 Flush();
14201 }
14202
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)14203 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
14204 uint64_t allocationCount,
14205 const VmaAllocation* pAllocations)
14206 {
14207 CallParams callParams;
14208 GetBasicParams(callParams);
14209
14210 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14211 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
14212 PrintPointerList(allocationCount, pAllocations);
14213 fprintf(m_File, "\n");
14214 Flush();
14215 }
14216
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)14217 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
14218 VmaAllocation allocation,
14219 const void* pUserData)
14220 {
14221 CallParams callParams;
14222 GetBasicParams(callParams);
14223
14224 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14225 UserDataString userDataStr(
14226 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
14227 pUserData);
14228 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14229 allocation,
14230 userDataStr.GetString());
14231 Flush();
14232 }
14233
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)14234 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
14235 VmaAllocation allocation)
14236 {
14237 CallParams callParams;
14238 GetBasicParams(callParams);
14239
14240 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14241 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
14242 allocation);
14243 Flush();
14244 }
14245
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)14246 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
14247 VmaAllocation allocation)
14248 {
14249 CallParams callParams;
14250 GetBasicParams(callParams);
14251
14252 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14253 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14254 allocation);
14255 Flush();
14256 }
14257
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)14258 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
14259 VmaAllocation allocation)
14260 {
14261 CallParams callParams;
14262 GetBasicParams(callParams);
14263
14264 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14265 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14266 allocation);
14267 Flush();
14268 }
14269
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)14270 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
14271 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
14272 {
14273 CallParams callParams;
14274 GetBasicParams(callParams);
14275
14276 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14277 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
14278 allocation,
14279 offset,
14280 size);
14281 Flush();
14282 }
14283
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)14284 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
14285 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
14286 {
14287 CallParams callParams;
14288 GetBasicParams(callParams);
14289
14290 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14291 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
14292 allocation,
14293 offset,
14294 size);
14295 Flush();
14296 }
14297
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)14298 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
14299 const VkBufferCreateInfo& bufCreateInfo,
14300 const VmaAllocationCreateInfo& allocCreateInfo,
14301 VmaAllocation allocation)
14302 {
14303 CallParams callParams;
14304 GetBasicParams(callParams);
14305
14306 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14307 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
14308 fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14309 bufCreateInfo.flags,
14310 bufCreateInfo.size,
14311 bufCreateInfo.usage,
14312 bufCreateInfo.sharingMode,
14313 allocCreateInfo.flags,
14314 allocCreateInfo.usage,
14315 allocCreateInfo.requiredFlags,
14316 allocCreateInfo.preferredFlags,
14317 allocCreateInfo.memoryTypeBits,
14318 allocCreateInfo.pool,
14319 allocation,
14320 userDataStr.GetString());
14321 Flush();
14322 }
14323
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)14324 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
14325 const VkImageCreateInfo& imageCreateInfo,
14326 const VmaAllocationCreateInfo& allocCreateInfo,
14327 VmaAllocation allocation)
14328 {
14329 CallParams callParams;
14330 GetBasicParams(callParams);
14331
14332 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14333 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
14334 fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14335 imageCreateInfo.flags,
14336 imageCreateInfo.imageType,
14337 imageCreateInfo.format,
14338 imageCreateInfo.extent.width,
14339 imageCreateInfo.extent.height,
14340 imageCreateInfo.extent.depth,
14341 imageCreateInfo.mipLevels,
14342 imageCreateInfo.arrayLayers,
14343 imageCreateInfo.samples,
14344 imageCreateInfo.tiling,
14345 imageCreateInfo.usage,
14346 imageCreateInfo.sharingMode,
14347 imageCreateInfo.initialLayout,
14348 allocCreateInfo.flags,
14349 allocCreateInfo.usage,
14350 allocCreateInfo.requiredFlags,
14351 allocCreateInfo.preferredFlags,
14352 allocCreateInfo.memoryTypeBits,
14353 allocCreateInfo.pool,
14354 allocation,
14355 userDataStr.GetString());
14356 Flush();
14357 }
14358
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)14359 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
14360 VmaAllocation allocation)
14361 {
14362 CallParams callParams;
14363 GetBasicParams(callParams);
14364
14365 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14366 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
14367 allocation);
14368 Flush();
14369 }
14370
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)14371 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
14372 VmaAllocation allocation)
14373 {
14374 CallParams callParams;
14375 GetBasicParams(callParams);
14376
14377 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14378 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
14379 allocation);
14380 Flush();
14381 }
14382
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)14383 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
14384 VmaAllocation allocation)
14385 {
14386 CallParams callParams;
14387 GetBasicParams(callParams);
14388
14389 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14390 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
14391 allocation);
14392 Flush();
14393 }
14394
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)14395 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
14396 VmaAllocation allocation)
14397 {
14398 CallParams callParams;
14399 GetBasicParams(callParams);
14400
14401 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14402 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
14403 allocation);
14404 Flush();
14405 }
14406
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)14407 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
14408 VmaPool pool)
14409 {
14410 CallParams callParams;
14411 GetBasicParams(callParams);
14412
14413 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14414 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
14415 pool);
14416 Flush();
14417 }
14418
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)14419 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
14420 const VmaDefragmentationInfo2& info,
14421 VmaDefragmentationContext ctx)
14422 {
14423 CallParams callParams;
14424 GetBasicParams(callParams);
14425
14426 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14427 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
14428 info.flags);
14429 PrintPointerList(info.allocationCount, info.pAllocations);
14430 fprintf(m_File, ",");
14431 PrintPointerList(info.poolCount, info.pPools);
14432 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
14433 info.maxCpuBytesToMove,
14434 info.maxCpuAllocationsToMove,
14435 info.maxGpuBytesToMove,
14436 info.maxGpuAllocationsToMove,
14437 info.commandBuffer,
14438 ctx);
14439 Flush();
14440 }
14441
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)14442 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
14443 VmaDefragmentationContext ctx)
14444 {
14445 CallParams callParams;
14446 GetBasicParams(callParams);
14447
14448 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14449 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
14450 ctx);
14451 Flush();
14452 }
14453
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)14454 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
14455 VmaPool pool,
14456 const char* name)
14457 {
14458 CallParams callParams;
14459 GetBasicParams(callParams);
14460
14461 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14462 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14463 pool, name != VMA_NULL ? name : "");
14464 Flush();
14465 }
14466
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)14467 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
14468 {
14469 if(pUserData != VMA_NULL)
14470 {
14471 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
14472 {
14473 m_Str = (const char*)pUserData;
14474 }
14475 else
14476 {
14477 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
14478 snprintf(m_PtrStr, 17, "%p", pUserData);
14479 m_Str = m_PtrStr;
14480 }
14481 }
14482 else
14483 {
14484 m_Str = "";
14485 }
14486 }
14487
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)14488 void VmaRecorder::WriteConfiguration(
14489 const VkPhysicalDeviceProperties& devProps,
14490 const VkPhysicalDeviceMemoryProperties& memProps,
14491 uint32_t vulkanApiVersion,
14492 bool dedicatedAllocationExtensionEnabled,
14493 bool bindMemory2ExtensionEnabled,
14494 bool memoryBudgetExtensionEnabled,
14495 bool deviceCoherentMemoryExtensionEnabled)
14496 {
14497 fprintf(m_File, "Config,Begin\n");
14498
14499 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
14500
14501 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
14502 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
14503 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
14504 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
14505 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
14506 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
14507
14508 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
14509 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
14510 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
14511
14512 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
14513 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
14514 {
14515 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
14516 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
14517 }
14518 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
14519 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
14520 {
14521 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
14522 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
14523 }
14524
14525 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
14526 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
14527 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
14528 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
14529
14530 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
14531 fprintf(m_File, "Macro,VMA_MIN_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_MIN_ALIGNMENT);
14532 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
14533 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
14534 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
14535 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
14536 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
14537 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
14538 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14539
14540 fprintf(m_File, "Config,End\n");
14541 }
14542
GetBasicParams(CallParams & outParams)14543 void VmaRecorder::GetBasicParams(CallParams& outParams)
14544 {
14545 #if defined(_WIN32)
14546 outParams.threadId = GetCurrentThreadId();
14547 #else
14548 // Use C++11 features to get thread id and convert it to uint32_t.
14549 // There is room for optimization since sstream is quite slow.
14550 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
14551 std::thread::id thread_id = std::this_thread::get_id();
14552 std::stringstream thread_id_to_string_converter;
14553 thread_id_to_string_converter << thread_id;
14554 std::string thread_id_as_string = thread_id_to_string_converter.str();
14555 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
14556 #endif
14557
14558 auto current_time = std::chrono::high_resolution_clock::now();
14559
14560 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
14561 }
14562
PrintPointerList(uint64_t count,const VmaAllocation * pItems)14563 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
14564 {
14565 if(count)
14566 {
14567 fprintf(m_File, "%p", pItems[0]);
14568 for(uint64_t i = 1; i < count; ++i)
14569 {
14570 fprintf(m_File, " %p", pItems[i]);
14571 }
14572 }
14573 }
14574
Flush()14575 void VmaRecorder::Flush()
14576 {
14577 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
14578 {
14579 fflush(m_File);
14580 }
14581 }
14582
14583 #endif // #if VMA_RECORDING_ENABLED
14584
14585 ////////////////////////////////////////////////////////////////////////////////
14586 // VmaAllocationObjectAllocator
14587
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)14588 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
14589 m_Allocator(pAllocationCallbacks, 1024)
14590 {
14591 }
14592
Allocate(Types &&...args)14593 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
14594 {
14595 VmaMutexLock mutexLock(m_Mutex);
14596 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
14597 }
14598
Free(VmaAllocation hAlloc)14599 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
14600 {
14601 VmaMutexLock mutexLock(m_Mutex);
14602 m_Allocator.Free(hAlloc);
14603 }
14604
14605 ////////////////////////////////////////////////////////////////////////////////
14606 // VmaAllocator_T
14607
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)14608 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
14609 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
14610 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
14611 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
14612 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
14613 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
14614 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
14615 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
14616 m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
14617 m_hDevice(pCreateInfo->device),
14618 m_hInstance(pCreateInfo->instance),
14619 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14620 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14621 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14622 m_AllocationObjectAllocator(&m_AllocationCallbacks),
14623 m_HeapSizeLimitMask(0),
14624 m_DeviceMemoryCount(0),
14625 m_PreferredLargeHeapBlockSize(0),
14626 m_PhysicalDevice(pCreateInfo->physicalDevice),
14627 m_CurrentFrameIndex(0),
14628 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
14629 m_NextPoolId(0),
14630 m_GlobalMemoryTypeBits(UINT32_MAX)
14631 #if VMA_RECORDING_ENABLED
14632 ,m_pRecorder(VMA_NULL)
14633 #endif
14634 {
14635 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14636 {
14637 m_UseKhrDedicatedAllocation = false;
14638 m_UseKhrBindMemory2 = false;
14639 }
14640
14641 if(VMA_DEBUG_DETECT_CORRUPTION)
14642 {
14643 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14644 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14645 }
14646
14647 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
14648
14649 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
14650 {
14651 #if !(VMA_DEDICATED_ALLOCATION)
14652 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14653 {
14654 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14655 }
14656 #endif
14657 #if !(VMA_BIND_MEMORY2)
14658 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
14659 {
14660 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
14661 }
14662 #endif
14663 }
14664 #if !(VMA_MEMORY_BUDGET)
14665 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
14666 {
14667 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
14668 }
14669 #endif
14670 #if !(VMA_BUFFER_DEVICE_ADDRESS)
14671 if(m_UseKhrBufferDeviceAddress)
14672 {
14673 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
14674 }
14675 #endif
14676 #if VMA_VULKAN_VERSION < 1002000
14677 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
14678 {
14679 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
14680 }
14681 #endif
14682 #if VMA_VULKAN_VERSION < 1001000
14683 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14684 {
14685 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
14686 }
14687 #endif
14688 #if !(VMA_MEMORY_PRIORITY)
14689 if(m_UseExtMemoryPriority)
14690 {
14691 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_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.");
14692 }
14693 #endif
14694
14695 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14696 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14697 memset(&m_MemProps, 0, sizeof(m_MemProps));
14698
14699 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14700 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
14701
14702 #if VMA_EXTERNAL_MEMORY
14703 memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
14704 #endif // #if VMA_EXTERNAL_MEMORY
14705
14706 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14707 {
14708 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
14709 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14710 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14711 }
14712
14713 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14714
14715 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14716 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14717
14718 VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
14719 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14720 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14721 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14722
14723 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14724 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14725
14726 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
14727
14728 #if VMA_EXTERNAL_MEMORY
14729 if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
14730 {
14731 memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
14732 sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
14733 }
14734 #endif // #if VMA_EXTERNAL_MEMORY
14735
14736 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14737 {
14738 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14739 {
14740 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14741 if(limit != VK_WHOLE_SIZE)
14742 {
14743 m_HeapSizeLimitMask |= 1u << heapIndex;
14744 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14745 {
14746 m_MemProps.memoryHeaps[heapIndex].size = limit;
14747 }
14748 }
14749 }
14750 }
14751
14752 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14753 {
14754 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14755
14756 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14757 this,
14758 VK_NULL_HANDLE, // hParentPool
14759 memTypeIndex,
14760 preferredBlockSize,
14761 0,
14762 SIZE_MAX,
14763 GetBufferImageGranularity(),
14764 pCreateInfo->frameInUseCount,
14765 false, // explicitBlockSize
14766 false, // linearAlgorithm
14767 0.5f, // priority (0.5 is the default per Vulkan spec)
14768 GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment
14769 VMA_NULL); // // pMemoryAllocateNext
14770 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14771 // becase minBlockCount is 0.
14772 }
14773 }
14774
Init(const VmaAllocatorCreateInfo * pCreateInfo)14775 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14776 {
14777 VkResult res = VK_SUCCESS;
14778
14779 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14780 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14781 {
14782 #if VMA_RECORDING_ENABLED
14783 m_pRecorder = vma_new(this, VmaRecorder)();
14784 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14785 if(res != VK_SUCCESS)
14786 {
14787 return res;
14788 }
14789 m_pRecorder->WriteConfiguration(
14790 m_PhysicalDeviceProperties,
14791 m_MemProps,
14792 m_VulkanApiVersion,
14793 m_UseKhrDedicatedAllocation,
14794 m_UseKhrBindMemory2,
14795 m_UseExtMemoryBudget,
14796 m_UseAmdDeviceCoherentMemory);
14797 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14798 #else
14799 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14800 return VK_ERROR_FEATURE_NOT_PRESENT;
14801 #endif
14802 }
14803
14804 #if VMA_MEMORY_BUDGET
14805 if(m_UseExtMemoryBudget)
14806 {
14807 UpdateVulkanBudget();
14808 }
14809 #endif // #if VMA_MEMORY_BUDGET
14810
14811 return res;
14812 }
14813
~VmaAllocator_T()14814 VmaAllocator_T::~VmaAllocator_T()
14815 {
14816 #if VMA_RECORDING_ENABLED
14817 if(m_pRecorder != VMA_NULL)
14818 {
14819 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14820 vma_delete(this, m_pRecorder);
14821 }
14822 #endif
14823
14824 VMA_ASSERT(m_Pools.IsEmpty());
14825
14826 for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
14827 {
14828 if(!m_DedicatedAllocations[memTypeIndex].IsEmpty())
14829 {
14830 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
14831 }
14832
14833 vma_delete(this, m_pBlockVectors[memTypeIndex]);
14834 }
14835 }
14836
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14837 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14838 {
14839 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14840 ImportVulkanFunctions_Static();
14841 #endif
14842
14843 if(pVulkanFunctions != VMA_NULL)
14844 {
14845 ImportVulkanFunctions_Custom(pVulkanFunctions);
14846 }
14847
14848 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14849 ImportVulkanFunctions_Dynamic();
14850 #endif
14851
14852 ValidateVulkanFunctions();
14853 }
14854
14855 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14856
ImportVulkanFunctions_Static()14857 void VmaAllocator_T::ImportVulkanFunctions_Static()
14858 {
14859 // Vulkan 1.0
14860 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
14861 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
14862 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
14863 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
14864 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
14865 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
14866 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
14867 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
14868 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
14869 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
14870 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
14871 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
14872 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
14873 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
14874 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
14875 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
14876 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
14877
14878 // Vulkan 1.1
14879 #if VMA_VULKAN_VERSION >= 1001000
14880 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14881 {
14882 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
14883 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
14884 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
14885 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
14886 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
14887 }
14888 #endif
14889 }
14890
14891 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14892
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)14893 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
14894 {
14895 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
14896
14897 #define VMA_COPY_IF_NOT_NULL(funcName) \
14898 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14899
14900 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14901 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14902 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14903 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14904 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14905 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14906 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14907 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14908 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14909 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14910 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14911 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14912 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14913 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14914 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14915 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14916 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14917
14918 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14919 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14920 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14921 #endif
14922
14923 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14924 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
14925 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
14926 #endif
14927
14928 #if VMA_MEMORY_BUDGET
14929 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
14930 #endif
14931
14932 #undef VMA_COPY_IF_NOT_NULL
14933 }
14934
14935 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14936
ImportVulkanFunctions_Dynamic()14937 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
14938 {
14939 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
14940 if(m_VulkanFunctions.memberName == VMA_NULL) \
14941 m_VulkanFunctions.memberName = \
14942 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
14943 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
14944 if(m_VulkanFunctions.memberName == VMA_NULL) \
14945 m_VulkanFunctions.memberName = \
14946 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
14947
14948 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
14949 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
14950 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
14951 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
14952 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
14953 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
14954 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
14955 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
14956 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
14957 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
14958 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
14959 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
14960 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
14961 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
14962 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
14963 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
14964 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
14965
14966 #if VMA_VULKAN_VERSION >= 1001000
14967 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14968 {
14969 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
14970 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
14971 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
14972 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
14973 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
14974 }
14975 #endif
14976
14977 #if VMA_DEDICATED_ALLOCATION
14978 if(m_UseKhrDedicatedAllocation)
14979 {
14980 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
14981 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
14982 }
14983 #endif
14984
14985 #if VMA_BIND_MEMORY2
14986 if(m_UseKhrBindMemory2)
14987 {
14988 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
14989 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
14990 }
14991 #endif // #if VMA_BIND_MEMORY2
14992
14993 #if VMA_MEMORY_BUDGET
14994 if(m_UseExtMemoryBudget)
14995 {
14996 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
14997 }
14998 #endif // #if VMA_MEMORY_BUDGET
14999
15000 #undef VMA_FETCH_DEVICE_FUNC
15001 #undef VMA_FETCH_INSTANCE_FUNC
15002 }
15003
15004 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15005
ValidateVulkanFunctions()15006 void VmaAllocator_T::ValidateVulkanFunctions()
15007 {
15008 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15009 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15010 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15011 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15012 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15013 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15014 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15015 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15016 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15017 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15018 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15019 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15020 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15021 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15022 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15023 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15024 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15025
15026 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15027 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15028 {
15029 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15030 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15031 }
15032 #endif
15033
15034 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15035 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15036 {
15037 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15038 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15039 }
15040 #endif
15041
15042 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15043 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15044 {
15045 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15046 }
15047 #endif
15048 }
15049
CalcPreferredBlockSize(uint32_t memTypeIndex)15050 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15051 {
15052 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15053 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15054 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15055 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15056 }
15057
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15058 VkResult VmaAllocator_T::AllocateMemoryOfType(
15059 VkDeviceSize size,
15060 VkDeviceSize alignment,
15061 bool dedicatedAllocation,
15062 VkBuffer dedicatedBuffer,
15063 VkBufferUsageFlags dedicatedBufferUsage,
15064 VkImage dedicatedImage,
15065 const VmaAllocationCreateInfo& createInfo,
15066 uint32_t memTypeIndex,
15067 VmaSuballocationType suballocType,
15068 size_t allocationCount,
15069 VmaAllocation* pAllocations)
15070 {
15071 VMA_ASSERT(pAllocations != VMA_NULL);
15072 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15073
15074 VmaAllocationCreateInfo finalCreateInfo = createInfo;
15075
15076 // If memory type is not HOST_VISIBLE, disable MAPPED.
15077 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15078 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15079 {
15080 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15081 }
15082 // If memory is lazily allocated, it should be always dedicated.
15083 if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15084 {
15085 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15086 }
15087
15088 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15089 VMA_ASSERT(blockVector);
15090
15091 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15092 bool preferDedicatedMemory =
15093 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15094 dedicatedAllocation ||
15095 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15096 size > preferredBlockSize / 2;
15097
15098 if(preferDedicatedMemory &&
15099 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15100 finalCreateInfo.pool == VK_NULL_HANDLE)
15101 {
15102 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15103 }
15104
15105 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15106 {
15107 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15108 {
15109 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15110 }
15111 else
15112 {
15113 return AllocateDedicatedMemory(
15114 size,
15115 suballocType,
15116 memTypeIndex,
15117 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15118 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15119 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15120 finalCreateInfo.pUserData,
15121 finalCreateInfo.priority,
15122 dedicatedBuffer,
15123 dedicatedBufferUsage,
15124 dedicatedImage,
15125 allocationCount,
15126 pAllocations);
15127 }
15128 }
15129 else
15130 {
15131 VkResult res = blockVector->Allocate(
15132 m_CurrentFrameIndex.load(),
15133 size,
15134 alignment,
15135 finalCreateInfo,
15136 suballocType,
15137 allocationCount,
15138 pAllocations);
15139 if(res == VK_SUCCESS)
15140 {
15141 return res;
15142 }
15143
15144 // 5. Try dedicated memory.
15145 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15146 {
15147 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15148 }
15149
15150 // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
15151 // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
15152 // 3/4 of the maximum allocation count.
15153 if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
15154 {
15155 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15156 }
15157
15158 res = AllocateDedicatedMemory(
15159 size,
15160 suballocType,
15161 memTypeIndex,
15162 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15163 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15164 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15165 finalCreateInfo.pUserData,
15166 finalCreateInfo.priority,
15167 dedicatedBuffer,
15168 dedicatedBufferUsage,
15169 dedicatedImage,
15170 allocationCount,
15171 pAllocations);
15172 if(res == VK_SUCCESS)
15173 {
15174 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
15175 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
15176 return VK_SUCCESS;
15177 }
15178 else
15179 {
15180 // Everything failed: Return error code.
15181 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
15182 return res;
15183 }
15184 }
15185 }
15186
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool withinBudget,bool map,bool isUserDataString,void * pUserData,float priority,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)15187 VkResult VmaAllocator_T::AllocateDedicatedMemory(
15188 VkDeviceSize size,
15189 VmaSuballocationType suballocType,
15190 uint32_t memTypeIndex,
15191 bool withinBudget,
15192 bool map,
15193 bool isUserDataString,
15194 void* pUserData,
15195 float priority,
15196 VkBuffer dedicatedBuffer,
15197 VkBufferUsageFlags dedicatedBufferUsage,
15198 VkImage dedicatedImage,
15199 size_t allocationCount,
15200 VmaAllocation* pAllocations)
15201 {
15202 VMA_ASSERT(allocationCount > 0 && pAllocations);
15203
15204 if(withinBudget)
15205 {
15206 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15207 VmaBudget heapBudget = {};
15208 GetBudget(&heapBudget, heapIndex, 1);
15209 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
15210 {
15211 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15212 }
15213 }
15214
15215 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
15216 allocInfo.memoryTypeIndex = memTypeIndex;
15217 allocInfo.allocationSize = size;
15218
15219 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15220 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
15221 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15222 {
15223 if(dedicatedBuffer != VK_NULL_HANDLE)
15224 {
15225 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
15226 dedicatedAllocInfo.buffer = dedicatedBuffer;
15227 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
15228 }
15229 else if(dedicatedImage != VK_NULL_HANDLE)
15230 {
15231 dedicatedAllocInfo.image = dedicatedImage;
15232 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
15233 }
15234 }
15235 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15236
15237 #if VMA_BUFFER_DEVICE_ADDRESS
15238 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
15239 if(m_UseKhrBufferDeviceAddress)
15240 {
15241 bool canContainBufferWithDeviceAddress = true;
15242 if(dedicatedBuffer != VK_NULL_HANDLE)
15243 {
15244 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
15245 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
15246 }
15247 else if(dedicatedImage != VK_NULL_HANDLE)
15248 {
15249 canContainBufferWithDeviceAddress = false;
15250 }
15251 if(canContainBufferWithDeviceAddress)
15252 {
15253 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
15254 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
15255 }
15256 }
15257 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
15258
15259 #if VMA_MEMORY_PRIORITY
15260 VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
15261 if(m_UseExtMemoryPriority)
15262 {
15263 priorityInfo.priority = priority;
15264 VmaPnextChainPushFront(&allocInfo, &priorityInfo);
15265 }
15266 #endif // #if VMA_MEMORY_PRIORITY
15267
15268 #if VMA_EXTERNAL_MEMORY
15269 // Attach VkExportMemoryAllocateInfoKHR if necessary.
15270 VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
15271 exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
15272 if(exportMemoryAllocInfo.handleTypes != 0)
15273 {
15274 VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
15275 }
15276 #endif // #if VMA_EXTERNAL_MEMORY
15277
15278 size_t allocIndex;
15279 VkResult res = VK_SUCCESS;
15280 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15281 {
15282 res = AllocateDedicatedMemoryPage(
15283 size,
15284 suballocType,
15285 memTypeIndex,
15286 allocInfo,
15287 map,
15288 isUserDataString,
15289 pUserData,
15290 pAllocations + allocIndex);
15291 if(res != VK_SUCCESS)
15292 {
15293 break;
15294 }
15295 }
15296
15297 if(res == VK_SUCCESS)
15298 {
15299 // Register them in m_DedicatedAllocations.
15300 {
15301 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15302 DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
15303 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15304 {
15305 dedicatedAllocations.PushBack(pAllocations[allocIndex]);
15306 }
15307 }
15308
15309 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
15310 }
15311 else
15312 {
15313 // Free all already created allocations.
15314 while(allocIndex--)
15315 {
15316 VmaAllocation currAlloc = pAllocations[allocIndex];
15317 VkDeviceMemory hMemory = currAlloc->GetMemory();
15318
15319 /*
15320 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15321 before vkFreeMemory.
15322
15323 if(currAlloc->GetMappedData() != VMA_NULL)
15324 {
15325 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15326 }
15327 */
15328
15329 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
15330 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
15331 currAlloc->SetUserData(this, VMA_NULL);
15332 m_AllocationObjectAllocator.Free(currAlloc);
15333 }
15334
15335 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15336 }
15337
15338 return res;
15339 }
15340
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)15341 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
15342 VkDeviceSize size,
15343 VmaSuballocationType suballocType,
15344 uint32_t memTypeIndex,
15345 const VkMemoryAllocateInfo& allocInfo,
15346 bool map,
15347 bool isUserDataString,
15348 void* pUserData,
15349 VmaAllocation* pAllocation)
15350 {
15351 VkDeviceMemory hMemory = VK_NULL_HANDLE;
15352 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
15353 if(res < 0)
15354 {
15355 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
15356 return res;
15357 }
15358
15359 void* pMappedData = VMA_NULL;
15360 if(map)
15361 {
15362 res = (*m_VulkanFunctions.vkMapMemory)(
15363 m_hDevice,
15364 hMemory,
15365 0,
15366 VK_WHOLE_SIZE,
15367 0,
15368 &pMappedData);
15369 if(res < 0)
15370 {
15371 VMA_DEBUG_LOG(" vkMapMemory FAILED");
15372 FreeVulkanMemory(memTypeIndex, size, hMemory);
15373 return res;
15374 }
15375 }
15376
15377 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
15378 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
15379 (*pAllocation)->SetUserData(this, pUserData);
15380 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
15381 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15382 {
15383 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
15384 }
15385
15386 return VK_SUCCESS;
15387 }
15388
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)15389 void VmaAllocator_T::GetBufferMemoryRequirements(
15390 VkBuffer hBuffer,
15391 VkMemoryRequirements& memReq,
15392 bool& requiresDedicatedAllocation,
15393 bool& prefersDedicatedAllocation) const
15394 {
15395 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15396 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15397 {
15398 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
15399 memReqInfo.buffer = hBuffer;
15400
15401 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
15402
15403 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
15404 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
15405
15406 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
15407
15408 memReq = memReq2.memoryRequirements;
15409 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
15410 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
15411 }
15412 else
15413 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15414 {
15415 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
15416 requiresDedicatedAllocation = false;
15417 prefersDedicatedAllocation = false;
15418 }
15419 }
15420
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)15421 void VmaAllocator_T::GetImageMemoryRequirements(
15422 VkImage hImage,
15423 VkMemoryRequirements& memReq,
15424 bool& requiresDedicatedAllocation,
15425 bool& prefersDedicatedAllocation) const
15426 {
15427 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15428 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15429 {
15430 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
15431 memReqInfo.image = hImage;
15432
15433 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
15434
15435 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
15436 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
15437
15438 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
15439
15440 memReq = memReq2.memoryRequirements;
15441 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
15442 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
15443 }
15444 else
15445 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15446 {
15447 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
15448 requiresDedicatedAllocation = false;
15449 prefersDedicatedAllocation = false;
15450 }
15451 }
15452
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15453 VkResult VmaAllocator_T::AllocateMemory(
15454 const VkMemoryRequirements& vkMemReq,
15455 bool requiresDedicatedAllocation,
15456 bool prefersDedicatedAllocation,
15457 VkBuffer dedicatedBuffer,
15458 VkBufferUsageFlags dedicatedBufferUsage,
15459 VkImage dedicatedImage,
15460 const VmaAllocationCreateInfo& createInfo,
15461 VmaSuballocationType suballocType,
15462 size_t allocationCount,
15463 VmaAllocation* pAllocations)
15464 {
15465 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15466
15467 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
15468
15469 if(vkMemReq.size == 0)
15470 {
15471 return VK_ERROR_INITIALIZATION_FAILED;
15472 }
15473 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
15474 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15475 {
15476 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
15477 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15478 }
15479 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15480 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
15481 {
15482 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
15483 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15484 }
15485 if(requiresDedicatedAllocation)
15486 {
15487 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15488 {
15489 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
15490 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15491 }
15492 if(createInfo.pool != VK_NULL_HANDLE)
15493 {
15494 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
15495 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15496 }
15497 }
15498 if((createInfo.pool != VK_NULL_HANDLE) &&
15499 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
15500 {
15501 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
15502 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15503 }
15504
15505 if(createInfo.pool != VK_NULL_HANDLE)
15506 {
15507 VmaAllocationCreateInfo createInfoForPool = createInfo;
15508 // If memory type is not HOST_VISIBLE, disable MAPPED.
15509 if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15510 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15511 {
15512 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15513 }
15514
15515 return createInfo.pool->m_BlockVector.Allocate(
15516 m_CurrentFrameIndex.load(),
15517 vkMemReq.size,
15518 vkMemReq.alignment,
15519 createInfoForPool,
15520 suballocType,
15521 allocationCount,
15522 pAllocations);
15523 }
15524 else
15525 {
15526 // Bit mask of memory Vulkan types acceptable for this allocation.
15527 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
15528 uint32_t memTypeIndex = UINT32_MAX;
15529 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
15530 if(res == VK_SUCCESS)
15531 {
15532 res = AllocateMemoryOfType(
15533 vkMemReq.size,
15534 vkMemReq.alignment,
15535 requiresDedicatedAllocation || prefersDedicatedAllocation,
15536 dedicatedBuffer,
15537 dedicatedBufferUsage,
15538 dedicatedImage,
15539 createInfo,
15540 memTypeIndex,
15541 suballocType,
15542 allocationCount,
15543 pAllocations);
15544 // Succeeded on first try.
15545 if(res == VK_SUCCESS)
15546 {
15547 return res;
15548 }
15549 // Allocation from this memory type failed. Try other compatible memory types.
15550 else
15551 {
15552 for(;;)
15553 {
15554 // Remove old memTypeIndex from list of possibilities.
15555 memoryTypeBits &= ~(1u << memTypeIndex);
15556 // Find alternative memTypeIndex.
15557 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
15558 if(res == VK_SUCCESS)
15559 {
15560 res = AllocateMemoryOfType(
15561 vkMemReq.size,
15562 vkMemReq.alignment,
15563 requiresDedicatedAllocation || prefersDedicatedAllocation,
15564 dedicatedBuffer,
15565 dedicatedBufferUsage,
15566 dedicatedImage,
15567 createInfo,
15568 memTypeIndex,
15569 suballocType,
15570 allocationCount,
15571 pAllocations);
15572 // Allocation from this alternative memory type succeeded.
15573 if(res == VK_SUCCESS)
15574 {
15575 return res;
15576 }
15577 // else: Allocation from this memory type failed. Try next one - next loop iteration.
15578 }
15579 // No other matching memory type index could be found.
15580 else
15581 {
15582 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
15583 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15584 }
15585 }
15586 }
15587 }
15588 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
15589 else
15590 return res;
15591 }
15592 }
15593
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)15594 void VmaAllocator_T::FreeMemory(
15595 size_t allocationCount,
15596 const VmaAllocation* pAllocations)
15597 {
15598 VMA_ASSERT(pAllocations);
15599
15600 for(size_t allocIndex = allocationCount; allocIndex--; )
15601 {
15602 VmaAllocation allocation = pAllocations[allocIndex];
15603
15604 if(allocation != VK_NULL_HANDLE)
15605 {
15606 if(TouchAllocation(allocation))
15607 {
15608 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15609 {
15610 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
15611 }
15612
15613 switch(allocation->GetType())
15614 {
15615 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15616 {
15617 VmaBlockVector* pBlockVector = VMA_NULL;
15618 VmaPool hPool = allocation->GetBlock()->GetParentPool();
15619 if(hPool != VK_NULL_HANDLE)
15620 {
15621 pBlockVector = &hPool->m_BlockVector;
15622 }
15623 else
15624 {
15625 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15626 pBlockVector = m_pBlockVectors[memTypeIndex];
15627 }
15628 pBlockVector->Free(allocation);
15629 }
15630 break;
15631 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15632 FreeDedicatedMemory(allocation);
15633 break;
15634 default:
15635 VMA_ASSERT(0);
15636 }
15637 }
15638
15639 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
15640 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
15641 allocation->SetUserData(this, VMA_NULL);
15642 m_AllocationObjectAllocator.Free(allocation);
15643 }
15644 }
15645 }
15646
CalculateStats(VmaStats * pStats)15647 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
15648 {
15649 // Initialize.
15650 VmaInitStatInfo(pStats->total);
15651 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
15652 VmaInitStatInfo(pStats->memoryType[i]);
15653 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
15654 VmaInitStatInfo(pStats->memoryHeap[i]);
15655
15656 // Process default pools.
15657 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15658 {
15659 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15660 VMA_ASSERT(pBlockVector);
15661 pBlockVector->AddStats(pStats);
15662 }
15663
15664 // Process custom pools.
15665 {
15666 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15667 for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15668 {
15669 pool->m_BlockVector.AddStats(pStats);
15670 }
15671 }
15672
15673 // Process dedicated allocations.
15674 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15675 {
15676 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15677 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15678 DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
15679 for(VmaAllocation alloc = dedicatedAllocList.Front();
15680 alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
15681 {
15682 VmaStatInfo allocationStatInfo;
15683 alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo);
15684 VmaAddStatInfo(pStats->total, allocationStatInfo);
15685 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
15686 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
15687 }
15688 }
15689
15690 // Postprocess.
15691 VmaPostprocessCalcStatInfo(pStats->total);
15692 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
15693 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
15694 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
15695 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
15696 }
15697
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)15698 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
15699 {
15700 #if VMA_MEMORY_BUDGET
15701 if(m_UseExtMemoryBudget)
15702 {
15703 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
15704 {
15705 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
15706 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
15707 {
15708 const uint32_t heapIndex = firstHeap + i;
15709
15710 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
15711 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15712
15713 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
15714 {
15715 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
15716 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15717 }
15718 else
15719 {
15720 outBudget->usage = 0;
15721 }
15722
15723 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
15724 outBudget->budget = VMA_MIN(
15725 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
15726 }
15727 }
15728 else
15729 {
15730 UpdateVulkanBudget(); // Outside of mutex lock
15731 GetBudget(outBudget, firstHeap, heapCount); // Recursion
15732 }
15733 }
15734 else
15735 #endif
15736 {
15737 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
15738 {
15739 const uint32_t heapIndex = firstHeap + i;
15740
15741 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
15742 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15743
15744 outBudget->usage = outBudget->blockBytes;
15745 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15746 }
15747 }
15748 }
15749
15750 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
15751
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)15752 VkResult VmaAllocator_T::DefragmentationBegin(
15753 const VmaDefragmentationInfo2& info,
15754 VmaDefragmentationStats* pStats,
15755 VmaDefragmentationContext* pContext)
15756 {
15757 if(info.pAllocationsChanged != VMA_NULL)
15758 {
15759 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
15760 }
15761
15762 *pContext = vma_new(this, VmaDefragmentationContext_T)(
15763 this, m_CurrentFrameIndex.load(), info.flags, pStats);
15764
15765 (*pContext)->AddPools(info.poolCount, info.pPools);
15766 (*pContext)->AddAllocations(
15767 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
15768
15769 VkResult res = (*pContext)->Defragment(
15770 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
15771 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
15772 info.commandBuffer, pStats, info.flags);
15773
15774 if(res != VK_NOT_READY)
15775 {
15776 vma_delete(this, *pContext);
15777 *pContext = VMA_NULL;
15778 }
15779
15780 return res;
15781 }
15782
DefragmentationEnd(VmaDefragmentationContext context)15783 VkResult VmaAllocator_T::DefragmentationEnd(
15784 VmaDefragmentationContext context)
15785 {
15786 vma_delete(this, context);
15787 return VK_SUCCESS;
15788 }
15789
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)15790 VkResult VmaAllocator_T::DefragmentationPassBegin(
15791 VmaDefragmentationPassInfo* pInfo,
15792 VmaDefragmentationContext context)
15793 {
15794 return context->DefragmentPassBegin(pInfo);
15795 }
DefragmentationPassEnd(VmaDefragmentationContext context)15796 VkResult VmaAllocator_T::DefragmentationPassEnd(
15797 VmaDefragmentationContext context)
15798 {
15799 return context->DefragmentPassEnd();
15800
15801 }
15802
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)15803 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
15804 {
15805 if(hAllocation->CanBecomeLost())
15806 {
15807 /*
15808 Warning: This is a carefully designed algorithm.
15809 Do not modify unless you really know what you are doing :)
15810 */
15811 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15812 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15813 for(;;)
15814 {
15815 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
15816 {
15817 pAllocationInfo->memoryType = UINT32_MAX;
15818 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
15819 pAllocationInfo->offset = 0;
15820 pAllocationInfo->size = hAllocation->GetSize();
15821 pAllocationInfo->pMappedData = VMA_NULL;
15822 pAllocationInfo->pUserData = hAllocation->GetUserData();
15823 return;
15824 }
15825 else if(localLastUseFrameIndex == localCurrFrameIndex)
15826 {
15827 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15828 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15829 pAllocationInfo->offset = hAllocation->GetOffset();
15830 pAllocationInfo->size = hAllocation->GetSize();
15831 pAllocationInfo->pMappedData = VMA_NULL;
15832 pAllocationInfo->pUserData = hAllocation->GetUserData();
15833 return;
15834 }
15835 else // Last use time earlier than current time.
15836 {
15837 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15838 {
15839 localLastUseFrameIndex = localCurrFrameIndex;
15840 }
15841 }
15842 }
15843 }
15844 else
15845 {
15846 #if VMA_STATS_STRING_ENABLED
15847 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15848 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15849 for(;;)
15850 {
15851 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
15852 if(localLastUseFrameIndex == localCurrFrameIndex)
15853 {
15854 break;
15855 }
15856 else // Last use time earlier than current time.
15857 {
15858 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15859 {
15860 localLastUseFrameIndex = localCurrFrameIndex;
15861 }
15862 }
15863 }
15864 #endif
15865
15866 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15867 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15868 pAllocationInfo->offset = hAllocation->GetOffset();
15869 pAllocationInfo->size = hAllocation->GetSize();
15870 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
15871 pAllocationInfo->pUserData = hAllocation->GetUserData();
15872 }
15873 }
15874
TouchAllocation(VmaAllocation hAllocation)15875 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
15876 {
15877 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
15878 if(hAllocation->CanBecomeLost())
15879 {
15880 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15881 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15882 for(;;)
15883 {
15884 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
15885 {
15886 return false;
15887 }
15888 else if(localLastUseFrameIndex == localCurrFrameIndex)
15889 {
15890 return true;
15891 }
15892 else // Last use time earlier than current time.
15893 {
15894 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15895 {
15896 localLastUseFrameIndex = localCurrFrameIndex;
15897 }
15898 }
15899 }
15900 }
15901 else
15902 {
15903 #if VMA_STATS_STRING_ENABLED
15904 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15905 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15906 for(;;)
15907 {
15908 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
15909 if(localLastUseFrameIndex == localCurrFrameIndex)
15910 {
15911 break;
15912 }
15913 else // Last use time earlier than current time.
15914 {
15915 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15916 {
15917 localLastUseFrameIndex = localCurrFrameIndex;
15918 }
15919 }
15920 }
15921 #endif
15922
15923 return true;
15924 }
15925 }
15926
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15927 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15928 {
15929 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15930
15931 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15932
15933 // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.
15934 if(pCreateInfo->pMemoryAllocateNext)
15935 {
15936 VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
15937 }
15938
15939 if(newCreateInfo.maxBlockCount == 0)
15940 {
15941 newCreateInfo.maxBlockCount = SIZE_MAX;
15942 }
15943 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15944 {
15945 return VK_ERROR_INITIALIZATION_FAILED;
15946 }
15947 // Memory type index out of range or forbidden.
15948 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
15949 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
15950 {
15951 return VK_ERROR_FEATURE_NOT_PRESENT;
15952 }
15953 if(newCreateInfo.minAllocationAlignment > 0)
15954 {
15955 VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
15956 }
15957
15958 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15959
15960 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15961
15962 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15963 if(res != VK_SUCCESS)
15964 {
15965 vma_delete(this, *pPool);
15966 *pPool = VMA_NULL;
15967 return res;
15968 }
15969
15970 // Add to m_Pools.
15971 {
15972 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15973 (*pPool)->SetId(m_NextPoolId++);
15974 m_Pools.PushBack(*pPool);
15975 }
15976
15977 return VK_SUCCESS;
15978 }
15979
DestroyPool(VmaPool pool)15980 void VmaAllocator_T::DestroyPool(VmaPool pool)
15981 {
15982 // Remove from m_Pools.
15983 {
15984 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15985 m_Pools.Remove(pool);
15986 }
15987
15988 vma_delete(this, pool);
15989 }
15990
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)15991 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15992 {
15993 pool->m_BlockVector.GetPoolStats(pPoolStats);
15994 }
15995
SetCurrentFrameIndex(uint32_t frameIndex)15996 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15997 {
15998 m_CurrentFrameIndex.store(frameIndex);
15999
16000 #if VMA_MEMORY_BUDGET
16001 if(m_UseExtMemoryBudget)
16002 {
16003 UpdateVulkanBudget();
16004 }
16005 #endif // #if VMA_MEMORY_BUDGET
16006 }
16007
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)16008 void VmaAllocator_T::MakePoolAllocationsLost(
16009 VmaPool hPool,
16010 size_t* pLostAllocationCount)
16011 {
16012 hPool->m_BlockVector.MakePoolAllocationsLost(
16013 m_CurrentFrameIndex.load(),
16014 pLostAllocationCount);
16015 }
16016
CheckPoolCorruption(VmaPool hPool)16017 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16018 {
16019 return hPool->m_BlockVector.CheckCorruption();
16020 }
16021
CheckCorruption(uint32_t memoryTypeBits)16022 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16023 {
16024 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16025
16026 // Process default pools.
16027 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16028 {
16029 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
16030 {
16031 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16032 VMA_ASSERT(pBlockVector);
16033 VkResult localRes = pBlockVector->CheckCorruption();
16034 switch(localRes)
16035 {
16036 case VK_ERROR_FEATURE_NOT_PRESENT:
16037 break;
16038 case VK_SUCCESS:
16039 finalRes = VK_SUCCESS;
16040 break;
16041 default:
16042 return localRes;
16043 }
16044 }
16045 }
16046
16047 // Process custom pools.
16048 {
16049 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16050 for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
16051 {
16052 if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16053 {
16054 VkResult localRes = pool->m_BlockVector.CheckCorruption();
16055 switch(localRes)
16056 {
16057 case VK_ERROR_FEATURE_NOT_PRESENT:
16058 break;
16059 case VK_SUCCESS:
16060 finalRes = VK_SUCCESS;
16061 break;
16062 default:
16063 return localRes;
16064 }
16065 }
16066 }
16067 }
16068
16069 return finalRes;
16070 }
16071
CreateLostAllocation(VmaAllocation * pAllocation)16072 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16073 {
16074 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16075 (*pAllocation)->InitLost();
16076 }
16077
16078 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
16079 template<typename T>
16080 struct AtomicTransactionalIncrement
16081 {
16082 public:
16083 typedef std::atomic<T> AtomicT;
~AtomicTransactionalIncrementAtomicTransactionalIncrement16084 ~AtomicTransactionalIncrement()
16085 {
16086 if(m_Atomic)
16087 --(*m_Atomic);
16088 }
IncrementAtomicTransactionalIncrement16089 T Increment(AtomicT* atomic)
16090 {
16091 m_Atomic = atomic;
16092 return m_Atomic->fetch_add(1);
16093 }
CommitAtomicTransactionalIncrement16094 void Commit()
16095 {
16096 m_Atomic = nullptr;
16097 }
16098
16099 private:
16100 AtomicT* m_Atomic = nullptr;
16101 };
16102
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)16103 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16104 {
16105 AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
16106 const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
16107 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
16108 if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
16109 {
16110 return VK_ERROR_TOO_MANY_OBJECTS;
16111 }
16112 #endif
16113
16114 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16115
16116 // HeapSizeLimit is in effect for this heap.
16117 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16118 {
16119 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16120 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16121 for(;;)
16122 {
16123 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16124 if(blockBytesAfterAllocation > heapSize)
16125 {
16126 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16127 }
16128 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16129 {
16130 break;
16131 }
16132 }
16133 }
16134 else
16135 {
16136 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16137 }
16138
16139 // VULKAN CALL vkAllocateMemory.
16140 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16141
16142 if(res == VK_SUCCESS)
16143 {
16144 #if VMA_MEMORY_BUDGET
16145 ++m_Budget.m_OperationsSinceBudgetFetch;
16146 #endif
16147
16148 // Informative callback.
16149 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16150 {
16151 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16152 }
16153
16154 deviceMemoryCountIncrement.Commit();
16155 }
16156 else
16157 {
16158 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16159 }
16160
16161 return res;
16162 }
16163
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)16164 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16165 {
16166 // Informative callback.
16167 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16168 {
16169 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16170 }
16171
16172 // VULKAN CALL vkFreeMemory.
16173 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16174
16175 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16176
16177 --m_DeviceMemoryCount;
16178 }
16179
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)16180 VkResult VmaAllocator_T::BindVulkanBuffer(
16181 VkDeviceMemory memory,
16182 VkDeviceSize memoryOffset,
16183 VkBuffer buffer,
16184 const void* pNext)
16185 {
16186 if(pNext != VMA_NULL)
16187 {
16188 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16189 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16190 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
16191 {
16192 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
16193 bindBufferMemoryInfo.pNext = pNext;
16194 bindBufferMemoryInfo.buffer = buffer;
16195 bindBufferMemoryInfo.memory = memory;
16196 bindBufferMemoryInfo.memoryOffset = memoryOffset;
16197 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16198 }
16199 else
16200 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16201 {
16202 return VK_ERROR_EXTENSION_NOT_PRESENT;
16203 }
16204 }
16205 else
16206 {
16207 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
16208 }
16209 }
16210
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)16211 VkResult VmaAllocator_T::BindVulkanImage(
16212 VkDeviceMemory memory,
16213 VkDeviceSize memoryOffset,
16214 VkImage image,
16215 const void* pNext)
16216 {
16217 if(pNext != VMA_NULL)
16218 {
16219 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16220 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16221 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
16222 {
16223 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
16224 bindBufferMemoryInfo.pNext = pNext;
16225 bindBufferMemoryInfo.image = image;
16226 bindBufferMemoryInfo.memory = memory;
16227 bindBufferMemoryInfo.memoryOffset = memoryOffset;
16228 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16229 }
16230 else
16231 #endif // #if VMA_BIND_MEMORY2
16232 {
16233 return VK_ERROR_EXTENSION_NOT_PRESENT;
16234 }
16235 }
16236 else
16237 {
16238 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
16239 }
16240 }
16241
Map(VmaAllocation hAllocation,void ** ppData)16242 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
16243 {
16244 if(hAllocation->CanBecomeLost())
16245 {
16246 return VK_ERROR_MEMORY_MAP_FAILED;
16247 }
16248
16249 switch(hAllocation->GetType())
16250 {
16251 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16252 {
16253 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16254 char *pBytes = VMA_NULL;
16255 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
16256 if(res == VK_SUCCESS)
16257 {
16258 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
16259 hAllocation->BlockAllocMap();
16260 }
16261 return res;
16262 }
16263 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16264 return hAllocation->DedicatedAllocMap(this, ppData);
16265 default:
16266 VMA_ASSERT(0);
16267 return VK_ERROR_MEMORY_MAP_FAILED;
16268 }
16269 }
16270
Unmap(VmaAllocation hAllocation)16271 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
16272 {
16273 switch(hAllocation->GetType())
16274 {
16275 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16276 {
16277 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16278 hAllocation->BlockAllocUnmap();
16279 pBlock->Unmap(this, 1);
16280 }
16281 break;
16282 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16283 hAllocation->DedicatedAllocUnmap(this);
16284 break;
16285 default:
16286 VMA_ASSERT(0);
16287 }
16288 }
16289
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)16290 VkResult VmaAllocator_T::BindBufferMemory(
16291 VmaAllocation hAllocation,
16292 VkDeviceSize allocationLocalOffset,
16293 VkBuffer hBuffer,
16294 const void* pNext)
16295 {
16296 VkResult res = VK_SUCCESS;
16297 switch(hAllocation->GetType())
16298 {
16299 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16300 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
16301 break;
16302 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16303 {
16304 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16305 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
16306 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
16307 break;
16308 }
16309 default:
16310 VMA_ASSERT(0);
16311 }
16312 return res;
16313 }
16314
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)16315 VkResult VmaAllocator_T::BindImageMemory(
16316 VmaAllocation hAllocation,
16317 VkDeviceSize allocationLocalOffset,
16318 VkImage hImage,
16319 const void* pNext)
16320 {
16321 VkResult res = VK_SUCCESS;
16322 switch(hAllocation->GetType())
16323 {
16324 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16325 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
16326 break;
16327 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16328 {
16329 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
16330 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
16331 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
16332 break;
16333 }
16334 default:
16335 VMA_ASSERT(0);
16336 }
16337 return res;
16338 }
16339
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)16340 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
16341 VmaAllocation hAllocation,
16342 VkDeviceSize offset, VkDeviceSize size,
16343 VMA_CACHE_OPERATION op)
16344 {
16345 VkResult res = VK_SUCCESS;
16346
16347 VkMappedMemoryRange memRange = {};
16348 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
16349 {
16350 switch(op)
16351 {
16352 case VMA_CACHE_FLUSH:
16353 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
16354 break;
16355 case VMA_CACHE_INVALIDATE:
16356 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
16357 break;
16358 default:
16359 VMA_ASSERT(0);
16360 }
16361 }
16362 // else: Just ignore this call.
16363 return res;
16364 }
16365
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)16366 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
16367 uint32_t allocationCount,
16368 const VmaAllocation* allocations,
16369 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
16370 VMA_CACHE_OPERATION op)
16371 {
16372 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
16373 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
16374 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
16375
16376 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16377 {
16378 const VmaAllocation alloc = allocations[allocIndex];
16379 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
16380 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
16381 VkMappedMemoryRange newRange;
16382 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
16383 {
16384 ranges.push_back(newRange);
16385 }
16386 }
16387
16388 VkResult res = VK_SUCCESS;
16389 if(!ranges.empty())
16390 {
16391 switch(op)
16392 {
16393 case VMA_CACHE_FLUSH:
16394 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
16395 break;
16396 case VMA_CACHE_INVALIDATE:
16397 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
16398 break;
16399 default:
16400 VMA_ASSERT(0);
16401 }
16402 }
16403 // else: Just ignore this call.
16404 return res;
16405 }
16406
FreeDedicatedMemory(const VmaAllocation allocation)16407 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
16408 {
16409 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
16410
16411 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16412 {
16413 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16414 DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
16415 dedicatedAllocations.Remove(allocation);
16416 }
16417
16418 VkDeviceMemory hMemory = allocation->GetMemory();
16419
16420 /*
16421 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16422 before vkFreeMemory.
16423
16424 if(allocation->GetMappedData() != VMA_NULL)
16425 {
16426 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16427 }
16428 */
16429
16430 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
16431
16432 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
16433 }
16434
CalculateGpuDefragmentationMemoryTypeBits()16435 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
16436 {
16437 VkBufferCreateInfo dummyBufCreateInfo;
16438 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
16439
16440 uint32_t memoryTypeBits = 0;
16441
16442 // Create buffer.
16443 VkBuffer buf = VK_NULL_HANDLE;
16444 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
16445 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
16446 if(res == VK_SUCCESS)
16447 {
16448 // Query for supported memory types.
16449 VkMemoryRequirements memReq;
16450 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
16451 memoryTypeBits = memReq.memoryTypeBits;
16452
16453 // Destroy buffer.
16454 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
16455 }
16456
16457 return memoryTypeBits;
16458 }
16459
CalculateGlobalMemoryTypeBits()16460 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
16461 {
16462 // Make sure memory information is already fetched.
16463 VMA_ASSERT(GetMemoryTypeCount() > 0);
16464
16465 uint32_t memoryTypeBits = UINT32_MAX;
16466
16467 if(!m_UseAmdDeviceCoherentMemory)
16468 {
16469 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
16470 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16471 {
16472 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
16473 {
16474 memoryTypeBits &= ~(1u << memTypeIndex);
16475 }
16476 }
16477 }
16478
16479 return memoryTypeBits;
16480 }
16481
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)16482 bool VmaAllocator_T::GetFlushOrInvalidateRange(
16483 VmaAllocation allocation,
16484 VkDeviceSize offset, VkDeviceSize size,
16485 VkMappedMemoryRange& outRange) const
16486 {
16487 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16488 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
16489 {
16490 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
16491 const VkDeviceSize allocationSize = allocation->GetSize();
16492 VMA_ASSERT(offset <= allocationSize);
16493
16494 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
16495 outRange.pNext = VMA_NULL;
16496 outRange.memory = allocation->GetMemory();
16497
16498 switch(allocation->GetType())
16499 {
16500 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16501 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
16502 if(size == VK_WHOLE_SIZE)
16503 {
16504 outRange.size = allocationSize - outRange.offset;
16505 }
16506 else
16507 {
16508 VMA_ASSERT(offset + size <= allocationSize);
16509 outRange.size = VMA_MIN(
16510 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
16511 allocationSize - outRange.offset);
16512 }
16513 break;
16514 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16515 {
16516 // 1. Still within this allocation.
16517 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
16518 if(size == VK_WHOLE_SIZE)
16519 {
16520 size = allocationSize - offset;
16521 }
16522 else
16523 {
16524 VMA_ASSERT(offset + size <= allocationSize);
16525 }
16526 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
16527
16528 // 2. Adjust to whole block.
16529 const VkDeviceSize allocationOffset = allocation->GetOffset();
16530 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
16531 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
16532 outRange.offset += allocationOffset;
16533 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
16534
16535 break;
16536 }
16537 default:
16538 VMA_ASSERT(0);
16539 }
16540 return true;
16541 }
16542 return false;
16543 }
16544
16545 #if VMA_MEMORY_BUDGET
16546
UpdateVulkanBudget()16547 void VmaAllocator_T::UpdateVulkanBudget()
16548 {
16549 VMA_ASSERT(m_UseExtMemoryBudget);
16550
16551 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
16552
16553 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
16554 VmaPnextChainPushFront(&memProps, &budgetProps);
16555
16556 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
16557
16558 {
16559 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
16560
16561 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
16562 {
16563 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
16564 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
16565 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
16566
16567 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
16568 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
16569 {
16570 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16571 }
16572 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
16573 {
16574 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
16575 }
16576 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
16577 {
16578 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16579 }
16580 }
16581 m_Budget.m_OperationsSinceBudgetFetch = 0;
16582 }
16583 }
16584
16585 #endif // #if VMA_MEMORY_BUDGET
16586
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)16587 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
16588 {
16589 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
16590 !hAllocation->CanBecomeLost() &&
16591 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
16592 {
16593 void* pData = VMA_NULL;
16594 VkResult res = Map(hAllocation, &pData);
16595 if(res == VK_SUCCESS)
16596 {
16597 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
16598 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
16599 Unmap(hAllocation);
16600 }
16601 else
16602 {
16603 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
16604 }
16605 }
16606 }
16607
GetGpuDefragmentationMemoryTypeBits()16608 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
16609 {
16610 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
16611 if(memoryTypeBits == UINT32_MAX)
16612 {
16613 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
16614 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
16615 }
16616 return memoryTypeBits;
16617 }
16618
16619 #if VMA_STATS_STRING_ENABLED
16620
PrintDetailedMap(VmaJsonWriter & json)16621 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
16622 {
16623 bool dedicatedAllocationsStarted = false;
16624 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16625 {
16626 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16627 DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
16628 if(!dedicatedAllocList.IsEmpty())
16629 {
16630 if(dedicatedAllocationsStarted == false)
16631 {
16632 dedicatedAllocationsStarted = true;
16633 json.WriteString("DedicatedAllocations");
16634 json.BeginObject();
16635 }
16636
16637 json.BeginString("Type ");
16638 json.ContinueString(memTypeIndex);
16639 json.EndString();
16640
16641 json.BeginArray();
16642
16643 for(VmaAllocation alloc = dedicatedAllocList.Front();
16644 alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
16645 {
16646 json.BeginObject(true);
16647 alloc->PrintParameters(json);
16648 json.EndObject();
16649 }
16650
16651 json.EndArray();
16652 }
16653 }
16654 if(dedicatedAllocationsStarted)
16655 {
16656 json.EndObject();
16657 }
16658
16659 {
16660 bool allocationsStarted = false;
16661 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16662 {
16663 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
16664 {
16665 if(allocationsStarted == false)
16666 {
16667 allocationsStarted = true;
16668 json.WriteString("DefaultPools");
16669 json.BeginObject();
16670 }
16671
16672 json.BeginString("Type ");
16673 json.ContinueString(memTypeIndex);
16674 json.EndString();
16675
16676 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
16677 }
16678 }
16679 if(allocationsStarted)
16680 {
16681 json.EndObject();
16682 }
16683 }
16684
16685 // Custom pools
16686 {
16687 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16688 if(!m_Pools.IsEmpty())
16689 {
16690 json.WriteString("Pools");
16691 json.BeginObject();
16692 for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
16693 {
16694 json.BeginString();
16695 json.ContinueString(pool->GetId());
16696 json.EndString();
16697
16698 pool->m_BlockVector.PrintDetailedMap(json);
16699 }
16700 json.EndObject();
16701 }
16702 }
16703 }
16704
16705 #endif // #if VMA_STATS_STRING_ENABLED
16706
16707 ////////////////////////////////////////////////////////////////////////////////
16708 // VmaVirtualBlock_T
16709
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo & createInfo)16710 VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) :
16711 m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
16712 m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
16713 {
16714 const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
16715 switch(algorithm)
16716 {
16717 case 0:
16718 m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Generic)(VK_NULL_HANDLE, true);
16719 break;
16720 case VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT:
16721 m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Buddy)(VK_NULL_HANDLE, true);
16722 break;
16723 case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
16724 m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, true);
16725 break;
16726 default:
16727 VMA_ASSERT(0);
16728 }
16729
16730 m_Metadata->Init(createInfo.size);
16731 }
16732
~VmaVirtualBlock_T()16733 VmaVirtualBlock_T::~VmaVirtualBlock_T()
16734 {
16735 // This is an important assert!!!
16736 // Hitting it means you have some memory leak - unreleased virtual allocations.
16737 VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
16738
16739 vma_delete(GetAllocationCallbacks(), m_Metadata);
16740 }
16741
Allocate(const VmaVirtualAllocationCreateInfo & createInfo,VkDeviceSize & outOffset)16742 VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset)
16743 {
16744 outOffset = VK_WHOLE_SIZE;
16745 VmaAllocationRequest request = {};
16746 if(m_Metadata->CreateAllocationRequest(
16747 0, // currentFrameIndex - unimportant
16748 0, // frameInUseCount - unimportant
16749 1, // bufferImageGranularity
16750 createInfo.size, // allocSize
16751 VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment
16752 (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
16753 VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
16754 false, // canMakeOthersLost
16755 createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
16756 &request))
16757 {
16758 m_Metadata->Alloc(request,
16759 VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
16760 createInfo.pUserData);
16761 outOffset = request.offset;
16762 return VK_SUCCESS;
16763 }
16764 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16765 }
16766
16767 #if VMA_STATS_STRING_ENABLED
BuildStatsString(bool detailedMap,VmaStringBuilder & sb)16768 void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
16769 {
16770 VmaJsonWriter json(GetAllocationCallbacks(), sb);
16771 json.BeginObject();
16772
16773 VmaStatInfo stat = {};
16774 CalculateStats(stat);
16775
16776 json.WriteString("Stats");
16777 VmaPrintStatInfo(json, stat);
16778
16779 if(detailedMap)
16780 {
16781 json.WriteString("Details");
16782 m_Metadata->PrintDetailedMap(json);
16783 }
16784
16785 json.EndObject();
16786 }
16787 #endif // #if VMA_STATS_STRING_ENABLED
16788
16789 ////////////////////////////////////////////////////////////////////////////////
16790 // Public interface
16791
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)16792 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
16793 const VmaAllocatorCreateInfo* pCreateInfo,
16794 VmaAllocator* pAllocator)
16795 {
16796 VMA_ASSERT(pCreateInfo && pAllocator);
16797 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
16798 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
16799 VMA_DEBUG_LOG("vmaCreateAllocator");
16800 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
16801 VkResult result = (*pAllocator)->Init(pCreateInfo);
16802 if(result < 0)
16803 {
16804 vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
16805 *pAllocator = VK_NULL_HANDLE;
16806 }
16807 return result;
16808 }
16809
vmaDestroyAllocator(VmaAllocator allocator)16810 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
16811 VmaAllocator allocator)
16812 {
16813 if(allocator != VK_NULL_HANDLE)
16814 {
16815 VMA_DEBUG_LOG("vmaDestroyAllocator");
16816 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
16817 vma_delete(&allocationCallbacks, allocator);
16818 }
16819 }
16820
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)16821 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
16822 {
16823 VMA_ASSERT(allocator && pAllocatorInfo);
16824 pAllocatorInfo->instance = allocator->m_hInstance;
16825 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
16826 pAllocatorInfo->device = allocator->m_hDevice;
16827 }
16828
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)16829 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
16830 VmaAllocator allocator,
16831 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
16832 {
16833 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
16834 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
16835 }
16836
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)16837 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
16838 VmaAllocator allocator,
16839 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
16840 {
16841 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
16842 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
16843 }
16844
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)16845 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
16846 VmaAllocator allocator,
16847 uint32_t memoryTypeIndex,
16848 VkMemoryPropertyFlags* pFlags)
16849 {
16850 VMA_ASSERT(allocator && pFlags);
16851 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
16852 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
16853 }
16854
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)16855 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
16856 VmaAllocator allocator,
16857 uint32_t frameIndex)
16858 {
16859 VMA_ASSERT(allocator);
16860 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
16861
16862 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16863
16864 allocator->SetCurrentFrameIndex(frameIndex);
16865 }
16866
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)16867 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
16868 VmaAllocator allocator,
16869 VmaStats* pStats)
16870 {
16871 VMA_ASSERT(allocator && pStats);
16872 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16873 allocator->CalculateStats(pStats);
16874 }
16875
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)16876 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
16877 VmaAllocator allocator,
16878 VmaBudget* pBudget)
16879 {
16880 VMA_ASSERT(allocator && pBudget);
16881 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16882 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
16883 }
16884
16885 #if VMA_STATS_STRING_ENABLED
16886
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)16887 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
16888 VmaAllocator allocator,
16889 char** ppStatsString,
16890 VkBool32 detailedMap)
16891 {
16892 VMA_ASSERT(allocator && ppStatsString);
16893 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16894
16895 VmaStringBuilder sb(allocator->GetAllocationCallbacks());
16896 {
16897 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
16898 json.BeginObject();
16899
16900 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
16901 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
16902
16903 VmaStats stats;
16904 allocator->CalculateStats(&stats);
16905
16906 json.WriteString("Total");
16907 VmaPrintStatInfo(json, stats.total);
16908
16909 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
16910 {
16911 json.BeginString("Heap ");
16912 json.ContinueString(heapIndex);
16913 json.EndString();
16914 json.BeginObject();
16915
16916 json.WriteString("Size");
16917 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
16918
16919 json.WriteString("Flags");
16920 json.BeginArray(true);
16921 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
16922 {
16923 json.WriteString("DEVICE_LOCAL");
16924 }
16925 json.EndArray();
16926
16927 json.WriteString("Budget");
16928 json.BeginObject();
16929 {
16930 json.WriteString("BlockBytes");
16931 json.WriteNumber(budget[heapIndex].blockBytes);
16932 json.WriteString("AllocationBytes");
16933 json.WriteNumber(budget[heapIndex].allocationBytes);
16934 json.WriteString("Usage");
16935 json.WriteNumber(budget[heapIndex].usage);
16936 json.WriteString("Budget");
16937 json.WriteNumber(budget[heapIndex].budget);
16938 }
16939 json.EndObject();
16940
16941 if(stats.memoryHeap[heapIndex].blockCount > 0)
16942 {
16943 json.WriteString("Stats");
16944 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
16945 }
16946
16947 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
16948 {
16949 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
16950 {
16951 json.BeginString("Type ");
16952 json.ContinueString(typeIndex);
16953 json.EndString();
16954
16955 json.BeginObject();
16956
16957 json.WriteString("Flags");
16958 json.BeginArray(true);
16959 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
16960 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
16961 {
16962 json.WriteString("DEVICE_LOCAL");
16963 }
16964 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
16965 {
16966 json.WriteString("HOST_VISIBLE");
16967 }
16968 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
16969 {
16970 json.WriteString("HOST_COHERENT");
16971 }
16972 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
16973 {
16974 json.WriteString("HOST_CACHED");
16975 }
16976 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
16977 {
16978 json.WriteString("LAZILY_ALLOCATED");
16979 }
16980 #if VMA_VULKAN_VERSION >= 1001000
16981 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
16982 {
16983 json.WriteString("PROTECTED");
16984 }
16985 #endif // #if VMA_VULKAN_VERSION >= 1001000
16986 #if VK_AMD_device_coherent_memory
16987 if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
16988 {
16989 json.WriteString("DEVICE_COHERENT");
16990 }
16991 if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
16992 {
16993 json.WriteString("DEVICE_UNCACHED");
16994 }
16995 #endif // #if VK_AMD_device_coherent_memory
16996 json.EndArray();
16997
16998 if(stats.memoryType[typeIndex].blockCount > 0)
16999 {
17000 json.WriteString("Stats");
17001 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17002 }
17003
17004 json.EndObject();
17005 }
17006 }
17007
17008 json.EndObject();
17009 }
17010 if(detailedMap == VK_TRUE)
17011 {
17012 allocator->PrintDetailedMap(json);
17013 }
17014
17015 json.EndObject();
17016 }
17017
17018 *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
17019 }
17020
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)17021 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17022 VmaAllocator allocator,
17023 char* pStatsString)
17024 {
17025 if(pStatsString != VMA_NULL)
17026 {
17027 VMA_ASSERT(allocator);
17028 VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
17029 }
17030 }
17031
17032 #endif // #if VMA_STATS_STRING_ENABLED
17033
17034 /*
17035 This function is not protected by any mutex because it just reads immutable data.
17036 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17037 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17038 VmaAllocator allocator,
17039 uint32_t memoryTypeBits,
17040 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17041 uint32_t* pMemoryTypeIndex)
17042 {
17043 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17044 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17045 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17046
17047 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17048
17049 if(pAllocationCreateInfo->memoryTypeBits != 0)
17050 {
17051 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17052 }
17053
17054 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17055 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17056 uint32_t notPreferredFlags = 0;
17057
17058 // Convert usage to requiredFlags and preferredFlags.
17059 switch(pAllocationCreateInfo->usage)
17060 {
17061 case VMA_MEMORY_USAGE_UNKNOWN:
17062 break;
17063 case VMA_MEMORY_USAGE_GPU_ONLY:
17064 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17065 {
17066 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17067 }
17068 break;
17069 case VMA_MEMORY_USAGE_CPU_ONLY:
17070 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17071 break;
17072 case VMA_MEMORY_USAGE_CPU_TO_GPU:
17073 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17074 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17075 {
17076 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17077 }
17078 break;
17079 case VMA_MEMORY_USAGE_GPU_TO_CPU:
17080 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17081 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17082 break;
17083 case VMA_MEMORY_USAGE_CPU_COPY:
17084 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17085 break;
17086 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
17087 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17088 break;
17089 default:
17090 VMA_ASSERT(0);
17091 break;
17092 }
17093
17094 // Avoid DEVICE_COHERENT unless explicitly requested.
17095 if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17096 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17097 {
17098 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17099 }
17100
17101 *pMemoryTypeIndex = UINT32_MAX;
17102 uint32_t minCost = UINT32_MAX;
17103 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17104 memTypeIndex < allocator->GetMemoryTypeCount();
17105 ++memTypeIndex, memTypeBit <<= 1)
17106 {
17107 // This memory type is acceptable according to memoryTypeBits bitmask.
17108 if((memTypeBit & memoryTypeBits) != 0)
17109 {
17110 const VkMemoryPropertyFlags currFlags =
17111 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17112 // This memory type contains requiredFlags.
17113 if((requiredFlags & ~currFlags) == 0)
17114 {
17115 // Calculate cost as number of bits from preferredFlags not present in this memory type.
17116 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17117 VmaCountBitsSet(currFlags & notPreferredFlags);
17118 // Remember memory type with lowest cost.
17119 if(currCost < minCost)
17120 {
17121 *pMemoryTypeIndex = memTypeIndex;
17122 if(currCost == 0)
17123 {
17124 return VK_SUCCESS;
17125 }
17126 minCost = currCost;
17127 }
17128 }
17129 }
17130 }
17131 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17132 }
17133
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17134 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17135 VmaAllocator allocator,
17136 const VkBufferCreateInfo* pBufferCreateInfo,
17137 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17138 uint32_t* pMemoryTypeIndex)
17139 {
17140 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17141 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17142 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17143 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17144
17145 const VkDevice hDev = allocator->m_hDevice;
17146 VkBuffer hBuffer = VK_NULL_HANDLE;
17147 const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
17148 VkResult res = funcs->vkCreateBuffer(
17149 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17150 if(res == VK_SUCCESS)
17151 {
17152 VkMemoryRequirements memReq = {};
17153 funcs->vkGetBufferMemoryRequirements(
17154 hDev, hBuffer, &memReq);
17155
17156 res = vmaFindMemoryTypeIndex(
17157 allocator,
17158 memReq.memoryTypeBits,
17159 pAllocationCreateInfo,
17160 pMemoryTypeIndex);
17161
17162 funcs->vkDestroyBuffer(
17163 hDev, hBuffer, allocator->GetAllocationCallbacks());
17164 }
17165 return res;
17166 }
17167
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17168 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17169 VmaAllocator allocator,
17170 const VkImageCreateInfo* pImageCreateInfo,
17171 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17172 uint32_t* pMemoryTypeIndex)
17173 {
17174 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17175 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17176 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17177 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17178
17179 const VkDevice hDev = allocator->m_hDevice;
17180 VkImage hImage = VK_NULL_HANDLE;
17181 const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
17182 VkResult res = funcs->vkCreateImage(
17183 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17184 if(res == VK_SUCCESS)
17185 {
17186 VkMemoryRequirements memReq = {};
17187 funcs->vkGetImageMemoryRequirements(
17188 hDev, hImage, &memReq);
17189
17190 res = vmaFindMemoryTypeIndex(
17191 allocator,
17192 memReq.memoryTypeBits,
17193 pAllocationCreateInfo,
17194 pMemoryTypeIndex);
17195
17196 funcs->vkDestroyImage(
17197 hDev, hImage, allocator->GetAllocationCallbacks());
17198 }
17199 return res;
17200 }
17201
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17202 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17203 VmaAllocator allocator,
17204 const VmaPoolCreateInfo* pCreateInfo,
17205 VmaPool* pPool)
17206 {
17207 VMA_ASSERT(allocator && pCreateInfo && pPool);
17208
17209 VMA_DEBUG_LOG("vmaCreatePool");
17210
17211 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17212
17213 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17214
17215 #if VMA_RECORDING_ENABLED
17216 if(allocator->GetRecorder() != VMA_NULL)
17217 {
17218 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17219 }
17220 #endif
17221
17222 return res;
17223 }
17224
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)17225 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17226 VmaAllocator allocator,
17227 VmaPool pool)
17228 {
17229 VMA_ASSERT(allocator);
17230
17231 if(pool == VK_NULL_HANDLE)
17232 {
17233 return;
17234 }
17235
17236 VMA_DEBUG_LOG("vmaDestroyPool");
17237
17238 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17239
17240 #if VMA_RECORDING_ENABLED
17241 if(allocator->GetRecorder() != VMA_NULL)
17242 {
17243 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17244 }
17245 #endif
17246
17247 allocator->DestroyPool(pool);
17248 }
17249
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)17250 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17251 VmaAllocator allocator,
17252 VmaPool pool,
17253 VmaPoolStats* pPoolStats)
17254 {
17255 VMA_ASSERT(allocator && pool && pPoolStats);
17256
17257 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17258
17259 allocator->GetPoolStats(pool, pPoolStats);
17260 }
17261
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)17262 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17263 VmaAllocator allocator,
17264 VmaPool pool,
17265 size_t* pLostAllocationCount)
17266 {
17267 VMA_ASSERT(allocator && pool);
17268
17269 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17270
17271 #if VMA_RECORDING_ENABLED
17272 if(allocator->GetRecorder() != VMA_NULL)
17273 {
17274 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
17275 }
17276 #endif
17277
17278 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
17279 }
17280
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)17281 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
17282 {
17283 VMA_ASSERT(allocator && pool);
17284
17285 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17286
17287 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
17288
17289 return allocator->CheckPoolCorruption(pool);
17290 }
17291
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)17292 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
17293 VmaAllocator allocator,
17294 VmaPool pool,
17295 const char** ppName)
17296 {
17297 VMA_ASSERT(allocator && pool && ppName);
17298
17299 VMA_DEBUG_LOG("vmaGetPoolName");
17300
17301 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17302
17303 *ppName = pool->GetName();
17304 }
17305
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)17306 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
17307 VmaAllocator allocator,
17308 VmaPool pool,
17309 const char* pName)
17310 {
17311 VMA_ASSERT(allocator && pool);
17312
17313 VMA_DEBUG_LOG("vmaSetPoolName");
17314
17315 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17316
17317 pool->SetName(pName);
17318
17319 #if VMA_RECORDING_ENABLED
17320 if(allocator->GetRecorder() != VMA_NULL)
17321 {
17322 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
17323 }
17324 #endif
17325 }
17326
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17327 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
17328 VmaAllocator allocator,
17329 const VkMemoryRequirements* pVkMemoryRequirements,
17330 const VmaAllocationCreateInfo* pCreateInfo,
17331 VmaAllocation* pAllocation,
17332 VmaAllocationInfo* pAllocationInfo)
17333 {
17334 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
17335
17336 VMA_DEBUG_LOG("vmaAllocateMemory");
17337
17338 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17339
17340 VkResult result = allocator->AllocateMemory(
17341 *pVkMemoryRequirements,
17342 false, // requiresDedicatedAllocation
17343 false, // prefersDedicatedAllocation
17344 VK_NULL_HANDLE, // dedicatedBuffer
17345 UINT32_MAX, // dedicatedBufferUsage
17346 VK_NULL_HANDLE, // dedicatedImage
17347 *pCreateInfo,
17348 VMA_SUBALLOCATION_TYPE_UNKNOWN,
17349 1, // allocationCount
17350 pAllocation);
17351
17352 #if VMA_RECORDING_ENABLED
17353 if(allocator->GetRecorder() != VMA_NULL)
17354 {
17355 allocator->GetRecorder()->RecordAllocateMemory(
17356 allocator->GetCurrentFrameIndex(),
17357 *pVkMemoryRequirements,
17358 *pCreateInfo,
17359 *pAllocation);
17360 }
17361 #endif
17362
17363 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
17364 {
17365 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17366 }
17367
17368 return result;
17369 }
17370
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)17371 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
17372 VmaAllocator allocator,
17373 const VkMemoryRequirements* pVkMemoryRequirements,
17374 const VmaAllocationCreateInfo* pCreateInfo,
17375 size_t allocationCount,
17376 VmaAllocation* pAllocations,
17377 VmaAllocationInfo* pAllocationInfo)
17378 {
17379 if(allocationCount == 0)
17380 {
17381 return VK_SUCCESS;
17382 }
17383
17384 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
17385
17386 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
17387
17388 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17389
17390 VkResult result = allocator->AllocateMemory(
17391 *pVkMemoryRequirements,
17392 false, // requiresDedicatedAllocation
17393 false, // prefersDedicatedAllocation
17394 VK_NULL_HANDLE, // dedicatedBuffer
17395 UINT32_MAX, // dedicatedBufferUsage
17396 VK_NULL_HANDLE, // dedicatedImage
17397 *pCreateInfo,
17398 VMA_SUBALLOCATION_TYPE_UNKNOWN,
17399 allocationCount,
17400 pAllocations);
17401
17402 #if VMA_RECORDING_ENABLED
17403 if(allocator->GetRecorder() != VMA_NULL)
17404 {
17405 allocator->GetRecorder()->RecordAllocateMemoryPages(
17406 allocator->GetCurrentFrameIndex(),
17407 *pVkMemoryRequirements,
17408 *pCreateInfo,
17409 (uint64_t)allocationCount,
17410 pAllocations);
17411 }
17412 #endif
17413
17414 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
17415 {
17416 for(size_t i = 0; i < allocationCount; ++i)
17417 {
17418 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
17419 }
17420 }
17421
17422 return result;
17423 }
17424
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17425 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
17426 VmaAllocator allocator,
17427 VkBuffer buffer,
17428 const VmaAllocationCreateInfo* pCreateInfo,
17429 VmaAllocation* pAllocation,
17430 VmaAllocationInfo* pAllocationInfo)
17431 {
17432 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
17433
17434 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
17435
17436 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17437
17438 VkMemoryRequirements vkMemReq = {};
17439 bool requiresDedicatedAllocation = false;
17440 bool prefersDedicatedAllocation = false;
17441 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
17442 requiresDedicatedAllocation,
17443 prefersDedicatedAllocation);
17444
17445 VkResult result = allocator->AllocateMemory(
17446 vkMemReq,
17447 requiresDedicatedAllocation,
17448 prefersDedicatedAllocation,
17449 buffer, // dedicatedBuffer
17450 UINT32_MAX, // dedicatedBufferUsage
17451 VK_NULL_HANDLE, // dedicatedImage
17452 *pCreateInfo,
17453 VMA_SUBALLOCATION_TYPE_BUFFER,
17454 1, // allocationCount
17455 pAllocation);
17456
17457 #if VMA_RECORDING_ENABLED
17458 if(allocator->GetRecorder() != VMA_NULL)
17459 {
17460 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
17461 allocator->GetCurrentFrameIndex(),
17462 vkMemReq,
17463 requiresDedicatedAllocation,
17464 prefersDedicatedAllocation,
17465 *pCreateInfo,
17466 *pAllocation);
17467 }
17468 #endif
17469
17470 if(pAllocationInfo && result == VK_SUCCESS)
17471 {
17472 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17473 }
17474
17475 return result;
17476 }
17477
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17478 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
17479 VmaAllocator allocator,
17480 VkImage image,
17481 const VmaAllocationCreateInfo* pCreateInfo,
17482 VmaAllocation* pAllocation,
17483 VmaAllocationInfo* pAllocationInfo)
17484 {
17485 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
17486
17487 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
17488
17489 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17490
17491 VkMemoryRequirements vkMemReq = {};
17492 bool requiresDedicatedAllocation = false;
17493 bool prefersDedicatedAllocation = false;
17494 allocator->GetImageMemoryRequirements(image, vkMemReq,
17495 requiresDedicatedAllocation, prefersDedicatedAllocation);
17496
17497 VkResult result = allocator->AllocateMemory(
17498 vkMemReq,
17499 requiresDedicatedAllocation,
17500 prefersDedicatedAllocation,
17501 VK_NULL_HANDLE, // dedicatedBuffer
17502 UINT32_MAX, // dedicatedBufferUsage
17503 image, // dedicatedImage
17504 *pCreateInfo,
17505 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
17506 1, // allocationCount
17507 pAllocation);
17508
17509 #if VMA_RECORDING_ENABLED
17510 if(allocator->GetRecorder() != VMA_NULL)
17511 {
17512 allocator->GetRecorder()->RecordAllocateMemoryForImage(
17513 allocator->GetCurrentFrameIndex(),
17514 vkMemReq,
17515 requiresDedicatedAllocation,
17516 prefersDedicatedAllocation,
17517 *pCreateInfo,
17518 *pAllocation);
17519 }
17520 #endif
17521
17522 if(pAllocationInfo && result == VK_SUCCESS)
17523 {
17524 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17525 }
17526
17527 return result;
17528 }
17529
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)17530 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
17531 VmaAllocator allocator,
17532 VmaAllocation allocation)
17533 {
17534 VMA_ASSERT(allocator);
17535
17536 if(allocation == VK_NULL_HANDLE)
17537 {
17538 return;
17539 }
17540
17541 VMA_DEBUG_LOG("vmaFreeMemory");
17542
17543 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17544
17545 #if VMA_RECORDING_ENABLED
17546 if(allocator->GetRecorder() != VMA_NULL)
17547 {
17548 allocator->GetRecorder()->RecordFreeMemory(
17549 allocator->GetCurrentFrameIndex(),
17550 allocation);
17551 }
17552 #endif
17553
17554 allocator->FreeMemory(
17555 1, // allocationCount
17556 &allocation);
17557 }
17558
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)17559 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
17560 VmaAllocator allocator,
17561 size_t allocationCount,
17562 const VmaAllocation* pAllocations)
17563 {
17564 if(allocationCount == 0)
17565 {
17566 return;
17567 }
17568
17569 VMA_ASSERT(allocator);
17570
17571 VMA_DEBUG_LOG("vmaFreeMemoryPages");
17572
17573 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17574
17575 #if VMA_RECORDING_ENABLED
17576 if(allocator->GetRecorder() != VMA_NULL)
17577 {
17578 allocator->GetRecorder()->RecordFreeMemoryPages(
17579 allocator->GetCurrentFrameIndex(),
17580 (uint64_t)allocationCount,
17581 pAllocations);
17582 }
17583 #endif
17584
17585 allocator->FreeMemory(allocationCount, pAllocations);
17586 }
17587
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)17588 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
17589 VmaAllocator allocator,
17590 VmaAllocation allocation,
17591 VmaAllocationInfo* pAllocationInfo)
17592 {
17593 VMA_ASSERT(allocator && allocation && pAllocationInfo);
17594
17595 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17596
17597 #if VMA_RECORDING_ENABLED
17598 if(allocator->GetRecorder() != VMA_NULL)
17599 {
17600 allocator->GetRecorder()->RecordGetAllocationInfo(
17601 allocator->GetCurrentFrameIndex(),
17602 allocation);
17603 }
17604 #endif
17605
17606 allocator->GetAllocationInfo(allocation, pAllocationInfo);
17607 }
17608
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)17609 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
17610 VmaAllocator allocator,
17611 VmaAllocation allocation)
17612 {
17613 VMA_ASSERT(allocator && allocation);
17614
17615 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17616
17617 #if VMA_RECORDING_ENABLED
17618 if(allocator->GetRecorder() != VMA_NULL)
17619 {
17620 allocator->GetRecorder()->RecordTouchAllocation(
17621 allocator->GetCurrentFrameIndex(),
17622 allocation);
17623 }
17624 #endif
17625
17626 return allocator->TouchAllocation(allocation);
17627 }
17628
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)17629 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
17630 VmaAllocator allocator,
17631 VmaAllocation allocation,
17632 void* pUserData)
17633 {
17634 VMA_ASSERT(allocator && allocation);
17635
17636 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17637
17638 allocation->SetUserData(allocator, pUserData);
17639
17640 #if VMA_RECORDING_ENABLED
17641 if(allocator->GetRecorder() != VMA_NULL)
17642 {
17643 allocator->GetRecorder()->RecordSetAllocationUserData(
17644 allocator->GetCurrentFrameIndex(),
17645 allocation,
17646 pUserData);
17647 }
17648 #endif
17649 }
17650
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)17651 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
17652 VmaAllocator allocator,
17653 VmaAllocation* pAllocation)
17654 {
17655 VMA_ASSERT(allocator && pAllocation);
17656
17657 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17658
17659 allocator->CreateLostAllocation(pAllocation);
17660
17661 #if VMA_RECORDING_ENABLED
17662 if(allocator->GetRecorder() != VMA_NULL)
17663 {
17664 allocator->GetRecorder()->RecordCreateLostAllocation(
17665 allocator->GetCurrentFrameIndex(),
17666 *pAllocation);
17667 }
17668 #endif
17669 }
17670
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)17671 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
17672 VmaAllocator allocator,
17673 VmaAllocation allocation,
17674 void** ppData)
17675 {
17676 VMA_ASSERT(allocator && allocation && ppData);
17677
17678 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17679
17680 VkResult res = allocator->Map(allocation, ppData);
17681
17682 #if VMA_RECORDING_ENABLED
17683 if(allocator->GetRecorder() != VMA_NULL)
17684 {
17685 allocator->GetRecorder()->RecordMapMemory(
17686 allocator->GetCurrentFrameIndex(),
17687 allocation);
17688 }
17689 #endif
17690
17691 return res;
17692 }
17693
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)17694 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
17695 VmaAllocator allocator,
17696 VmaAllocation allocation)
17697 {
17698 VMA_ASSERT(allocator && allocation);
17699
17700 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17701
17702 #if VMA_RECORDING_ENABLED
17703 if(allocator->GetRecorder() != VMA_NULL)
17704 {
17705 allocator->GetRecorder()->RecordUnmapMemory(
17706 allocator->GetCurrentFrameIndex(),
17707 allocation);
17708 }
17709 #endif
17710
17711 allocator->Unmap(allocation);
17712 }
17713
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)17714 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
17715 {
17716 VMA_ASSERT(allocator && allocation);
17717
17718 VMA_DEBUG_LOG("vmaFlushAllocation");
17719
17720 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17721
17722 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
17723
17724 #if VMA_RECORDING_ENABLED
17725 if(allocator->GetRecorder() != VMA_NULL)
17726 {
17727 allocator->GetRecorder()->RecordFlushAllocation(
17728 allocator->GetCurrentFrameIndex(),
17729 allocation, offset, size);
17730 }
17731 #endif
17732
17733 return res;
17734 }
17735
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)17736 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
17737 {
17738 VMA_ASSERT(allocator && allocation);
17739
17740 VMA_DEBUG_LOG("vmaInvalidateAllocation");
17741
17742 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17743
17744 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
17745
17746 #if VMA_RECORDING_ENABLED
17747 if(allocator->GetRecorder() != VMA_NULL)
17748 {
17749 allocator->GetRecorder()->RecordInvalidateAllocation(
17750 allocator->GetCurrentFrameIndex(),
17751 allocation, offset, size);
17752 }
17753 #endif
17754
17755 return res;
17756 }
17757
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)17758 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
17759 VmaAllocator allocator,
17760 uint32_t allocationCount,
17761 const VmaAllocation* allocations,
17762 const VkDeviceSize* offsets,
17763 const VkDeviceSize* sizes)
17764 {
17765 VMA_ASSERT(allocator);
17766
17767 if(allocationCount == 0)
17768 {
17769 return VK_SUCCESS;
17770 }
17771
17772 VMA_ASSERT(allocations);
17773
17774 VMA_DEBUG_LOG("vmaFlushAllocations");
17775
17776 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17777
17778 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
17779
17780 #if VMA_RECORDING_ENABLED
17781 if(allocator->GetRecorder() != VMA_NULL)
17782 {
17783 //TODO
17784 }
17785 #endif
17786
17787 return res;
17788 }
17789
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)17790 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
17791 VmaAllocator allocator,
17792 uint32_t allocationCount,
17793 const VmaAllocation* allocations,
17794 const VkDeviceSize* offsets,
17795 const VkDeviceSize* sizes)
17796 {
17797 VMA_ASSERT(allocator);
17798
17799 if(allocationCount == 0)
17800 {
17801 return VK_SUCCESS;
17802 }
17803
17804 VMA_ASSERT(allocations);
17805
17806 VMA_DEBUG_LOG("vmaInvalidateAllocations");
17807
17808 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17809
17810 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
17811
17812 #if VMA_RECORDING_ENABLED
17813 if(allocator->GetRecorder() != VMA_NULL)
17814 {
17815 //TODO
17816 }
17817 #endif
17818
17819 return res;
17820 }
17821
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)17822 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
17823 {
17824 VMA_ASSERT(allocator);
17825
17826 VMA_DEBUG_LOG("vmaCheckCorruption");
17827
17828 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17829
17830 return allocator->CheckCorruption(memoryTypeBits);
17831 }
17832
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)17833 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
17834 VmaAllocator allocator,
17835 const VmaAllocation* pAllocations,
17836 size_t allocationCount,
17837 VkBool32* pAllocationsChanged,
17838 const VmaDefragmentationInfo *pDefragmentationInfo,
17839 VmaDefragmentationStats* pDefragmentationStats)
17840 {
17841 // Deprecated interface, reimplemented using new one.
17842
17843 VmaDefragmentationInfo2 info2 = {};
17844 info2.allocationCount = (uint32_t)allocationCount;
17845 info2.pAllocations = pAllocations;
17846 info2.pAllocationsChanged = pAllocationsChanged;
17847 if(pDefragmentationInfo != VMA_NULL)
17848 {
17849 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
17850 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
17851 }
17852 else
17853 {
17854 info2.maxCpuAllocationsToMove = UINT32_MAX;
17855 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
17856 }
17857 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
17858
17859 VmaDefragmentationContext ctx;
17860 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
17861 if(res == VK_NOT_READY)
17862 {
17863 res = vmaDefragmentationEnd( allocator, ctx);
17864 }
17865 return res;
17866 }
17867
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)17868 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
17869 VmaAllocator allocator,
17870 const VmaDefragmentationInfo2* pInfo,
17871 VmaDefragmentationStats* pStats,
17872 VmaDefragmentationContext *pContext)
17873 {
17874 VMA_ASSERT(allocator && pInfo && pContext);
17875
17876 // Degenerate case: Nothing to defragment.
17877 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
17878 {
17879 return VK_SUCCESS;
17880 }
17881
17882 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
17883 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
17884 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
17885 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
17886
17887 VMA_DEBUG_LOG("vmaDefragmentationBegin");
17888
17889 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17890
17891 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
17892
17893 #if VMA_RECORDING_ENABLED
17894 if(allocator->GetRecorder() != VMA_NULL)
17895 {
17896 allocator->GetRecorder()->RecordDefragmentationBegin(
17897 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
17898 }
17899 #endif
17900
17901 return res;
17902 }
17903
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)17904 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
17905 VmaAllocator allocator,
17906 VmaDefragmentationContext context)
17907 {
17908 VMA_ASSERT(allocator);
17909
17910 VMA_DEBUG_LOG("vmaDefragmentationEnd");
17911
17912 if(context != VK_NULL_HANDLE)
17913 {
17914 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17915
17916 #if VMA_RECORDING_ENABLED
17917 if(allocator->GetRecorder() != VMA_NULL)
17918 {
17919 allocator->GetRecorder()->RecordDefragmentationEnd(
17920 allocator->GetCurrentFrameIndex(), context);
17921 }
17922 #endif
17923
17924 return allocator->DefragmentationEnd(context);
17925 }
17926 else
17927 {
17928 return VK_SUCCESS;
17929 }
17930 }
17931
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)17932 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
17933 VmaAllocator allocator,
17934 VmaDefragmentationContext context,
17935 VmaDefragmentationPassInfo* pInfo
17936 )
17937 {
17938 VMA_ASSERT(allocator);
17939 VMA_ASSERT(pInfo);
17940
17941 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
17942
17943 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17944
17945 if(context == VK_NULL_HANDLE)
17946 {
17947 pInfo->moveCount = 0;
17948 return VK_SUCCESS;
17949 }
17950
17951 return allocator->DefragmentationPassBegin(pInfo, context);
17952 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)17953 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
17954 VmaAllocator allocator,
17955 VmaDefragmentationContext context)
17956 {
17957 VMA_ASSERT(allocator);
17958
17959 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
17960 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17961
17962 if(context == VK_NULL_HANDLE)
17963 return VK_SUCCESS;
17964
17965 return allocator->DefragmentationPassEnd(context);
17966 }
17967
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)17968 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
17969 VmaAllocator allocator,
17970 VmaAllocation allocation,
17971 VkBuffer buffer)
17972 {
17973 VMA_ASSERT(allocator && allocation && buffer);
17974
17975 VMA_DEBUG_LOG("vmaBindBufferMemory");
17976
17977 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17978
17979 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
17980 }
17981
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)17982 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
17983 VmaAllocator allocator,
17984 VmaAllocation allocation,
17985 VkDeviceSize allocationLocalOffset,
17986 VkBuffer buffer,
17987 const void* pNext)
17988 {
17989 VMA_ASSERT(allocator && allocation && buffer);
17990
17991 VMA_DEBUG_LOG("vmaBindBufferMemory2");
17992
17993 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17994
17995 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
17996 }
17997
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)17998 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
17999 VmaAllocator allocator,
18000 VmaAllocation allocation,
18001 VkImage image)
18002 {
18003 VMA_ASSERT(allocator && allocation && image);
18004
18005 VMA_DEBUG_LOG("vmaBindImageMemory");
18006
18007 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18008
18009 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18010 }
18011
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)18012 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18013 VmaAllocator allocator,
18014 VmaAllocation allocation,
18015 VkDeviceSize allocationLocalOffset,
18016 VkImage image,
18017 const void* pNext)
18018 {
18019 VMA_ASSERT(allocator && allocation && image);
18020
18021 VMA_DEBUG_LOG("vmaBindImageMemory2");
18022
18023 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18024
18025 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18026 }
18027
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18028 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18029 VmaAllocator allocator,
18030 const VkBufferCreateInfo* pBufferCreateInfo,
18031 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18032 VkBuffer* pBuffer,
18033 VmaAllocation* pAllocation,
18034 VmaAllocationInfo* pAllocationInfo)
18035 {
18036 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18037
18038 if(pBufferCreateInfo->size == 0)
18039 {
18040 return VK_ERROR_INITIALIZATION_FAILED;
18041 }
18042 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18043 !allocator->m_UseKhrBufferDeviceAddress)
18044 {
18045 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18046 return VK_ERROR_INITIALIZATION_FAILED;
18047 }
18048
18049 VMA_DEBUG_LOG("vmaCreateBuffer");
18050
18051 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18052
18053 *pBuffer = VK_NULL_HANDLE;
18054 *pAllocation = VK_NULL_HANDLE;
18055
18056 // 1. Create VkBuffer.
18057 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18058 allocator->m_hDevice,
18059 pBufferCreateInfo,
18060 allocator->GetAllocationCallbacks(),
18061 pBuffer);
18062 if(res >= 0)
18063 {
18064 // 2. vkGetBufferMemoryRequirements.
18065 VkMemoryRequirements vkMemReq = {};
18066 bool requiresDedicatedAllocation = false;
18067 bool prefersDedicatedAllocation = false;
18068 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18069 requiresDedicatedAllocation, prefersDedicatedAllocation);
18070
18071 // 3. Allocate memory using allocator.
18072 res = allocator->AllocateMemory(
18073 vkMemReq,
18074 requiresDedicatedAllocation,
18075 prefersDedicatedAllocation,
18076 *pBuffer, // dedicatedBuffer
18077 pBufferCreateInfo->usage, // dedicatedBufferUsage
18078 VK_NULL_HANDLE, // dedicatedImage
18079 *pAllocationCreateInfo,
18080 VMA_SUBALLOCATION_TYPE_BUFFER,
18081 1, // allocationCount
18082 pAllocation);
18083
18084 #if VMA_RECORDING_ENABLED
18085 if(allocator->GetRecorder() != VMA_NULL)
18086 {
18087 allocator->GetRecorder()->RecordCreateBuffer(
18088 allocator->GetCurrentFrameIndex(),
18089 *pBufferCreateInfo,
18090 *pAllocationCreateInfo,
18091 *pAllocation);
18092 }
18093 #endif
18094
18095 if(res >= 0)
18096 {
18097 // 3. Bind buffer with memory.
18098 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18099 {
18100 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18101 }
18102 if(res >= 0)
18103 {
18104 // All steps succeeded.
18105 #if VMA_STATS_STRING_ENABLED
18106 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18107 #endif
18108 if(pAllocationInfo != VMA_NULL)
18109 {
18110 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18111 }
18112
18113 return VK_SUCCESS;
18114 }
18115 allocator->FreeMemory(
18116 1, // allocationCount
18117 pAllocation);
18118 *pAllocation = VK_NULL_HANDLE;
18119 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18120 *pBuffer = VK_NULL_HANDLE;
18121 return res;
18122 }
18123 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18124 *pBuffer = VK_NULL_HANDLE;
18125 return res;
18126 }
18127 return res;
18128 }
18129
vmaCreateBufferWithAlignment(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkDeviceSize minAlignment,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18130 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
18131 VmaAllocator allocator,
18132 const VkBufferCreateInfo* pBufferCreateInfo,
18133 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18134 VkDeviceSize minAlignment,
18135 VkBuffer* pBuffer,
18136 VmaAllocation* pAllocation,
18137 VmaAllocationInfo* pAllocationInfo)
18138 {
18139 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
18140
18141 if(pBufferCreateInfo->size == 0)
18142 {
18143 return VK_ERROR_INITIALIZATION_FAILED;
18144 }
18145 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18146 !allocator->m_UseKhrBufferDeviceAddress)
18147 {
18148 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18149 return VK_ERROR_INITIALIZATION_FAILED;
18150 }
18151
18152 VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
18153
18154 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18155
18156 *pBuffer = VK_NULL_HANDLE;
18157 *pAllocation = VK_NULL_HANDLE;
18158
18159 // 1. Create VkBuffer.
18160 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18161 allocator->m_hDevice,
18162 pBufferCreateInfo,
18163 allocator->GetAllocationCallbacks(),
18164 pBuffer);
18165 if(res >= 0)
18166 {
18167 // 2. vkGetBufferMemoryRequirements.
18168 VkMemoryRequirements vkMemReq = {};
18169 bool requiresDedicatedAllocation = false;
18170 bool prefersDedicatedAllocation = false;
18171 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18172 requiresDedicatedAllocation, prefersDedicatedAllocation);
18173
18174 // 2a. Include minAlignment
18175 vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
18176
18177 // 3. Allocate memory using allocator.
18178 res = allocator->AllocateMemory(
18179 vkMemReq,
18180 requiresDedicatedAllocation,
18181 prefersDedicatedAllocation,
18182 *pBuffer, // dedicatedBuffer
18183 pBufferCreateInfo->usage, // dedicatedBufferUsage
18184 VK_NULL_HANDLE, // dedicatedImage
18185 *pAllocationCreateInfo,
18186 VMA_SUBALLOCATION_TYPE_BUFFER,
18187 1, // allocationCount
18188 pAllocation);
18189
18190 #if VMA_RECORDING_ENABLED
18191 if(allocator->GetRecorder() != VMA_NULL)
18192 {
18193 VMA_ASSERT(0 && "Not implemented.");
18194 }
18195 #endif
18196
18197 if(res >= 0)
18198 {
18199 // 3. Bind buffer with memory.
18200 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18201 {
18202 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18203 }
18204 if(res >= 0)
18205 {
18206 // All steps succeeded.
18207 #if VMA_STATS_STRING_ENABLED
18208 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18209 #endif
18210 if(pAllocationInfo != VMA_NULL)
18211 {
18212 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18213 }
18214
18215 return VK_SUCCESS;
18216 }
18217 allocator->FreeMemory(
18218 1, // allocationCount
18219 pAllocation);
18220 *pAllocation = VK_NULL_HANDLE;
18221 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18222 *pBuffer = VK_NULL_HANDLE;
18223 return res;
18224 }
18225 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18226 *pBuffer = VK_NULL_HANDLE;
18227 return res;
18228 }
18229 return res;
18230 }
18231
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)18232 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18233 VmaAllocator allocator,
18234 VkBuffer buffer,
18235 VmaAllocation allocation)
18236 {
18237 VMA_ASSERT(allocator);
18238
18239 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18240 {
18241 return;
18242 }
18243
18244 VMA_DEBUG_LOG("vmaDestroyBuffer");
18245
18246 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18247
18248 #if VMA_RECORDING_ENABLED
18249 if(allocator->GetRecorder() != VMA_NULL)
18250 {
18251 allocator->GetRecorder()->RecordDestroyBuffer(
18252 allocator->GetCurrentFrameIndex(),
18253 allocation);
18254 }
18255 #endif
18256
18257 if(buffer != VK_NULL_HANDLE)
18258 {
18259 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18260 }
18261
18262 if(allocation != VK_NULL_HANDLE)
18263 {
18264 allocator->FreeMemory(
18265 1, // allocationCount
18266 &allocation);
18267 }
18268 }
18269
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18270 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18271 VmaAllocator allocator,
18272 const VkImageCreateInfo* pImageCreateInfo,
18273 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18274 VkImage* pImage,
18275 VmaAllocation* pAllocation,
18276 VmaAllocationInfo* pAllocationInfo)
18277 {
18278 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18279
18280 if(pImageCreateInfo->extent.width == 0 ||
18281 pImageCreateInfo->extent.height == 0 ||
18282 pImageCreateInfo->extent.depth == 0 ||
18283 pImageCreateInfo->mipLevels == 0 ||
18284 pImageCreateInfo->arrayLayers == 0)
18285 {
18286 return VK_ERROR_INITIALIZATION_FAILED;
18287 }
18288
18289 VMA_DEBUG_LOG("vmaCreateImage");
18290
18291 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18292
18293 *pImage = VK_NULL_HANDLE;
18294 *pAllocation = VK_NULL_HANDLE;
18295
18296 // 1. Create VkImage.
18297 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18298 allocator->m_hDevice,
18299 pImageCreateInfo,
18300 allocator->GetAllocationCallbacks(),
18301 pImage);
18302 if(res >= 0)
18303 {
18304 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18305 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18306 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18307
18308 // 2. Allocate memory using allocator.
18309 VkMemoryRequirements vkMemReq = {};
18310 bool requiresDedicatedAllocation = false;
18311 bool prefersDedicatedAllocation = false;
18312 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18313 requiresDedicatedAllocation, prefersDedicatedAllocation);
18314
18315 res = allocator->AllocateMemory(
18316 vkMemReq,
18317 requiresDedicatedAllocation,
18318 prefersDedicatedAllocation,
18319 VK_NULL_HANDLE, // dedicatedBuffer
18320 UINT32_MAX, // dedicatedBufferUsage
18321 *pImage, // dedicatedImage
18322 *pAllocationCreateInfo,
18323 suballocType,
18324 1, // allocationCount
18325 pAllocation);
18326
18327 #if VMA_RECORDING_ENABLED
18328 if(allocator->GetRecorder() != VMA_NULL)
18329 {
18330 allocator->GetRecorder()->RecordCreateImage(
18331 allocator->GetCurrentFrameIndex(),
18332 *pImageCreateInfo,
18333 *pAllocationCreateInfo,
18334 *pAllocation);
18335 }
18336 #endif
18337
18338 if(res >= 0)
18339 {
18340 // 3. Bind image with memory.
18341 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18342 {
18343 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18344 }
18345 if(res >= 0)
18346 {
18347 // All steps succeeded.
18348 #if VMA_STATS_STRING_ENABLED
18349 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18350 #endif
18351 if(pAllocationInfo != VMA_NULL)
18352 {
18353 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18354 }
18355
18356 return VK_SUCCESS;
18357 }
18358 allocator->FreeMemory(
18359 1, // allocationCount
18360 pAllocation);
18361 *pAllocation = VK_NULL_HANDLE;
18362 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18363 *pImage = VK_NULL_HANDLE;
18364 return res;
18365 }
18366 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18367 *pImage = VK_NULL_HANDLE;
18368 return res;
18369 }
18370 return res;
18371 }
18372
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)18373 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
18374 VmaAllocator allocator,
18375 VkImage image,
18376 VmaAllocation allocation)
18377 {
18378 VMA_ASSERT(allocator);
18379
18380 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18381 {
18382 return;
18383 }
18384
18385 VMA_DEBUG_LOG("vmaDestroyImage");
18386
18387 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18388
18389 #if VMA_RECORDING_ENABLED
18390 if(allocator->GetRecorder() != VMA_NULL)
18391 {
18392 allocator->GetRecorder()->RecordDestroyImage(
18393 allocator->GetCurrentFrameIndex(),
18394 allocation);
18395 }
18396 #endif
18397
18398 if(image != VK_NULL_HANDLE)
18399 {
18400 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
18401 }
18402 if(allocation != VK_NULL_HANDLE)
18403 {
18404 allocator->FreeMemory(
18405 1, // allocationCount
18406 &allocation);
18407 }
18408 }
18409
vmaCreateVirtualBlock(const VmaVirtualBlockCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)18410 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
18411 const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
18412 VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
18413 {
18414 VMA_ASSERT(pCreateInfo && pVirtualBlock);
18415 VMA_ASSERT(pCreateInfo->size > 0);
18416 VMA_DEBUG_LOG("vmaCreateVirtualBlock");
18417 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18418 *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
18419 VkResult res = (*pVirtualBlock)->Init();
18420 if(res < 0)
18421 {
18422 vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
18423 *pVirtualBlock = VK_NULL_HANDLE;
18424 }
18425 return res;
18426 }
18427
vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)18428 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
18429 {
18430 if(virtualBlock != VK_NULL_HANDLE)
18431 {
18432 VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
18433 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18434 VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
18435 vma_delete(&allocationCallbacks, virtualBlock);
18436 }
18437 }
18438
vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)18439 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
18440 {
18441 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18442 VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
18443 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18444 return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
18445 }
18446
vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset,VmaVirtualAllocationInfo * VMA_NOT_NULL pVirtualAllocInfo)18447 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18448 VkDeviceSize offset, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
18449 {
18450 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
18451 VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
18452 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18453 virtualBlock->GetAllocationInfo(offset, *pVirtualAllocInfo);
18454 }
18455
vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,const VmaVirtualAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VkDeviceSize * VMA_NOT_NULL pOffset)18456 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18457 const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VkDeviceSize* VMA_NOT_NULL pOffset)
18458 {
18459 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pOffset != VMA_NULL);
18460 VMA_DEBUG_LOG("vmaVirtualAllocate");
18461 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18462 return virtualBlock->Allocate(*pCreateInfo, *pOffset);
18463 }
18464
vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset)18465 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VkDeviceSize offset)
18466 {
18467 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18468 VMA_DEBUG_LOG("vmaVirtualFree");
18469 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18470 virtualBlock->Free(offset);
18471 }
18472
vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)18473 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
18474 {
18475 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18476 VMA_DEBUG_LOG("vmaClearVirtualBlock");
18477 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18478 virtualBlock->Clear();
18479 }
18480
vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset,void * VMA_NULLABLE pUserData)18481 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18482 VkDeviceSize offset, void* VMA_NULLABLE pUserData)
18483 {
18484 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18485 VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
18486 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18487 virtualBlock->SetAllocationUserData(offset, pUserData);
18488 }
18489
vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaStatInfo * VMA_NOT_NULL pStatInfo)18490 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18491 VmaStatInfo* VMA_NOT_NULL pStatInfo)
18492 {
18493 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStatInfo != VMA_NULL);
18494 VMA_DEBUG_LOG("vmaCalculateVirtualBlockStats");
18495 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18496 virtualBlock->CalculateStats(*pStatInfo);
18497 }
18498
18499 #if VMA_STATS_STRING_ENABLED
18500
vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE * VMA_NOT_NULL ppStatsString,VkBool32 detailedMap)18501 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18502 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
18503 {
18504 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
18505 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18506 const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
18507 VmaStringBuilder sb(allocationCallbacks);
18508 virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
18509 *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
18510 }
18511
18512 #endif // #if VMA_STATS_STRING_ENABLED
18513
vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE pStatsString)18514 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18515 char* VMA_NULLABLE pStatsString)
18516 {
18517 if(pStatsString != VMA_NULL)
18518 {
18519 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18520 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18521 VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
18522 }
18523 }
18524
18525 #endif // #ifdef VMA_IMPLEMENTATION
18526
18527 /**
18528 \page quick_start Quick start
18529
18530 \section quick_start_project_setup Project setup
18531
18532 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
18533 You don't need to build it as a separate library project.
18534 You can add this file directly to your project and submit it to code repository next to your other source files.
18535
18536 "Single header" doesn't mean that everything is contained in C/C++ declarations,
18537 like it tends to be in case of inline functions or C++ templates.
18538 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
18539 If you don't do it properly, you will get linker errors.
18540
18541 To do it properly:
18542
18543 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
18544 This includes declarations of all members of the library.
18545 -# In exactly one CPP file define following macro before this include.
18546 It enables also internal definitions.
18547
18548 \code
18549 #define VMA_IMPLEMENTATION
18550 #include "vk_mem_alloc.h"
18551 \endcode
18552
18553 It may be a good idea to create dedicated CPP file just for this purpose.
18554
18555 Note on language: This library is written in C++, but has C-compatible interface.
18556 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
18557 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
18558
18559 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
18560 includes `<windows.h>` on Windows. If you need some specific macros defined
18561 before including these headers (like `WIN32_LEAN_AND_MEAN` or
18562 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
18563 them before every `#include` of this library.
18564
18565 You may need to configure the way you import Vulkan functions.
18566
18567 - By default, VMA assumes you you link statically with Vulkan API. If this is not the case,
18568 `#define VMA_STATIC_VULKAN_FUNCTIONS 0` before `#include` of the VMA implementation and use another way.
18569 - You can `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1` and make sure `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` globals are defined.
18570 All the remaining Vulkan functions will be fetched automatically.
18571 - Finally, you can provide your own pointers to all Vulkan functions needed by VMA using structure member
18572 VmaAllocatorCreateInfo::pVulkanFunctions, if you fetched them in some custom way e.g. using some loader like [Volk](https://github.com/zeux/volk).
18573
18574
18575 \section quick_start_initialization Initialization
18576
18577 At program startup:
18578
18579 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
18580 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
18581 calling vmaCreateAllocator().
18582
18583 \code
18584 VmaAllocatorCreateInfo allocatorInfo = {};
18585 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2;
18586 allocatorInfo.physicalDevice = physicalDevice;
18587 allocatorInfo.device = device;
18588 allocatorInfo.instance = instance;
18589
18590 VmaAllocator allocator;
18591 vmaCreateAllocator(&allocatorInfo, &allocator);
18592 \endcode
18593
18594 Only members `physicalDevice`, `device`, `instance` are required.
18595 However, you should inform the library which Vulkan version do you use by setting
18596 VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable
18597 by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address).
18598 Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.
18599
18600
18601 \section quick_start_resource_allocation Resource allocation
18602
18603 When you want to create a buffer or image:
18604
18605 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
18606 -# Fill VmaAllocationCreateInfo structure.
18607 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
18608 already allocated and bound to it.
18609
18610 \code
18611 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18612 bufferInfo.size = 65536;
18613 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18614
18615 VmaAllocationCreateInfo allocInfo = {};
18616 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18617
18618 VkBuffer buffer;
18619 VmaAllocation allocation;
18620 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18621 \endcode
18622
18623 Don't forget to destroy your objects when no longer needed:
18624
18625 \code
18626 vmaDestroyBuffer(allocator, buffer, allocation);
18627 vmaDestroyAllocator(allocator);
18628 \endcode
18629
18630
18631 \page choosing_memory_type Choosing memory type
18632
18633 Physical devices in Vulkan support various combinations of memory heaps and
18634 types. Help with choosing correct and optimal memory type for your specific
18635 resource is one of the key features of this library. You can use it by filling
18636 appropriate members of VmaAllocationCreateInfo structure, as described below.
18637 You can also combine multiple methods.
18638
18639 -# If you just want to find memory type index that meets your requirements, you
18640 can use function: vmaFindMemoryTypeIndex(), vmaFindMemoryTypeIndexForBufferInfo(),
18641 vmaFindMemoryTypeIndexForImageInfo().
18642 -# If you want to allocate a region of device memory without association with any
18643 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
18644 this function is not recommended and usually not needed.
18645 vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
18646 which may be useful for sparse binding.
18647 -# If you already have a buffer or an image created, you want to allocate memory
18648 for it and then you will bind it yourself, you can use function
18649 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
18650 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
18651 or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
18652 -# If you want to create a buffer or an image, allocate memory for it and bind
18653 them together, all in one call, you can use function vmaCreateBuffer(),
18654 vmaCreateImage(). This is the easiest and recommended way to use this library.
18655
18656 When using 3. or 4., the library internally queries Vulkan for memory types
18657 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
18658 and uses only one of these types.
18659
18660 If no memory type can be found that meets all the requirements, these functions
18661 return `VK_ERROR_FEATURE_NOT_PRESENT`.
18662
18663 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
18664 It means no requirements are specified for memory type.
18665 It is valid, although not very useful.
18666
18667 \section choosing_memory_type_usage Usage
18668
18669 The easiest way to specify memory requirements is to fill member
18670 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
18671 It defines high level, common usage types.
18672 For more details, see description of this enum.
18673
18674 For example, if you want to create a uniform buffer that will be filled using
18675 transfer only once or infrequently and used for rendering every frame, you can
18676 do it using following code:
18677
18678 \code
18679 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18680 bufferInfo.size = 65536;
18681 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18682
18683 VmaAllocationCreateInfo allocInfo = {};
18684 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18685
18686 VkBuffer buffer;
18687 VmaAllocation allocation;
18688 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18689 \endcode
18690
18691 \section choosing_memory_type_required_preferred_flags Required and preferred flags
18692
18693 You can specify more detailed requirements by filling members
18694 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
18695 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
18696 if you want to create a buffer that will be persistently mapped on host (so it
18697 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
18698 use following code:
18699
18700 \code
18701 VmaAllocationCreateInfo allocInfo = {};
18702 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18703 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18704 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18705
18706 VkBuffer buffer;
18707 VmaAllocation allocation;
18708 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18709 \endcode
18710
18711 A memory type is chosen that has all the required flags and as many preferred
18712 flags set as possible.
18713
18714 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
18715 a set of required and preferred flags.
18716
18717 \section choosing_memory_type_explicit_memory_types Explicit memory types
18718
18719 If you inspected memory types available on the physical device and you have
18720 a preference for memory types that you want to use, you can fill member
18721 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
18722 means that a memory type with that index is allowed to be used for the
18723 allocation. Special value 0, just like `UINT32_MAX`, means there are no
18724 restrictions to memory type index.
18725
18726 Please note that this member is NOT just a memory type index.
18727 Still you can use it to choose just one, specific memory type.
18728 For example, if you already determined that your buffer should be created in
18729 memory type 2, use following code:
18730
18731 \code
18732 uint32_t memoryTypeIndex = 2;
18733
18734 VmaAllocationCreateInfo allocInfo = {};
18735 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
18736
18737 VkBuffer buffer;
18738 VmaAllocation allocation;
18739 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18740 \endcode
18741
18742
18743 \section choosing_memory_type_custom_memory_pools Custom memory pools
18744
18745 If you allocate from custom memory pool, all the ways of specifying memory
18746 requirements described above are not applicable and the aforementioned members
18747 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
18748 explicitly when creating the pool and then used to make all the allocations from
18749 that pool. For further details, see \ref custom_memory_pools.
18750
18751 \section choosing_memory_type_dedicated_allocations Dedicated allocations
18752
18753 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
18754 allocated from Vulkan internally. That is the main feature of this whole library.
18755 You can still request a separate memory block to be created for an allocation,
18756 just like you would do in a trivial solution without using any allocator.
18757 In that case, a buffer or image is always bound to that memory at offset 0.
18758 This is called a "dedicated allocation".
18759 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18760 The library can also internally decide to use dedicated allocation in some cases, e.g.:
18761
18762 - When the size of the allocation is large.
18763 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
18764 and it reports that dedicated allocation is required or recommended for the resource.
18765 - When allocation of next big memory block fails due to not enough device memory,
18766 but allocation with the exact requested size succeeds.
18767
18768
18769 \page memory_mapping Memory mapping
18770
18771 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
18772 to be able to read from it or write to it in CPU code.
18773 Mapping is possible only of memory allocated from a memory type that has
18774 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
18775 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
18776 You can use them directly with memory allocated by this library,
18777 but it is not recommended because of following issue:
18778 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
18779 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
18780 Because of this, Vulkan Memory Allocator provides following facilities:
18781
18782 \section memory_mapping_mapping_functions Mapping functions
18783
18784 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
18785 They are safer and more convenient to use than standard Vulkan functions.
18786 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
18787 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
18788 The way it is implemented is that the library always maps entire memory block, not just region of the allocation.
18789 For further details, see description of vmaMapMemory() function.
18790 Example:
18791
18792 \code
18793 // Having these objects initialized:
18794
18795 struct ConstantBuffer
18796 {
18797 ...
18798 };
18799 ConstantBuffer constantBufferData;
18800
18801 VmaAllocator allocator;
18802 VkBuffer constantBuffer;
18803 VmaAllocation constantBufferAllocation;
18804
18805 // You can map and fill your buffer using following code:
18806
18807 void* mappedData;
18808 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
18809 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
18810 vmaUnmapMemory(allocator, constantBufferAllocation);
18811 \endcode
18812
18813 When mapping, you may see a warning from Vulkan validation layer similar to this one:
18814
18815 <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
18816
18817 It happens because the library maps entire `VkDeviceMemory` block, where different
18818 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
18819 You can safely ignore it if you are sure you access only memory of the intended
18820 object that you wanted to map.
18821
18822
18823 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
18824
18825 Kepping your memory persistently mapped is generally OK in Vulkan.
18826 You don't need to unmap it before using its data on the GPU.
18827 The library provides a special feature designed for that:
18828 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
18829 VmaAllocationCreateInfo::flags stay mapped all the time,
18830 so you can just access CPU pointer to it any time
18831 without a need to call any "map" or "unmap" function.
18832 Example:
18833
18834 \code
18835 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18836 bufCreateInfo.size = sizeof(ConstantBuffer);
18837 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
18838
18839 VmaAllocationCreateInfo allocCreateInfo = {};
18840 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
18841 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18842
18843 VkBuffer buf;
18844 VmaAllocation alloc;
18845 VmaAllocationInfo allocInfo;
18846 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18847
18848 // Buffer is already mapped. You can access its memory.
18849 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
18850 \endcode
18851
18852 There are some exceptions though, when you should consider mapping memory only for a short period of time:
18853
18854 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
18855 device is discrete AMD GPU,
18856 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
18857 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
18858 then whenever a memory block allocated from this memory type stays mapped
18859 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
18860 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
18861 matter if that particular memory block is actually used by the command buffer
18862 being submitted.
18863 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
18864
18865 \section memory_mapping_cache_control Cache flush and invalidate
18866
18867 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
18868 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
18869 you need to manually **invalidate** cache before reading of mapped pointer
18870 and **flush** cache after writing to mapped pointer.
18871 Map/unmap operations don't do that automatically.
18872 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
18873 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
18874 functions that refer to given allocation object: vmaFlushAllocation(),
18875 vmaInvalidateAllocation(),
18876 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
18877
18878 Regions of memory specified for flush/invalidate must be aligned to
18879 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
18880 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
18881 within blocks are aligned to this value, so their offsets are always multiply of
18882 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
18883
18884 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
18885
18886 Also, Windows drivers from all 3 **PC** GPU vendors (AMD, Intel, NVIDIA)
18887 currently provide `HOST_COHERENT` flag on all memory types that are
18888 `HOST_VISIBLE`, so on this platform you may not need to bother.
18889
18890 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
18891
18892 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
18893 despite it wasn't explicitly requested.
18894 For example, application may work on integrated graphics with unified memory (like Intel) or
18895 allocation from video memory might have failed, so the library chose system memory as fallback.
18896
18897 You can detect this case and map such allocation to access its memory on CPU directly,
18898 instead of launching a transfer operation.
18899 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
18900 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
18901
18902 \code
18903 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18904 bufCreateInfo.size = sizeof(ConstantBuffer);
18905 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18906
18907 VmaAllocationCreateInfo allocCreateInfo = {};
18908 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18909 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18910
18911 VkBuffer buf;
18912 VmaAllocation alloc;
18913 VmaAllocationInfo allocInfo;
18914 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18915
18916 VkMemoryPropertyFlags memFlags;
18917 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
18918 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18919 {
18920 // Allocation ended up in mappable memory. You can map it and access it directly.
18921 void* mappedData;
18922 vmaMapMemory(allocator, alloc, &mappedData);
18923 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
18924 vmaUnmapMemory(allocator, alloc);
18925 }
18926 else
18927 {
18928 // Allocation ended up in non-mappable memory.
18929 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
18930 }
18931 \endcode
18932
18933 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
18934 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
18935 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
18936 If not, the flag is just ignored.
18937 Example:
18938
18939 \code
18940 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18941 bufCreateInfo.size = sizeof(ConstantBuffer);
18942 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18943
18944 VmaAllocationCreateInfo allocCreateInfo = {};
18945 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18946 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18947
18948 VkBuffer buf;
18949 VmaAllocation alloc;
18950 VmaAllocationInfo allocInfo;
18951 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18952
18953 if(allocInfo.pMappedData != nullptr)
18954 {
18955 // Allocation ended up in mappable memory.
18956 // It is persistently mapped. You can access it directly.
18957 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
18958 }
18959 else
18960 {
18961 // Allocation ended up in non-mappable memory.
18962 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
18963 }
18964 \endcode
18965
18966
18967 \page staying_within_budget Staying within budget
18968
18969 When developing a graphics-intensive game or program, it is important to avoid allocating
18970 more GPU memory than it is physically available. When the memory is over-committed,
18971 various bad things can happen, depending on the specific GPU, graphics driver, and
18972 operating system:
18973
18974 - It may just work without any problems.
18975 - The application may slow down because some memory blocks are moved to system RAM
18976 and the GPU has to access them through PCI Express bus.
18977 - A new allocation may take very long time to complete, even few seconds, and possibly
18978 freeze entire system.
18979 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18980 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
18981 returned somewhere later.
18982
18983 \section staying_within_budget_querying_for_budget Querying for budget
18984
18985 To query for current memory usage and available budget, use function vmaGetBudget().
18986 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
18987
18988 Please note that this function returns different information and works faster than
18989 vmaCalculateStats(). vmaGetBudget() can be called every frame or even before every
18990 allocation, while vmaCalculateStats() is intended to be used rarely,
18991 only to obtain statistical information, e.g. for debugging purposes.
18992
18993 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
18994 about the budget from Vulkan device. VMA is able to use this extension automatically.
18995 When not enabled, the allocator behaves same way, but then it estimates current usage
18996 and available budget based on its internal information and Vulkan memory heap sizes,
18997 which may be less precise. In order to use this extension:
18998
18999 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
19000 required by it are available and enable them. Please note that the first is a device
19001 extension and the second is instance extension!
19002 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
19003 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
19004 Vulkan inside of it to avoid overhead of querying it with every allocation.
19005
19006 \section staying_within_budget_controlling_memory_usage Controlling memory usage
19007
19008 There are many ways in which you can try to stay within the budget.
19009
19010 First, when making new allocation requires allocating a new memory block, the library
19011 tries not to exceed the budget automatically. If a block with default recommended size
19012 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
19013 dedicated memory for just this resource.
19014
19015 If the size of the requested resource plus current memory usage is more than the
19016 budget, by default the library still tries to create it, leaving it to the Vulkan
19017 implementation whether the allocation succeeds or fails. You can change this behavior
19018 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
19019 not made if it would exceed the budget or if the budget is already exceeded.
19020 Some other allocations become lost instead to make room for it, if the mechanism of
19021 [lost allocations](@ref lost_allocations) is used.
19022 If that is not possible, the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19023 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
19024 when creating resources that are not essential for the application (e.g. the texture
19025 of a specific object) and not to pass it when creating critically important resources
19026 (e.g. render targets).
19027
19028 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
19029 a new allocation is created only when it fits inside one of the existing memory blocks.
19030 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19031 This also ensures that the function call is very fast because it never goes to Vulkan
19032 to obtain a new block.
19033
19034 Please note that creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
19035 set to more than 0 will try to allocate memory blocks without checking whether they
19036 fit within budget.
19037
19038
19039 \page resource_aliasing Resource aliasing (overlap)
19040
19041 New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
19042 management, give an opportunity to alias (overlap) multiple resources in the
19043 same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
19044 It can be useful to save video memory, but it must be used with caution.
19045
19046 For example, if you know the flow of your whole render frame in advance, you
19047 are going to use some intermediate textures or buffers only during a small range of render passes,
19048 and you know these ranges don't overlap in time, you can bind these resources to
19049 the same place in memory, even if they have completely different parameters (width, height, format etc.).
19050
19051 
19052
19053 Such scenario is possible using VMA, but you need to create your images manually.
19054 Then you need to calculate parameters of an allocation to be made using formula:
19055
19056 - allocation size = max(size of each image)
19057 - allocation alignment = max(alignment of each image)
19058 - allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)
19059
19060 Following example shows two different images bound to the same place in memory,
19061 allocated to fit largest of them.
19062
19063 \code
19064 // A 512x512 texture to be sampled.
19065 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19066 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
19067 img1CreateInfo.extent.width = 512;
19068 img1CreateInfo.extent.height = 512;
19069 img1CreateInfo.extent.depth = 1;
19070 img1CreateInfo.mipLevels = 10;
19071 img1CreateInfo.arrayLayers = 1;
19072 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
19073 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19074 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19075 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
19076 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19077
19078 // A full screen texture to be used as color attachment.
19079 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19080 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
19081 img2CreateInfo.extent.width = 1920;
19082 img2CreateInfo.extent.height = 1080;
19083 img2CreateInfo.extent.depth = 1;
19084 img2CreateInfo.mipLevels = 1;
19085 img2CreateInfo.arrayLayers = 1;
19086 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
19087 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19088 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19089 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
19090 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19091
19092 VkImage img1;
19093 res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);
19094 VkImage img2;
19095 res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);
19096
19097 VkMemoryRequirements img1MemReq;
19098 vkGetImageMemoryRequirements(device, img1, &img1MemReq);
19099 VkMemoryRequirements img2MemReq;
19100 vkGetImageMemoryRequirements(device, img2, &img2MemReq);
19101
19102 VkMemoryRequirements finalMemReq = {};
19103 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
19104 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
19105 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
19106 // Validate if(finalMemReq.memoryTypeBits != 0)
19107
19108 VmaAllocationCreateInfo allocCreateInfo = {};
19109 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19110
19111 VmaAllocation alloc;
19112 res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);
19113
19114 res = vmaBindImageMemory(allocator, alloc, img1);
19115 res = vmaBindImageMemory(allocator, alloc, img2);
19116
19117 // You can use img1, img2 here, but not at the same time!
19118
19119 vmaFreeMemory(allocator, alloc);
19120 vkDestroyImage(allocator, img2, nullptr);
19121 vkDestroyImage(allocator, img1, nullptr);
19122 \endcode
19123
19124 Remember that using resources that alias in memory requires proper synchronization.
19125 You need to issue a memory barrier to make sure commands that use `img1` and `img2`
19126 don't overlap on GPU timeline.
19127 You also need to treat a resource after aliasing as uninitialized - containing garbage data.
19128 For example, if you use `img1` and then want to use `img2`, you need to issue
19129 an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.
19130
19131 Additional considerations:
19132
19133 - Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.
19134 See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.
19135 - You can create more complex layout where different images and buffers are bound
19136 at different offsets inside one large allocation. For example, one can imagine
19137 a big texture used in some render passes, aliasing with a set of many small buffers
19138 used between in some further passes. To bind a resource at non-zero offset of an allocation,
19139 use vmaBindBufferMemory2() / vmaBindImageMemory2().
19140 - Before allocating memory for the resources you want to alias, check `memoryTypeBits`
19141 returned in memory requirements of each resource to make sure the bits overlap.
19142 Some GPUs may expose multiple memory types suitable e.g. only for buffers or
19143 images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your
19144 resources may be disjoint. Aliasing them is not possible in that case.
19145
19146
19147 \page custom_memory_pools Custom memory pools
19148
19149 A memory pool contains a number of `VkDeviceMemory` blocks.
19150 The library automatically creates and manages default pool for each memory type available on the device.
19151 Default memory pool automatically grows in size.
19152 Size of allocated blocks is also variable and managed automatically.
19153
19154 You can create custom pool and allocate memory out of it.
19155 It can be useful if you want to:
19156
19157 - Keep certain kind of allocations separate from others.
19158 - Enforce particular, fixed size of Vulkan memory blocks.
19159 - Limit maximum amount of Vulkan memory allocated for that pool.
19160 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
19161 - Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in
19162 #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.
19163
19164 To use custom memory pools:
19165
19166 -# Fill VmaPoolCreateInfo structure.
19167 -# Call vmaCreatePool() to obtain #VmaPool handle.
19168 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
19169 You don't need to specify any other parameters of this structure, like `usage`.
19170
19171 Example:
19172
19173 \code
19174 // Create a pool that can have at most 2 blocks, 128 MiB each.
19175 VmaPoolCreateInfo poolCreateInfo = {};
19176 poolCreateInfo.memoryTypeIndex = ...
19177 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
19178 poolCreateInfo.maxBlockCount = 2;
19179
19180 VmaPool pool;
19181 vmaCreatePool(allocator, &poolCreateInfo, &pool);
19182
19183 // Allocate a buffer out of it.
19184 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19185 bufCreateInfo.size = 1024;
19186 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19187
19188 VmaAllocationCreateInfo allocCreateInfo = {};
19189 allocCreateInfo.pool = pool;
19190
19191 VkBuffer buf;
19192 VmaAllocation alloc;
19193 VmaAllocationInfo allocInfo;
19194 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19195 \endcode
19196
19197 You have to free all allocations made from this pool before destroying it.
19198
19199 \code
19200 vmaDestroyBuffer(allocator, buf, alloc);
19201 vmaDestroyPool(allocator, pool);
19202 \endcode
19203
19204 \section custom_memory_pools_MemTypeIndex Choosing memory type index
19205
19206 When creating a pool, you must explicitly specify memory type index.
19207 To find the one suitable for your buffers or images, you can use helper functions
19208 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
19209 You need to provide structures with example parameters of buffers or images
19210 that you are going to create in that pool.
19211
19212 \code
19213 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19214 exampleBufCreateInfo.size = 1024; // Whatever.
19215 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
19216
19217 VmaAllocationCreateInfo allocCreateInfo = {};
19218 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
19219
19220 uint32_t memTypeIndex;
19221 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
19222
19223 VmaPoolCreateInfo poolCreateInfo = {};
19224 poolCreateInfo.memoryTypeIndex = memTypeIndex;
19225 // ...
19226 \endcode
19227
19228 When creating buffers/images allocated in that pool, provide following parameters:
19229
19230 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
19231 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
19232 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
19233 or the other way around.
19234 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
19235 Other members are ignored anyway.
19236
19237 \section linear_algorithm Linear allocation algorithm
19238
19239 Each Vulkan memory block managed by this library has accompanying metadata that
19240 keeps track of used and unused regions. By default, the metadata structure and
19241 algorithm tries to find best place for new allocations among free regions to
19242 optimize memory usage. This way you can allocate and free objects in any order.
19243
19244 
19245
19246 Sometimes there is a need to use simpler, linear allocation algorithm. You can
19247 create custom pool that uses such algorithm by adding flag
19248 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
19249 #VmaPool object. Then an alternative metadata management is used. It always
19250 creates new allocations after last one and doesn't reuse free regions after
19251 allocations freed in the middle. It results in better allocation performance and
19252 less memory consumed by metadata.
19253
19254 
19255
19256 With this one flag, you can create a custom pool that can be used in many ways:
19257 free-at-once, stack, double stack, and ring buffer. See below for details.
19258 You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
19259
19260 \subsection linear_algorithm_free_at_once Free-at-once
19261
19262 In a pool that uses linear algorithm, you still need to free all the allocations
19263 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
19264 them in any order. New allocations are always made after last one - free space
19265 in the middle is not reused. However, when you release all the allocation and
19266 the pool becomes empty, allocation starts from the beginning again. This way you
19267 can use linear algorithm to speed up creation of allocations that you are going
19268 to release all at once.
19269
19270 
19271
19272 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
19273 value that allows multiple memory blocks.
19274
19275 \subsection linear_algorithm_stack Stack
19276
19277 When you free an allocation that was created last, its space can be reused.
19278 Thanks to this, if you always release allocations in the order opposite to their
19279 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
19280
19281 
19282
19283 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
19284 value that allows multiple memory blocks.
19285
19286 \subsection linear_algorithm_double_stack Double stack
19287
19288 The space reserved by a custom pool with linear algorithm may be used by two
19289 stacks:
19290
19291 - First, default one, growing up from offset 0.
19292 - Second, "upper" one, growing down from the end towards lower offsets.
19293
19294 To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
19295 to VmaAllocationCreateInfo::flags.
19296
19297 
19298
19299 Double stack is available only in pools with one memory block -
19300 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
19301
19302 When the two stacks' ends meet so there is not enough space between them for a
19303 new allocation, such allocation fails with usual
19304 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
19305
19306 \subsection linear_algorithm_ring_buffer Ring buffer
19307
19308 When you free some allocations from the beginning and there is not enough free space
19309 for a new one at the end of a pool, allocator's "cursor" wraps around to the
19310 beginning and starts allocation there. Thanks to this, if you always release
19311 allocations in the same order as you created them (FIFO - First In First Out),
19312 you can achieve behavior of a ring buffer / queue.
19313
19314 
19315
19316 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
19317 If there is not enough free space for a new allocation, but existing allocations
19318 from the front of the queue can become lost, they become lost and the allocation
19319 succeeds.
19320
19321 
19322
19323 Ring buffer is available only in pools with one memory block -
19324 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
19325
19326 \section buddy_algorithm Buddy allocation algorithm
19327
19328 There is another allocation algorithm that can be used with custom pools, called
19329 "buddy". Its internal data structure is based on a binary tree of blocks, each having
19330 size that is a power of two and a half of its parent's size. When you want to
19331 allocate memory of certain size, a free node in the tree is located. If it is too
19332 large, it is recursively split into two halves (called "buddies"). However, if
19333 requested allocation size is not a power of two, the size of the allocation is
19334 aligned up to the nearest power of two and the remaining space is wasted. When
19335 two buddy nodes become free, they are merged back into one larger node.
19336
19337 
19338
19339 The advantage of buddy allocation algorithm over default algorithm is faster
19340 allocation and deallocation, as well as smaller external fragmentation. The
19341 disadvantage is more wasted space (internal fragmentation).
19342 For more information, please search the Internet for "Buddy memory allocation" -
19343 sources that describe this concept in general.
19344
19345 To use buddy allocation algorithm with a custom pool, add flag
19346 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
19347 #VmaPool object.
19348
19349 Several limitations apply to pools that use buddy algorithm:
19350
19351 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
19352 Otherwise, only largest power of two smaller than the size is used for
19353 allocations. The remaining space always stays unused.
19354 - [Margins](@ref debugging_memory_usage_margins) and
19355 [corruption detection](@ref debugging_memory_usage_corruption_detection)
19356 don't work in such pools.
19357 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
19358 use them, but they never become lost. Support may be added in the future.
19359 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
19360 such pool.
19361
19362 \page defragmentation Defragmentation
19363
19364 Interleaved allocations and deallocations of many objects of varying size can
19365 cause fragmentation over time, which can lead to a situation where the library is unable
19366 to find a continuous range of free memory for a new allocation despite there is
19367 enough free space, just scattered across many small free ranges between existing
19368 allocations.
19369
19370 To mitigate this problem, you can use defragmentation feature:
19371 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
19372 Given set of allocations,
19373 this function can move them to compact used memory, ensure more continuous free
19374 space and possibly also free some `VkDeviceMemory` blocks.
19375
19376 What the defragmentation does is:
19377
19378 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
19379 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
19380 VmaAllocationInfo::offset changes. You must query them again using
19381 vmaGetAllocationInfo() if you need them.
19382 - Moves actual data in memory.
19383
19384 What it doesn't do, so you need to do it yourself:
19385
19386 - Recreate buffers and images that were bound to allocations that were defragmented and
19387 bind them with their new places in memory.
19388 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
19389 `vkCreateBuffer()`, `vkCreateImage()`, vmaBindBufferMemory(), vmaBindImageMemory()
19390 for that purpose and NOT vmaDestroyBuffer(),
19391 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
19392 destroy or create allocation objects!
19393 - Recreate views and update descriptors that point to these buffers and images.
19394
19395 \section defragmentation_cpu Defragmenting CPU memory
19396
19397 Following example demonstrates how you can run defragmentation on CPU.
19398 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
19399 Others are ignored.
19400
19401 The way it works is:
19402
19403 - It temporarily maps entire memory blocks when necessary.
19404 - It moves data using `memmove()` function.
19405
19406 \code
19407 // Given following variables already initialized:
19408 VkDevice device;
19409 VmaAllocator allocator;
19410 std::vector<VkBuffer> buffers;
19411 std::vector<VmaAllocation> allocations;
19412
19413
19414 const uint32_t allocCount = (uint32_t)allocations.size();
19415 std::vector<VkBool32> allocationsChanged(allocCount);
19416
19417 VmaDefragmentationInfo2 defragInfo = {};
19418 defragInfo.allocationCount = allocCount;
19419 defragInfo.pAllocations = allocations.data();
19420 defragInfo.pAllocationsChanged = allocationsChanged.data();
19421 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
19422 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
19423
19424 VmaDefragmentationContext defragCtx;
19425 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
19426 vmaDefragmentationEnd(allocator, defragCtx);
19427
19428 for(uint32_t i = 0; i < allocCount; ++i)
19429 {
19430 if(allocationsChanged[i])
19431 {
19432 // Destroy buffer that is immutably bound to memory region which is no longer valid.
19433 vkDestroyBuffer(device, buffers[i], nullptr);
19434
19435 // Create new buffer with same parameters.
19436 VkBufferCreateInfo bufferInfo = ...;
19437 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
19438
19439 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
19440
19441 // Bind new buffer to new memory region. Data contained in it is already moved.
19442 VmaAllocationInfo allocInfo;
19443 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
19444 vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
19445 }
19446 }
19447 \endcode
19448
19449 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
19450 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
19451 has been modified during defragmentation.
19452 You can pass null, but you then need to query every allocation passed to defragmentation
19453 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
19454
19455 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
19456 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
19457 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
19458 to defragment all allocations in given pools.
19459 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
19460 You can also combine both methods.
19461
19462 \section defragmentation_gpu Defragmenting GPU memory
19463
19464 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
19465 To do that, you need to pass a command buffer that meets requirements as described in
19466 VmaDefragmentationInfo2::commandBuffer. The way it works is:
19467
19468 - It creates temporary buffers and binds them to entire memory blocks when necessary.
19469 - It issues `vkCmdCopyBuffer()` to passed command buffer.
19470
19471 Example:
19472
19473 \code
19474 // Given following variables already initialized:
19475 VkDevice device;
19476 VmaAllocator allocator;
19477 VkCommandBuffer commandBuffer;
19478 std::vector<VkBuffer> buffers;
19479 std::vector<VmaAllocation> allocations;
19480
19481
19482 const uint32_t allocCount = (uint32_t)allocations.size();
19483 std::vector<VkBool32> allocationsChanged(allocCount);
19484
19485 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
19486 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
19487
19488 VmaDefragmentationInfo2 defragInfo = {};
19489 defragInfo.allocationCount = allocCount;
19490 defragInfo.pAllocations = allocations.data();
19491 defragInfo.pAllocationsChanged = allocationsChanged.data();
19492 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it is "GPU" this time.
19493 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it is "GPU" this time.
19494 defragInfo.commandBuffer = commandBuffer;
19495
19496 VmaDefragmentationContext defragCtx;
19497 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
19498
19499 vkEndCommandBuffer(commandBuffer);
19500
19501 // Submit commandBuffer.
19502 // Wait for a fence that ensures commandBuffer execution finished.
19503
19504 vmaDefragmentationEnd(allocator, defragCtx);
19505
19506 for(uint32_t i = 0; i < allocCount; ++i)
19507 {
19508 if(allocationsChanged[i])
19509 {
19510 // Destroy buffer that is immutably bound to memory region which is no longer valid.
19511 vkDestroyBuffer(device, buffers[i], nullptr);
19512
19513 // Create new buffer with same parameters.
19514 VkBufferCreateInfo bufferInfo = ...;
19515 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
19516
19517 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
19518
19519 // Bind new buffer to new memory region. Data contained in it is already moved.
19520 VmaAllocationInfo allocInfo;
19521 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
19522 vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
19523 }
19524 }
19525 \endcode
19526
19527 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
19528 The library automatically chooses best method to defragment each memory pool.
19529
19530 You may try not to block your entire program to wait until defragmentation finishes,
19531 but do it in the background, as long as you carefully fullfill requirements described
19532 in function vmaDefragmentationBegin().
19533
19534 \section defragmentation_additional_notes Additional notes
19535
19536 It is only legal to defragment allocations bound to:
19537
19538 - buffers
19539 - images created with `VK_IMAGE_CREATE_ALIAS_BIT`, `VK_IMAGE_TILING_LINEAR`, and
19540 being currently in `VK_IMAGE_LAYOUT_GENERAL` or `VK_IMAGE_LAYOUT_PREINITIALIZED`.
19541
19542 Defragmentation of images created with `VK_IMAGE_TILING_OPTIMAL` or in any other
19543 layout may give undefined results.
19544
19545 If you defragment allocations bound to images, new images to be bound to new
19546 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
19547 and then transitioned to their original layout from before defragmentation if
19548 needed using an image memory barrier.
19549
19550 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
19551 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
19552
19553 Please don't expect memory to be fully compacted after defragmentation.
19554 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
19555 memory blocks to make totally empty to release them, as well as to maximize continuous
19556 empty space inside remaining blocks, while minimizing the number and size of allocations that
19557 need to be moved. Some fragmentation may still remain - this is normal.
19558
19559 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
19560
19561 If you want to implement your own, custom defragmentation algorithm,
19562 there is infrastructure prepared for that,
19563 but it is not exposed through the library API - you need to hack its source code.
19564 Here are steps needed to do this:
19565
19566 -# Main thing you need to do is to define your own class derived from base abstract
19567 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
19568 See definition and comments of this class for details.
19569 -# Your code needs to interact with device memory block metadata.
19570 If you need more access to its data than it is provided by its public interface,
19571 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
19572 -# If you want to create a flag that would enable your algorithm or pass some additional
19573 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
19574 VmaDefragmentationInfo2::flags.
19575 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
19576 of your new class whenever needed.
19577
19578
19579 \page lost_allocations Lost allocations
19580
19581 If your game oversubscribes video memory, if may work OK in previous-generation
19582 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
19583 paged to system RAM. In Vulkan you can't do it because when you run out of
19584 memory, an allocation just fails. If you have more data (e.g. textures) that can
19585 fit into VRAM and you don't need it all at once, you may want to upload them to
19586 GPU on demand and "push out" ones that are not used for a long time to make room
19587 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
19588 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
19589 "lost allocations".
19590
19591 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
19592 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
19593 such allocation in every new frame, you need to query it if it is not lost.
19594 To check it, call vmaTouchAllocation().
19595 If the allocation is lost, you should not use it or buffer/image bound to it.
19596 You mustn't forget to destroy this allocation and this buffer/image.
19597 vmaGetAllocationInfo() can also be used for checking status of the allocation.
19598 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
19599
19600 To create an allocation that can make some other allocations lost to make room
19601 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
19602 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
19603 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
19604
19605 Warning! Current implementation uses quite naive, brute force algorithm,
19606 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
19607 flag quite slow. A new, more optimal algorithm and data structure to speed this
19608 up is planned for the future.
19609
19610 <b>Q: When interleaving creation of new allocations with usage of existing ones,
19611 how do you make sure that an allocation won't become lost while it is used in the
19612 current frame?</b>
19613
19614 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
19615 status/parameters and checks whether it is not lost, but when it is not, it also
19616 atomically marks it as used in the current frame, which makes it impossible to
19617 become lost in that frame. It uses lockless algorithm, so it works fast and
19618 doesn't involve locking any internal mutex.
19619
19620 <b>Q: What if my allocation may still be in use by the GPU when it is rendering a
19621 previous frame while I already submit new frame on the CPU?</b>
19622
19623 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
19624 become lost for a number of additional frames back from the current one by
19625 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
19626 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
19627
19628 <b>Q: How do you inform the library when new frame starts?</b>
19629
19630 You need to call function vmaSetCurrentFrameIndex().
19631
19632 Example code:
19633
19634 \code
19635 struct MyBuffer
19636 {
19637 VkBuffer m_Buf = nullptr;
19638 VmaAllocation m_Alloc = nullptr;
19639
19640 // Called when the buffer is really needed in the current frame.
19641 void EnsureBuffer();
19642 };
19643
19644 void MyBuffer::EnsureBuffer()
19645 {
19646 // Buffer has been created.
19647 if(m_Buf != VK_NULL_HANDLE)
19648 {
19649 // Check if its allocation is not lost + mark it as used in current frame.
19650 if(vmaTouchAllocation(allocator, m_Alloc))
19651 {
19652 // It is all OK - safe to use m_Buf.
19653 return;
19654 }
19655 }
19656
19657 // Buffer not yet exists or lost - destroy and recreate it.
19658
19659 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
19660
19661 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19662 bufCreateInfo.size = 1024;
19663 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19664
19665 VmaAllocationCreateInfo allocCreateInfo = {};
19666 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19667 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
19668 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
19669
19670 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
19671 }
19672 \endcode
19673
19674 When using lost allocations, you may see some Vulkan validation layer warnings
19675 about overlapping regions of memory bound to different kinds of buffers and
19676 images. This is still valid as long as you implement proper handling of lost
19677 allocations (like in the example above) and don't use them.
19678
19679 You can create an allocation that is already in lost state from the beginning using function
19680 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
19681
19682 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
19683 in a specified custom pool to lost state.
19684 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
19685 cannot become lost.
19686
19687 <b>Q: Can I touch allocation that cannot become lost?</b>
19688
19689 Yes, although it has no visible effect.
19690 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
19691 also for allocations that cannot become lost, but the only way to observe it is to dump
19692 internal allocator state using vmaBuildStatsString().
19693 You can use this feature for debugging purposes to explicitly mark allocations that you use
19694 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
19695
19696
19697 \page statistics Statistics
19698
19699 This library contains functions that return information about its internal state,
19700 especially the amount of memory allocated from Vulkan.
19701 Please keep in mind that these functions need to traverse all internal data structures
19702 to gather these information, so they may be quite time-consuming.
19703 Don't call them too often.
19704
19705 \section statistics_numeric_statistics Numeric statistics
19706
19707 You can query for overall statistics of the allocator using function vmaCalculateStats().
19708 Information are returned using structure #VmaStats.
19709 It contains #VmaStatInfo - number of allocated blocks, number of allocations
19710 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
19711 number of bytes used and unused (but still allocated from Vulkan) and other information.
19712 They are summed across memory heaps, memory types and total for whole allocator.
19713
19714 You can query for statistics of a custom pool using function vmaGetPoolStats().
19715 Information are returned using structure #VmaPoolStats.
19716
19717 You can query for information about specific allocation using function vmaGetAllocationInfo().
19718 It fill structure #VmaAllocationInfo.
19719
19720 \section statistics_json_dump JSON dump
19721
19722 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
19723 The result is guaranteed to be correct JSON.
19724 It uses ANSI encoding.
19725 Any strings provided by user (see [Allocation names](@ref allocation_names))
19726 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
19727 this JSON string can be treated as using this encoding.
19728 It must be freed using function vmaFreeStatsString().
19729
19730 The format of this JSON string is not part of official documentation of the library,
19731 but it will not change in backward-incompatible way without increasing library major version number
19732 and appropriate mention in changelog.
19733
19734 The JSON string contains all the data that can be obtained using vmaCalculateStats().
19735 It can also contain detailed map of allocated memory blocks and their regions -
19736 free and occupied by allocations.
19737 This allows e.g. to visualize the memory or assess fragmentation.
19738
19739
19740 \page allocation_annotation Allocation names and user data
19741
19742 \section allocation_user_data Allocation user data
19743
19744 You can annotate allocations with your own information, e.g. for debugging purposes.
19745 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
19746 an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,
19747 some handle, index, key, ordinal number or any other value that would associate
19748 the allocation with your custom metadata.
19749
19750 \code
19751 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19752 // Fill bufferInfo...
19753
19754 MyBufferMetadata* pMetadata = CreateBufferMetadata();
19755
19756 VmaAllocationCreateInfo allocCreateInfo = {};
19757 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19758 allocCreateInfo.pUserData = pMetadata;
19759
19760 VkBuffer buffer;
19761 VmaAllocation allocation;
19762 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
19763 \endcode
19764
19765 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
19766
19767 \code
19768 VmaAllocationInfo allocInfo;
19769 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
19770 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
19771 \endcode
19772
19773 It can also be changed using function vmaSetAllocationUserData().
19774
19775 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
19776 vmaBuildStatsString(), in hexadecimal form.
19777
19778 \section allocation_names Allocation names
19779
19780 There is alternative mode available where `pUserData` pointer is used to point to
19781 a null-terminated string, giving a name to the allocation. To use this mode,
19782 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
19783 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
19784 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
19785 The library creates internal copy of the string, so the pointer you pass doesn't need
19786 to be valid for whole lifetime of the allocation. You can free it after the call.
19787
19788 \code
19789 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19790 // Fill imageInfo...
19791
19792 std::string imageName = "Texture: ";
19793 imageName += fileName;
19794
19795 VmaAllocationCreateInfo allocCreateInfo = {};
19796 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19797 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
19798 allocCreateInfo.pUserData = imageName.c_str();
19799
19800 VkImage image;
19801 VmaAllocation allocation;
19802 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
19803 \endcode
19804
19805 The value of `pUserData` pointer of the allocation will be different than the one
19806 you passed when setting allocation's name - pointing to a buffer managed
19807 internally that holds copy of the string.
19808
19809 \code
19810 VmaAllocationInfo allocInfo;
19811 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
19812 const char* imageName = (const char*)allocInfo.pUserData;
19813 printf("Image name: %s\n", imageName);
19814 \endcode
19815
19816 That string is also printed in JSON report created by vmaBuildStatsString().
19817
19818 \note Passing string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
19819 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
19820
19821
19822 \page virtual_allocator Virtual allocator
19823
19824 As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
19825 It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
19826 You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.
19827 A common use case is sub-allocation of pieces of one large GPU buffer.
19828
19829 \section virtual_allocator_creating_virtual_block Creating virtual block
19830
19831 To use this functionality, there is no main "allocator" object.
19832 You don't need to have #VmaAllocator object created.
19833 All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator:
19834
19835 -# Fill in #VmaVirtualBlockCreateInfo structure.
19836 -# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.
19837
19838 Example:
19839
19840 \code
19841 VmaVirtualBlockCreateInfo blockCreateInfo = {};
19842 blockCreateInfo.size = 1048576; // 1 MB
19843
19844 VmaVirtualBlock block;
19845 VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);
19846 \endcode
19847
19848 \section virtual_allocator_making_virtual_allocations Making virtual allocations
19849
19850 #VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions
19851 using the same code as the main Vulkan memory allocator.
19852 However, there is no "virtual allocation" object.
19853 When you request a new allocation, a `VkDeviceSize` number is returned.
19854 It is an offset inside the block where the allocation has been placed, but it also uniquely identifies the allocation within this block.
19855
19856 In order to make an allocation:
19857
19858 -# Fill in #VmaVirtualAllocationCreateInfo structure.
19859 -# Call vmaVirtualAllocate(). Get new `VkDeviceSize offset` that identifies the allocation.
19860
19861 Example:
19862
19863 \code
19864 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
19865 allocCreateInfo.size = 4096; // 4 KB
19866
19867 VkDeviceSize allocOffset;
19868 res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);
19869 if(res == VK_SUCCESS)
19870 {
19871 // Use the 4 KB of your memory starting at allocOffset.
19872 }
19873 else
19874 {
19875 // Allocation failed - no space for it could be found. Handle this error!
19876 }
19877 \endcode
19878
19879 \section virtual_allocator_deallocation Deallocation
19880
19881 When no longer needed, an allocation can be freed by calling vmaVirtualFree().
19882 You can only pass to this function the exact offset that was previously returned by vmaVirtualAllocate()
19883 and not any other location within the memory.
19884
19885 When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().
19886 All allocations must be freed before the block is destroyed, which is checked internally by an assert.
19887 However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -
19888 a feature not available in normal Vulkan memory allocator. Example:
19889
19890 \code
19891 vmaVirtualFree(block, allocOffset);
19892 vmaDestroyVirtualBlock(block);
19893 \endcode
19894
19895 \section virtual_allocator_allocation_parameters Allocation parameters
19896
19897 You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().
19898 Its default value is null.
19899 It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some
19900 larger data structure containing more information. Example:
19901
19902 \code
19903 struct CustomAllocData
19904 {
19905 std::string m_AllocName;
19906 };
19907 CustomAllocData* allocData = new CustomAllocData();
19908 allocData->m_AllocName = "My allocation 1";
19909 vmaSetVirtualAllocationUserData(block, allocOffset, allocData);
19910 \endcode
19911
19912 The pointer can later be fetched, along with allocation size, by passing the allocation offset to function
19913 vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.
19914 If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
19915 Example:
19916
19917 \code
19918 VmaVirtualAllocationInfo allocInfo;
19919 vmaGetVirtualAllocationInfo(block, allocOffset, &allocInfo);
19920 delete (CustomAllocData*)allocInfo.pUserData;
19921
19922 vmaVirtualFree(block, allocOffset);
19923 \endcode
19924
19925 \section virtual_allocator_alignment_and_units Alignment and units
19926
19927 It feels natural to express sizes and offsets in bytes.
19928 If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member
19929 VmaVirtualAllocationCreateInfo::alignment to request it. Example:
19930
19931 \code
19932 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
19933 allocCreateInfo.size = 4096; // 4 KB
19934 allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
19935
19936 VkDeviceSize allocOffset;
19937 res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);
19938 \endcode
19939
19940 Alignments of different allocations made from one block may vary.
19941 However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
19942 you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
19943 It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
19944
19945 - VmaVirtualBlockCreateInfo::size
19946 - VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment
19947 - Using offset returned by vmaVirtualAllocate()
19948
19949 \section virtual_allocator_statistics Statistics
19950
19951 You can obtain statistics of a virtual block using vmaCalculateVirtualBlockStats().
19952 The function fills structure #VmaStatInfo - same as used by the normal Vulkan memory allocator.
19953 Example:
19954
19955 \code
19956 VmaStatInfo statInfo;
19957 vmaCalculateVirtualBlockStats(block, &statInfo);
19958 printf("My virtual block has %llu bytes used by %u virtual allocations\n",
19959 statInfo.usedBytes, statInfo.allocationCount);
19960 \endcode
19961
19962 You can also request a full list of allocations and free regions as a string in JSON format by calling
19963 vmaBuildVirtualBlockStatsString().
19964 Returned string must be later freed using vmaFreeVirtualBlockStatsString().
19965 The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.
19966
19967 \section virtual_allocator_additional_considerations Additional considerations
19968
19969 The "virtual allocator" functionality is implemented on a level of individual memory blocks.
19970 Keeping track of a whole collection of blocks, allocating new ones when out of free space,
19971 deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
19972
19973 Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.
19974 See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).
19975 You can find their description in chapter \ref custom_memory_pools.
19976 Allocation strategies are also supported.
19977 See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).
19978
19979 Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:
19980 buffer-image granularity, \ref lost_allocations, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.
19981
19982
19983 \page debugging_memory_usage Debugging incorrect memory usage
19984
19985 If you suspect a bug with memory usage, like usage of uninitialized memory or
19986 memory being overwritten out of bounds of an allocation,
19987 you can use debug features of this library to verify this.
19988
19989 \section debugging_memory_usage_initialization Memory initialization
19990
19991 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
19992 you can enable automatic memory initialization to verify this.
19993 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
19994
19995 \code
19996 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
19997 #include "vk_mem_alloc.h"
19998 \endcode
19999
20000 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
20001 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
20002 Memory is automatically mapped and unmapped if necessary.
20003
20004 If you find these values while debugging your program, good chances are that you incorrectly
20005 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
20006
20007 Memory initialization works only with memory types that are `HOST_VISIBLE`.
20008 It works also with dedicated allocations.
20009 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
20010 as they cannot be mapped.
20011
20012 \section debugging_memory_usage_margins Margins
20013
20014 By default, allocations are laid out in memory blocks next to each other if possible
20015 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
20016
20017 
20018
20019 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
20020 number of bytes as a margin before and after every allocation.
20021
20022 \code
20023 #define VMA_DEBUG_MARGIN 16
20024 #include "vk_mem_alloc.h"
20025 \endcode
20026
20027 
20028
20029 If your bug goes away after enabling margins, it means it may be caused by memory
20030 being overwritten outside of allocation boundaries. It is not 100% certain though.
20031 Change in application behavior may also be caused by different order and distribution
20032 of allocations across memory blocks after margins are applied.
20033
20034 The margin is applied also before first and after last allocation in a block.
20035 It may occur only once between two adjacent allocations.
20036
20037 Margins work with all types of memory.
20038
20039 Margin is applied only to allocations made out of memory blocks and not to dedicated
20040 allocations, which have their own memory block of specific size.
20041 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
20042 or those automatically decided to put into dedicated allocations, e.g. due to its
20043 large size or recommended by VK_KHR_dedicated_allocation extension.
20044 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
20045
20046 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
20047
20048 Note that enabling margins increases memory usage and fragmentation.
20049
20050 \section debugging_memory_usage_corruption_detection Corruption detection
20051
20052 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
20053 of contents of the margins.
20054
20055 \code
20056 #define VMA_DEBUG_MARGIN 16
20057 #define VMA_DEBUG_DETECT_CORRUPTION 1
20058 #include "vk_mem_alloc.h"
20059 \endcode
20060
20061 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
20062 (it must be multiply of 4) before and after every allocation is filled with a magic number.
20063 This idea is also know as "canary".
20064 Memory is automatically mapped and unmapped if necessary.
20065
20066 This number is validated automatically when the allocation is destroyed.
20067 If it is not equal to the expected value, `VMA_ASSERT()` is executed.
20068 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
20069 which indicates a serious bug.
20070
20071 You can also explicitly request checking margins of all allocations in all memory blocks
20072 that belong to specified memory types by using function vmaCheckCorruption(),
20073 or in memory blocks that belong to specified custom pool, by using function
20074 vmaCheckPoolCorruption().
20075
20076 Margin validation (corruption detection) works only for memory types that are
20077 `HOST_VISIBLE` and `HOST_COHERENT`.
20078
20079
20080 \page record_and_replay Record and replay
20081
20082 \section record_and_replay_introduction Introduction
20083
20084 While using the library, sequence of calls to its functions together with their
20085 parameters can be recorded to a file and later replayed using standalone player
20086 application. It can be useful to:
20087
20088 - Test correctness - check if same sequence of calls will not cause crash or
20089 failures on a target platform.
20090 - Gather statistics - see number of allocations, peak memory usage, number of
20091 calls etc.
20092 - Benchmark performance - see how much time it takes to replay the whole
20093 sequence.
20094
20095 \section record_and_replay_usage Usage
20096
20097 Recording functionality is disabled by default.
20098 To enable it, define following macro before every include of this library:
20099
20100 \code
20101 #define VMA_RECORDING_ENABLED 1
20102 \endcode
20103
20104 <b>To record sequence of calls to a file:</b> Fill in
20105 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
20106 object. File is opened and written during whole lifetime of the allocator.
20107
20108 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
20109 Precompiled binary can be found in "bin" directory.
20110 Its source can be found in "src/VmaReplay" directory.
20111 Its project is generated by Premake.
20112 Command line syntax is printed when the program is launched without parameters.
20113 Basic usage:
20114
20115 VmaReplay.exe MyRecording.csv
20116
20117 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
20118 It is a human-readable, text file in CSV format (Comma Separated Values).
20119
20120 \section record_and_replay_additional_considerations Additional considerations
20121
20122 - Replaying file that was recorded on a different GPU (with different parameters
20123 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
20124 set of memory heaps and types) may give different performance and memory usage
20125 results, as well as issue some warnings and errors.
20126 - Current implementation of recording in VMA, as well as VmaReplay application, is
20127 coded and tested only on Windows. Inclusion of recording code is driven by
20128 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
20129 add. Contributions are welcomed.
20130
20131
20132 \page opengl_interop OpenGL Interop
20133
20134 VMA provides some features that help with interoperability with OpenGL.
20135
20136 \section opengl_interop_exporting_memory Exporting memory
20137
20138 If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library:
20139
20140 It is recommended to create \ref custom_memory_pools for such allocations.
20141 Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext
20142 while creating the custom pool.
20143 Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,
20144 not only while creating it, as no copy of the structure is made,
20145 but its original pointer is used for each allocation instead.
20146
20147 If you want to export all memory allocated by the library from certain memory types,
20148 also dedicated allocations or other allocations made from default pools,
20149 an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.
20150 It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library
20151 through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.
20152 This is currently the only method to use if you need exported dedicated allocations, as they cannot be created out of custom pools.
20153 This will change in future versions of the library though.
20154
20155 You should not mix these two methods in a way that allows to apply both to the same memory type.
20156 Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.
20157
20158
20159 \section opengl_interop_custom_alignment Custom alignment
20160
20161 Buffers or images exported to a different API like OpenGL may require a different alignment,
20162 higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.
20163 To impose such alignment:
20164
20165 It is recommended to create \ref custom_memory_pools for such allocations.
20166 Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation
20167 to be made out of this pool.
20168 The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image
20169 from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.
20170
20171 If you want to create a buffer with a specific minimum alignment out of default pools,
20172 use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.
20173 This is currently the only method to use if you need exported dedicated allocations, as they cannot be created out of custom pools.
20174 This will change in future versions of the library though.
20175
20176 Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated
20177 allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.
20178 Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.
20179
20180
20181 \page usage_patterns Recommended usage patterns
20182
20183 See also slides from talk:
20184 [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
20185
20186
20187 \section usage_patterns_common_mistakes Common mistakes
20188
20189 <b>Use of CPU_TO_GPU instead of CPU_ONLY memory</b>
20190
20191 #VMA_MEMORY_USAGE_CPU_TO_GPU is recommended only for resources that will be
20192 mapped and written by the CPU, as well as read directly by the GPU - like some
20193 buffers or textures updated every frame (dynamic). If you create a staging copy
20194 of a resource to be written by CPU and then used as a source of transfer to
20195 another resource placed in the GPU memory, that staging resource should be
20196 created with #VMA_MEMORY_USAGE_CPU_ONLY. Please read the descriptions of these
20197 enums carefully for details.
20198
20199 <b>Unnecessary use of custom pools</b>
20200
20201 \ref custom_memory_pools may be useful for special purposes - when you want to
20202 keep certain type of resources separate e.g. to reserve minimum amount of memory
20203 for them, limit maximum amount of memory they can occupy, or make some of them
20204 push out the other through the mechanism of \ref lost_allocations. For most
20205 resources this is not needed and so it is not recommended to create #VmaPool
20206 objects and allocations out of them. Allocating from the default pool is sufficient.
20207
20208 \section usage_patterns_simple Simple patterns
20209
20210 \subsection usage_patterns_simple_render_targets Render targets
20211
20212 <b>When:</b>
20213 Any resources that you frequently write and read on GPU,
20214 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
20215 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
20216
20217 <b>What to do:</b>
20218 Create them in video memory that is fastest to access from GPU using
20219 #VMA_MEMORY_USAGE_GPU_ONLY.
20220
20221 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
20222 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
20223 especially if they are large or if you plan to destroy and recreate them e.g. when
20224 display resolution changes.
20225 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
20226
20227 \subsection usage_patterns_simple_immutable_resources Immutable resources
20228
20229 <b>When:</b>
20230 Any resources that you fill on CPU only once (aka "immutable") or infrequently
20231 and then read frequently on GPU,
20232 e.g. textures, vertex and index buffers, constant buffers that don't change often.
20233
20234 <b>What to do:</b>
20235 Create them in video memory that is fastest to access from GPU using
20236 #VMA_MEMORY_USAGE_GPU_ONLY.
20237
20238 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
20239 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
20240 and submit a transfer from it to the GPU resource.
20241 You can keep the staging copy if you need it for another upload transfer in the future.
20242 If you don't, you can destroy it or reuse this buffer for uploading different resource
20243 after the transfer finishes.
20244
20245 Prefer to create just buffers in system memory rather than images, even for uploading textures.
20246 Use `vkCmdCopyBufferToImage()`.
20247 Dont use images with `VK_IMAGE_TILING_LINEAR`.
20248
20249 \subsection usage_patterns_dynamic_resources Dynamic resources
20250
20251 <b>When:</b>
20252 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
20253 written on CPU, read on GPU.
20254
20255 <b>What to do:</b>
20256 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
20257 You can map it and write to it directly on CPU, as well as read from it on GPU.
20258
20259 This is a more complex situation. Different solutions are possible,
20260 and the best one depends on specific GPU type, but you can use this simple approach for the start.
20261 Prefer to write to such resource sequentially (e.g. using `memcpy`).
20262 Don't perform random access or any reads from it on CPU, as it may be very slow.
20263 Also note that textures written directly from the host through a mapped pointer need to be in LINEAR not OPTIMAL layout.
20264
20265 \subsection usage_patterns_readback Readback
20266
20267 <b>When:</b>
20268 Resources that contain data written by GPU that you want to read back on CPU,
20269 e.g. results of some computations.
20270
20271 <b>What to do:</b>
20272 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
20273 You can write to them directly on GPU, as well as map and read them on CPU.
20274
20275 \section usage_patterns_advanced Advanced patterns
20276
20277 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
20278
20279 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
20280 by detecting it in Vulkan.
20281 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
20282 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
20283 When you find it, you can assume that memory is unified and all memory types are comparably fast
20284 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
20285
20286 You can then sum up sizes of all available memory heaps and treat them as useful for
20287 your GPU resources, instead of only `DEVICE_LOCAL` ones.
20288 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
20289 directly instead of submitting explicit transfer (see below).
20290
20291 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
20292
20293 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
20294
20295 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
20296 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit transfer each time.
20297 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
20298 read it directly on GPU.
20299 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
20300 read it directly on GPU.
20301
20302 Which solution is the most efficient depends on your resource and especially on the GPU.
20303 It is best to measure it and then make the decision.
20304 Some general recommendations:
20305
20306 - On integrated graphics use (2) or (3) to avoid unnecessary time and memory overhead
20307 related to using a second copy and making transfer.
20308 - For small resources (e.g. constant buffers) use (2).
20309 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
20310 Even if the resource ends up in system memory, its data may be cached on GPU after first
20311 fetch over PCIe bus.
20312 - For larger resources (e.g. textures), decide between (1) and (2).
20313 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
20314 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
20315
20316 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
20317 solutions are possible:
20318
20319 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
20320 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
20321 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
20322 map it and read it on CPU.
20323
20324 You should take some measurements to decide which option is faster in case of your specific
20325 resource.
20326
20327 Note that textures accessed directly from the host through a mapped pointer need to be in LINEAR layout,
20328 which may slow down their usage on the device.
20329 Textures accessed only by the device and transfer operations can use OPTIMAL layout.
20330
20331 If you don't want to specialize your code for specific types of GPUs, you can still make
20332 an simple optimization for cases when your resource ends up in mappable memory to use it
20333 directly in this case instead of creating CPU-side staging copy.
20334 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
20335
20336
20337 \page configuration Configuration
20338
20339 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
20340 before each include of this file or change directly in this file to provide
20341 your own implementation of basic facilities like assert, `min()` and `max()` functions,
20342 mutex, atomic etc.
20343 The library uses its own implementation of containers by default, but you can switch to using
20344 STL containers instead.
20345
20346 For example, define `VMA_ASSERT(expr)` before including the library to provide
20347 custom implementation of the assertion, compatible with your project.
20348 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
20349 and empty otherwise.
20350
20351 \section config_Vulkan_functions Pointers to Vulkan functions
20352
20353 There are multiple ways to import pointers to Vulkan functions in the library.
20354 In the simplest case you don't need to do anything.
20355 If the compilation or linking of your program or the initialization of the #VmaAllocator
20356 doesn't work for you, you can try to reconfigure it.
20357
20358 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
20359 like this:
20360
20361 \code
20362 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
20363 \endcode
20364
20365 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
20366
20367 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
20368 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
20369 by using a helper library like [volk](https://github.com/zeux/volk).
20370
20371 Third, VMA tries to fetch remaining pointers that are still null by calling
20372 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
20373 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
20374
20375 Finally, all the function pointers required by the library (considering selected
20376 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
20377
20378
20379 \section custom_memory_allocator Custom host memory allocator
20380
20381 If you use custom allocator for CPU memory rather than default operator `new`
20382 and `delete` from C++, you can make this library using your allocator as well
20383 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
20384 functions will be passed to Vulkan, as well as used by the library itself to
20385 make any CPU-side allocations.
20386
20387 \section allocation_callbacks Device memory allocation callbacks
20388
20389 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
20390 You can setup callbacks to be informed about these calls, e.g. for the purpose
20391 of gathering some statistics. To do it, fill optional member
20392 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
20393
20394 \section heap_memory_limit Device heap memory limit
20395
20396 When device memory of certain heap runs out of free space, new allocations may
20397 fail (returning error code) or they may succeed, silently pushing some existing
20398 memory blocks from GPU VRAM to system RAM (which degrades performance). This
20399 behavior is implementation-dependent - it depends on GPU vendor and graphics
20400 driver.
20401
20402 On AMD cards it can be controlled while creating Vulkan device object by using
20403 VK_AMD_memory_overallocation_behavior extension, if available.
20404
20405 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
20406 memory available without switching your graphics card to one that really has
20407 smaller VRAM, you can use a feature of this library intended for this purpose.
20408 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
20409
20410
20411
20412 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
20413
20414 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
20415 performance on some GPUs. It augments Vulkan API with possibility to query
20416 driver whether it prefers particular buffer or image to have its own, dedicated
20417 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
20418 to do some internal optimizations.
20419
20420 The extension is supported by this library. It will be used automatically when
20421 enabled. To enable it:
20422
20423 1 . When creating Vulkan device, check if following 2 device extensions are
20424 supported (call `vkEnumerateDeviceExtensionProperties()`).
20425 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
20426
20427 - VK_KHR_get_memory_requirements2
20428 - VK_KHR_dedicated_allocation
20429
20430 If you enabled these extensions:
20431
20432 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
20433 your #VmaAllocator`to inform the library that you enabled required extensions
20434 and you want the library to use them.
20435
20436 \code
20437 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
20438
20439 vmaCreateAllocator(&allocatorInfo, &allocator);
20440 \endcode
20441
20442 That is all. The extension will be automatically used whenever you create a
20443 buffer using vmaCreateBuffer() or image using vmaCreateImage().
20444
20445 When using the extension together with Vulkan Validation Layer, you will receive
20446 warnings like this:
20447
20448 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
20449
20450 It is OK, you should just ignore it. It happens because you use function
20451 `vkGetBufferMemoryRequirements2KHR()` instead of standard
20452 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
20453 unaware of it.
20454
20455 To learn more about this extension, see:
20456
20457 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)
20458 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
20459
20460
20461
20462 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
20463
20464 VK_AMD_device_coherent_memory is a device extension that enables access to
20465 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
20466 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
20467 allocation of buffers intended for writing "breadcrumb markers" in between passes
20468 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
20469
20470 When the extension is available but has not been enabled, Vulkan physical device
20471 still exposes those memory types, but their usage is forbidden. VMA automatically
20472 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
20473 to allocate memory of such type is made.
20474
20475 If you want to use this extension in connection with VMA, follow these steps:
20476
20477 \section vk_amd_device_coherent_memory_initialization Initialization
20478
20479 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
20480 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
20481
20482 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
20483 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
20484 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
20485
20486 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
20487 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
20488
20489 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
20490 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
20491 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
20492 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
20493
20494 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
20495 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
20496 to VmaAllocatorCreateInfo::flags.
20497
20498 \section vk_amd_device_coherent_memory_usage Usage
20499
20500 After following steps described above, you can create VMA allocations and custom pools
20501 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
20502 devices. There are multiple ways to do it, for example:
20503
20504 - You can request or prefer to allocate out of such memory types by adding
20505 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
20506 or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
20507 other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
20508 - If you manually found memory type index to use for this purpose, force allocation
20509 from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
20510
20511 \section vk_amd_device_coherent_memory_more_information More information
20512
20513 To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html)
20514
20515 Example use of this extension can be found in the code of the sample and test suite
20516 accompanying this library.
20517
20518
20519 \page enabling_buffer_device_address Enabling buffer device address
20520
20521 Device extension VK_KHR_buffer_device_address
20522 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
20523 It is promoted to core Vulkan 1.2.
20524
20525 If you want to use this feature in connection with VMA, follow these steps:
20526
20527 \section enabling_buffer_device_address_initialization Initialization
20528
20529 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
20530 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
20531 "VK_KHR_buffer_device_address".
20532
20533 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
20534 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
20535 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true.
20536
20537 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
20538 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
20539
20540 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
20541 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
20542 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
20543 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
20544
20545 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
20546 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
20547 to VmaAllocatorCreateInfo::flags.
20548
20549 \section enabling_buffer_device_address_usage Usage
20550
20551 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
20552 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
20553 allocated memory blocks wherever it might be needed.
20554
20555 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
20556 The second part of this functionality related to "capture and replay" is not supported,
20557 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
20558
20559 \section enabling_buffer_device_address_more_information More information
20560
20561 To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address)
20562
20563 Example use of this extension can be found in the code of the sample and test suite
20564 accompanying this library.
20565
20566 \page general_considerations General considerations
20567
20568 \section general_considerations_thread_safety Thread safety
20569
20570 - The library has no global state, so separate #VmaAllocator objects can be used
20571 independently.
20572 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
20573 - By default, all calls to functions that take #VmaAllocator as first parameter
20574 are safe to call from multiple threads simultaneously because they are
20575 synchronized internally when needed.
20576 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
20577 flag, calls to functions that take such #VmaAllocator object must be
20578 synchronized externally.
20579 - Access to a #VmaAllocation object must be externally synchronized. For example,
20580 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
20581 threads at the same time if you pass the same #VmaAllocation object to these
20582 functions.
20583
20584 \section general_considerations_validation_layer_warnings Validation layer warnings
20585
20586 When using this library, you can meet following types of warnings issued by
20587 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
20588 to just ignore them.
20589
20590 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
20591 - It happens when VK_KHR_dedicated_allocation extension is enabled.
20592 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
20593 - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
20594 - It happens when you map a buffer or image, because the library maps entire
20595 `VkDeviceMemory` block, where different types of images and buffers may end
20596 up together, especially on GPUs with unified memory like Intel.
20597 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
20598 - It happens when you use lost allocations, and a new image or buffer is
20599 created in place of an existing object that became lost.
20600 - It may happen also when you use [defragmentation](@ref defragmentation).
20601
20602 \section general_considerations_allocation_algorithm Allocation algorithm
20603
20604 The library uses following algorithm for allocation, in order:
20605
20606 -# Try to find free range of memory in existing blocks.
20607 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
20608 -# If failed, try to create such block with size/2, size/4, size/8.
20609 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
20610 specified, try to find space in existing blocks, possilby making some other
20611 allocations lost.
20612 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
20613 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
20614 -# If failed, choose other memory type that meets the requirements specified in
20615 VmaAllocationCreateInfo and go to point 1.
20616 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
20617
20618 \section general_considerations_features_not_supported Features not supported
20619
20620 Features deliberately excluded from the scope of this library:
20621
20622 - **Data transfer.** Uploading (streaming) and downloading data of buffers and images
20623 between CPU and GPU memory and related synchronization is responsibility of the user.
20624 Defining some "texture" object that would automatically stream its data from a
20625 staging copy in CPU memory to GPU memory would rather be a feature of another,
20626 higher-level library implemented on top of VMA.
20627 - **Recreation of buffers and images.** Although the library has functions for
20628 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
20629 recreate these objects yourself after defragmentation. That is because the big
20630 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
20631 #VmaAllocation object.
20632 - **Handling CPU memory allocation failures.** When dynamically creating small C++
20633 objects in CPU memory (not Vulkan memory), allocation failures are not checked
20634 and handled gracefully, because that would complicate code significantly and
20635 is usually not needed in desktop PC applications anyway.
20636 Success of an allocation is just checked with an assert.
20637 - **Code free of any compiler warnings.** Maintaining the library to compile and
20638 work correctly on so many different platforms is hard enough. Being free of
20639 any warnings, on any version of any compiler, is simply not feasible.
20640 There are many preprocessor macros that make some variables unused, function parameters unreferenced,
20641 or conditional expressions constant in some configurations.
20642 The code of this library should not be bigger or more complicated just to silence these warnings.
20643 It is recommended to disable such warnings instead.
20644 - This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but
20645 are not going to be included into this repository.
20646 */
20647