Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/vulkan/vk_mem_alloc.h
11211 views
1
//
2
// Copyright (c) 2017-2024 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.1.0</b>
29
30
Copyright (c) 2017-2024 Advanced Micro Devices, Inc. All rights reserved. \n
31
License: MIT \n
32
See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/),
33
[repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
34
35
36
<b>API documentation divided into groups:</b> [Topics](topics.html)
37
38
<b>General documentation chapters:</b>
39
40
- <b>User guide</b>
41
- \subpage quick_start
42
- [Project setup](@ref quick_start_project_setup)
43
- [Initialization](@ref quick_start_initialization)
44
- [Resource allocation](@ref quick_start_resource_allocation)
45
- \subpage choosing_memory_type
46
- [Usage](@ref choosing_memory_type_usage)
47
- [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
48
- [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
49
- [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
50
- [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
51
- \subpage memory_mapping
52
- [Copy functions](@ref memory_mapping_copy_functions)
53
- [Mapping functions](@ref memory_mapping_mapping_functions)
54
- [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
55
- [Cache flush and invalidate](@ref memory_mapping_cache_control)
56
- \subpage staying_within_budget
57
- [Querying for budget](@ref staying_within_budget_querying_for_budget)
58
- [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
59
- \subpage resource_aliasing
60
- \subpage custom_memory_pools
61
- [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
62
- [When not to use custom pools](@ref custom_memory_pools_when_not_use)
63
- [Linear allocation algorithm](@ref linear_algorithm)
64
- [Free-at-once](@ref linear_algorithm_free_at_once)
65
- [Stack](@ref linear_algorithm_stack)
66
- [Double stack](@ref linear_algorithm_double_stack)
67
- [Ring buffer](@ref linear_algorithm_ring_buffer)
68
- \subpage defragmentation
69
- \subpage statistics
70
- [Numeric statistics](@ref statistics_numeric_statistics)
71
- [JSON dump](@ref statistics_json_dump)
72
- \subpage allocation_annotation
73
- [Allocation user data](@ref allocation_user_data)
74
- [Allocation names](@ref allocation_names)
75
- \subpage virtual_allocator
76
- \subpage debugging_memory_usage
77
- [Memory initialization](@ref debugging_memory_usage_initialization)
78
- [Margins](@ref debugging_memory_usage_margins)
79
- [Corruption detection](@ref debugging_memory_usage_corruption_detection)
80
- [Leak detection features](@ref debugging_memory_usage_leak_detection)
81
- \subpage other_api_interop
82
- \subpage usage_patterns
83
- [GPU-only resource](@ref usage_patterns_gpu_only)
84
- [Staging copy for upload](@ref usage_patterns_staging_copy_upload)
85
- [Readback](@ref usage_patterns_readback)
86
- [Advanced data uploading](@ref usage_patterns_advanced_data_uploading)
87
- [Other use cases](@ref usage_patterns_other_use_cases)
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
- <b>Extension support</b>
94
- \subpage vk_khr_dedicated_allocation
95
- \subpage enabling_buffer_device_address
96
- \subpage vk_ext_memory_priority
97
- \subpage vk_amd_device_coherent_memory
98
- \subpage general_considerations
99
- [Thread safety](@ref general_considerations_thread_safety)
100
- [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
101
- [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
102
- [Allocation algorithm](@ref general_considerations_allocation_algorithm)
103
- [Features not supported](@ref general_considerations_features_not_supported)
104
105
\defgroup group_init Library initialization
106
107
\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object.
108
109
\defgroup group_alloc Memory allocation
110
111
\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images.
112
Most basic ones being: vmaCreateBuffer(), vmaCreateImage().
113
114
\defgroup group_virtual Virtual allocator
115
116
\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm
117
for user-defined purpose without allocating any real GPU memory.
118
119
\defgroup group_stats Statistics
120
121
\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format.
122
See documentation chapter: \ref statistics.
123
*/
124
125
#include "drivers/vulkan/godot_vulkan.h"
126
127
#ifdef __cplusplus
128
extern "C" {
129
#endif
130
131
132
#if !defined(VMA_VULKAN_VERSION)
133
#if defined(VK_VERSION_1_3)
134
#define VMA_VULKAN_VERSION 1003000
135
#elif defined(VK_VERSION_1_2)
136
#define VMA_VULKAN_VERSION 1002000
137
#elif defined(VK_VERSION_1_1)
138
#define VMA_VULKAN_VERSION 1001000
139
#else
140
#define VMA_VULKAN_VERSION 1000000
141
#endif
142
#endif
143
144
#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
145
extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
146
extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
147
extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
148
extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
149
extern PFN_vkAllocateMemory vkAllocateMemory;
150
extern PFN_vkFreeMemory vkFreeMemory;
151
extern PFN_vkMapMemory vkMapMemory;
152
extern PFN_vkUnmapMemory vkUnmapMemory;
153
extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
154
extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
155
extern PFN_vkBindBufferMemory vkBindBufferMemory;
156
extern PFN_vkBindImageMemory vkBindImageMemory;
157
extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
158
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
159
extern PFN_vkCreateBuffer vkCreateBuffer;
160
extern PFN_vkDestroyBuffer vkDestroyBuffer;
161
extern PFN_vkCreateImage vkCreateImage;
162
extern PFN_vkDestroyImage vkDestroyImage;
163
extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
164
#if VMA_VULKAN_VERSION >= 1001000
165
extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
166
extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
167
extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
168
extern PFN_vkBindImageMemory2 vkBindImageMemory2;
169
extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
170
#endif // #if VMA_VULKAN_VERSION >= 1001000
171
#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
172
173
#if !defined(VMA_DEDICATED_ALLOCATION)
174
#if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
175
#define VMA_DEDICATED_ALLOCATION 1
176
#else
177
#define VMA_DEDICATED_ALLOCATION 0
178
#endif
179
#endif
180
181
#if !defined(VMA_BIND_MEMORY2)
182
#if VK_KHR_bind_memory2
183
#define VMA_BIND_MEMORY2 1
184
#else
185
#define VMA_BIND_MEMORY2 0
186
#endif
187
#endif
188
189
#if !defined(VMA_MEMORY_BUDGET)
190
#if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
191
#define VMA_MEMORY_BUDGET 1
192
#else
193
#define VMA_MEMORY_BUDGET 0
194
#endif
195
#endif
196
197
// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
198
#if !defined(VMA_BUFFER_DEVICE_ADDRESS)
199
#if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
200
#define VMA_BUFFER_DEVICE_ADDRESS 1
201
#else
202
#define VMA_BUFFER_DEVICE_ADDRESS 0
203
#endif
204
#endif
205
206
// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
207
#if !defined(VMA_MEMORY_PRIORITY)
208
#if VK_EXT_memory_priority
209
#define VMA_MEMORY_PRIORITY 1
210
#else
211
#define VMA_MEMORY_PRIORITY 0
212
#endif
213
#endif
214
215
// Defined to 1 when VK_KHR_maintenance4 device extension is defined in Vulkan headers.
216
#if !defined(VMA_KHR_MAINTENANCE4)
217
#if VK_KHR_maintenance4
218
#define VMA_KHR_MAINTENANCE4 1
219
#else
220
#define VMA_KHR_MAINTENANCE4 0
221
#endif
222
#endif
223
224
// Defined to 1 when VK_KHR_maintenance5 device extension is defined in Vulkan headers.
225
#if !defined(VMA_KHR_MAINTENANCE5)
226
#if VK_KHR_maintenance5
227
#define VMA_KHR_MAINTENANCE5 1
228
#else
229
#define VMA_KHR_MAINTENANCE5 0
230
#endif
231
#endif
232
233
234
// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.
235
#if !defined(VMA_EXTERNAL_MEMORY)
236
#if VK_KHR_external_memory
237
#define VMA_EXTERNAL_MEMORY 1
238
#else
239
#define VMA_EXTERNAL_MEMORY 0
240
#endif
241
#endif
242
243
// Define these macros to decorate all public functions with additional code,
244
// before and after returned type, appropriately. This may be useful for
245
// exporting the functions when compiling VMA as a separate library. Example:
246
// #define VMA_CALL_PRE __declspec(dllexport)
247
// #define VMA_CALL_POST __cdecl
248
#ifndef VMA_CALL_PRE
249
#define VMA_CALL_PRE
250
#endif
251
#ifndef VMA_CALL_POST
252
#define VMA_CALL_POST
253
#endif
254
255
// Define this macro to decorate pNext pointers with an attribute specifying the Vulkan
256
// structure that will be extended via the pNext chain.
257
#ifndef VMA_EXTENDS_VK_STRUCT
258
#define VMA_EXTENDS_VK_STRUCT(vkStruct)
259
#endif
260
261
// Define this macro to decorate pointers with an attribute specifying the
262
// length of the array they point to if they are not null.
263
//
264
// The length may be one of
265
// - The name of another parameter in the argument list where the pointer is declared
266
// - The name of another member in the struct where the pointer is declared
267
// - The name of a member of a struct type, meaning the value of that member in
268
// the context of the call. For example
269
// VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
270
// this means the number of memory heaps available in the device associated
271
// with the VmaAllocator being dealt with.
272
#ifndef VMA_LEN_IF_NOT_NULL
273
#define VMA_LEN_IF_NOT_NULL(len)
274
#endif
275
276
// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
277
// see: https://clang.llvm.org/docs/AttributeReference.html#nullable
278
#ifndef VMA_NULLABLE
279
#ifdef __clang__
280
#define VMA_NULLABLE _Nullable
281
#else
282
#define VMA_NULLABLE
283
#endif
284
#endif
285
286
// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
287
// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
288
#ifndef VMA_NOT_NULL
289
#ifdef __clang__
290
#define VMA_NOT_NULL _Nonnull
291
#else
292
#define VMA_NOT_NULL
293
#endif
294
#endif
295
296
// If non-dispatchable handles are represented as pointers then we can give
297
// then nullability annotations
298
#ifndef VMA_NOT_NULL_NON_DISPATCHABLE
299
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
300
#define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
301
#else
302
#define VMA_NOT_NULL_NON_DISPATCHABLE
303
#endif
304
#endif
305
306
#ifndef VMA_NULLABLE_NON_DISPATCHABLE
307
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
308
#define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
309
#else
310
#define VMA_NULLABLE_NON_DISPATCHABLE
311
#endif
312
#endif
313
314
#ifndef VMA_STATS_STRING_ENABLED
315
#define VMA_STATS_STRING_ENABLED 1
316
#endif
317
318
////////////////////////////////////////////////////////////////////////////////
319
////////////////////////////////////////////////////////////////////////////////
320
//
321
// INTERFACE
322
//
323
////////////////////////////////////////////////////////////////////////////////
324
////////////////////////////////////////////////////////////////////////////////
325
326
// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE.
327
#ifndef _VMA_ENUM_DECLARATIONS
328
329
/**
330
\addtogroup group_init
331
@{
332
*/
333
334
/// Flags for created #VmaAllocator.
335
typedef enum VmaAllocatorCreateFlagBits
336
{
337
/** \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.
338
339
Using this flag may increase performance because internal mutexes are not used.
340
*/
341
VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
342
/** \brief Enables usage of VK_KHR_dedicated_allocation extension.
343
344
The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
345
When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
346
347
Using this extension will automatically allocate dedicated blocks of memory for
348
some buffers and images instead of suballocating place for them out of bigger
349
memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
350
flag) when it is recommended by the driver. It may improve performance on some
351
GPUs.
352
353
You may set this flag only if you found out that following device extensions are
354
supported, you enabled them while creating Vulkan device passed as
355
VmaAllocatorCreateInfo::device, and you want them to be used internally by this
356
library:
357
358
- VK_KHR_get_memory_requirements2 (device extension)
359
- VK_KHR_dedicated_allocation (device extension)
360
361
When this flag is set, you can experience following warnings reported by Vulkan
362
validation layer. You can ignore them.
363
364
> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
365
*/
366
VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
367
/**
368
Enables usage of VK_KHR_bind_memory2 extension.
369
370
The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
371
When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
372
373
You may set this flag only if you found out that this device extension is supported,
374
you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
375
and you want it to be used internally by this library.
376
377
The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
378
which allow to pass a chain of `pNext` structures while binding.
379
This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
380
*/
381
VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
382
/**
383
Enables usage of VK_EXT_memory_budget extension.
384
385
You may set this flag only if you found out that this device extension is supported,
386
you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
387
and you want it to be used internally by this library, along with another instance extension
388
VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
389
390
The extension provides query for current memory usage and budget, which will probably
391
be more accurate than an estimation used by the library otherwise.
392
*/
393
VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
394
/**
395
Enables usage of VK_AMD_device_coherent_memory extension.
396
397
You may set this flag only if you:
398
399
- found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
400
- checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
401
- want it to be used internally by this library.
402
403
The extension and accompanying device feature provide access to memory types with
404
`VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
405
They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
406
407
When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
408
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,
409
returning `VK_ERROR_FEATURE_NOT_PRESENT`.
410
*/
411
VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
412
/**
413
Enables usage of "buffer device address" feature, which allows you to use function
414
`vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
415
416
You may set this flag only if you:
417
418
1. (For Vulkan version < 1.2) Found as available and enabled device extension
419
VK_KHR_buffer_device_address.
420
This extension is promoted to core Vulkan 1.2.
421
2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.
422
423
When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.
424
The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to
425
allocated memory blocks wherever it might be needed.
426
427
For more information, see documentation chapter \ref enabling_buffer_device_address.
428
*/
429
VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
430
/**
431
Enables usage of VK_EXT_memory_priority extension in the library.
432
433
You may set this flag only if you found available and enabled this device extension,
434
along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,
435
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
436
437
When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority
438
are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.
439
440
A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
441
Larger values are higher priority. The granularity of the priorities is implementation-dependent.
442
It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.
443
The value to be used for default priority is 0.5.
444
For more details, see the documentation of the VK_EXT_memory_priority extension.
445
*/
446
VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,
447
/**
448
Enables usage of VK_KHR_maintenance4 extension in the library.
449
450
You may set this flag only if you found available and enabled this device extension,
451
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
452
*/
453
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT = 0x00000080,
454
/**
455
Enables usage of VK_KHR_maintenance5 extension in the library.
456
457
You should set this flag if you found available and enabled this device extension,
458
while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
459
*/
460
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,
461
462
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
463
} VmaAllocatorCreateFlagBits;
464
/// See #VmaAllocatorCreateFlagBits.
465
typedef VkFlags VmaAllocatorCreateFlags;
466
467
/** @} */
468
469
/**
470
\addtogroup group_alloc
471
@{
472
*/
473
474
/// \brief Intended usage of the allocated memory.
475
typedef enum VmaMemoryUsage
476
{
477
/** No intended memory usage specified.
478
Use other members of VmaAllocationCreateInfo to specify your requirements.
479
*/
480
VMA_MEMORY_USAGE_UNKNOWN = 0,
481
/**
482
\deprecated Obsolete, preserved for backward compatibility.
483
Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
484
*/
485
VMA_MEMORY_USAGE_GPU_ONLY = 1,
486
/**
487
\deprecated Obsolete, preserved for backward compatibility.
488
Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`.
489
*/
490
VMA_MEMORY_USAGE_CPU_ONLY = 2,
491
/**
492
\deprecated Obsolete, preserved for backward compatibility.
493
Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
494
*/
495
VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
496
/**
497
\deprecated Obsolete, preserved for backward compatibility.
498
Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
499
*/
500
VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
501
/**
502
\deprecated Obsolete, preserved for backward compatibility.
503
Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
504
*/
505
VMA_MEMORY_USAGE_CPU_COPY = 5,
506
/**
507
Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
508
Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
509
510
Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
511
512
Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
513
*/
514
VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
515
/**
516
Selects best memory type automatically.
517
This flag is recommended for most common use cases.
518
519
When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
520
you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
521
in VmaAllocationCreateInfo::flags.
522
523
It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
524
vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
525
and not with generic memory allocation functions.
526
*/
527
VMA_MEMORY_USAGE_AUTO = 7,
528
/**
529
Selects best memory type automatically with preference for GPU (device) memory.
530
531
When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
532
you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
533
in VmaAllocationCreateInfo::flags.
534
535
It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
536
vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
537
and not with generic memory allocation functions.
538
*/
539
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8,
540
/**
541
Selects best memory type automatically with preference for CPU (host) memory.
542
543
When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
544
you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
545
in VmaAllocationCreateInfo::flags.
546
547
It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
548
vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
549
and not with generic memory allocation functions.
550
*/
551
VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9,
552
553
VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
554
} VmaMemoryUsage;
555
556
/// Flags to be passed as VmaAllocationCreateInfo::flags.
557
typedef enum VmaAllocationCreateFlagBits
558
{
559
/** \brief Set this flag if the allocation should have its own memory block.
560
561
Use it for special, big resources, like fullscreen images used as attachments.
562
563
If you use this flag while creating a buffer or an image, `VkMemoryDedicatedAllocateInfo`
564
structure is applied if possible.
565
*/
566
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
567
568
/** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
569
570
If new allocation cannot be placed in any of the existing blocks, allocation
571
fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
572
573
You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
574
#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
575
*/
576
VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
577
/** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
578
579
Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
580
581
It is valid to use this flag for allocation made from memory type that is not
582
`HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
583
useful if you need an allocation that is efficient to use on GPU
584
(`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
585
support it (e.g. Intel GPU).
586
*/
587
VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
588
/** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead.
589
590
Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
591
null-terminated string. Instead of copying pointer value, a local copy of the
592
string is made and stored in allocation's `pName`. The string is automatically
593
freed together with the allocation. It is also used in vmaBuildStatsString().
594
*/
595
VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
596
/** Allocation will be created from upper stack in a double stack pool.
597
598
This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
599
*/
600
VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
601
/** Create both buffer/image and allocation, but don't bind them together.
602
It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
603
The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
604
Otherwise it is ignored.
605
606
If you want to make sure the new buffer/image is not tied to the new memory allocation
607
through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block,
608
use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT.
609
*/
610
VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
611
/** Create allocation only if additional device memory required for it, if any, won't exceed
612
memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
613
*/
614
VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
615
/** \brief Set this flag if the allocated memory will have aliasing resources.
616
617
Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified.
618
Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors.
619
*/
620
VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200,
621
/**
622
Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
623
624
- If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
625
you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
626
- If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
627
This includes allocations created in \ref custom_memory_pools.
628
629
Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number,
630
never read or accessed randomly, so a memory type can be selected that is uncached and write-combined.
631
632
\warning Violating this declaration may work correctly, but will likely be very slow.
633
Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;`
634
Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once.
635
*/
636
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400,
637
/**
638
Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
639
640
- If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
641
you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
642
- If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
643
This includes allocations created in \ref custom_memory_pools.
644
645
Declares that mapped memory can be read, written, and accessed in random order,
646
so a `HOST_CACHED` memory type is preferred.
647
*/
648
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800,
649
/**
650
Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
651
it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected
652
if it may improve performance.
653
654
By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type
655
(e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and
656
issue an explicit transfer to write/read your data.
657
To prepare for this possibility, don't forget to add appropriate flags like
658
`VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image.
659
*/
660
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000,
661
/** Allocation strategy that chooses smallest possible free range for the allocation
662
to minimize memory usage and fragmentation, possibly at the expense of allocation time.
663
*/
664
VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000,
665
/** Allocation strategy that chooses first suitable free range for the allocation -
666
not necessarily in terms of the smallest offset but the one that is easiest and fastest to find
667
to minimize allocation time, possibly at the expense of allocation quality.
668
*/
669
VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000,
670
/** Allocation strategy that chooses always the lowest offset in available space.
671
This is not the most efficient strategy but achieves highly packed data.
672
Used internally by defragmentation, not recommended in typical usage.
673
*/
674
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = 0x00040000,
675
/** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT.
676
*/
677
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
678
/** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT.
679
*/
680
VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
681
/** A bit mask to extract only `STRATEGY` bits from entire set of flags.
682
*/
683
VMA_ALLOCATION_CREATE_STRATEGY_MASK =
684
VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT |
685
VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT |
686
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
687
688
VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
689
} VmaAllocationCreateFlagBits;
690
/// See #VmaAllocationCreateFlagBits.
691
typedef VkFlags VmaAllocationCreateFlags;
692
693
/// Flags to be passed as VmaPoolCreateInfo::flags.
694
typedef enum VmaPoolCreateFlagBits
695
{
696
/** \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.
697
698
This is an optional optimization flag.
699
700
If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
701
vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
702
knows exact type of your allocations so it can handle Buffer-Image Granularity
703
in the optimal way.
704
705
If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
706
exact type of such allocations is not known, so allocator must be conservative
707
in handling Buffer-Image Granularity, which can lead to suboptimal allocation
708
(wasted memory). In that case, if you can make sure you always allocate only
709
buffers and linear images or only optimal images out of this pool, use this flag
710
to make allocator disregard Buffer-Image Granularity and so make allocations
711
faster and more optimal.
712
*/
713
VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
714
715
/** \brief Enables alternative, linear allocation algorithm in this pool.
716
717
Specify this flag to enable linear allocation algorithm, which always creates
718
new allocations after last one and doesn't reuse space from allocations freed in
719
between. It trades memory consumption for simplified algorithm and data
720
structure, which has better performance and uses less memory for metadata.
721
722
By using this flag, you can achieve behavior of free-at-once, stack,
723
ring buffer, and double stack.
724
For details, see documentation chapter \ref linear_algorithm.
725
*/
726
VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
727
728
/** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
729
*/
730
VMA_POOL_CREATE_ALGORITHM_MASK =
731
VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT,
732
733
VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
734
} VmaPoolCreateFlagBits;
735
/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.
736
typedef VkFlags VmaPoolCreateFlags;
737
738
/// Flags to be passed as VmaDefragmentationInfo::flags.
739
typedef enum VmaDefragmentationFlagBits
740
{
741
/* \brief Use simple but fast algorithm for defragmentation.
742
May not achieve best results but will require least time to compute and least allocations to copy.
743
*/
744
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1,
745
/* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified.
746
Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved.
747
*/
748
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2,
749
/* \brief Perform full defragmentation of memory.
750
Can result in notably more time to compute and allocations to copy, but will achieve best memory packing.
751
*/
752
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4,
753
/** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make.
754
Only available when bufferImageGranularity is greater than 1, since it aims to reduce
755
alignment issues between different types of resources.
756
Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT.
757
*/
758
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8,
759
760
/// A bit mask to extract only `ALGORITHM` bits from entire set of flags.
761
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK =
762
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT |
763
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT |
764
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT |
765
VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT,
766
767
VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
768
} VmaDefragmentationFlagBits;
769
/// See #VmaDefragmentationFlagBits.
770
typedef VkFlags VmaDefragmentationFlags;
771
772
/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove.
773
typedef enum VmaDefragmentationMoveOperation
774
{
775
/// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass().
776
VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0,
777
/// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged.
778
VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1,
779
/// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed.
780
VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2,
781
} VmaDefragmentationMoveOperation;
782
783
/** @} */
784
785
/**
786
\addtogroup group_virtual
787
@{
788
*/
789
790
/// Flags to be passed as VmaVirtualBlockCreateInfo::flags.
791
typedef enum VmaVirtualBlockCreateFlagBits
792
{
793
/** \brief Enables alternative, linear allocation algorithm in this virtual block.
794
795
Specify this flag to enable linear allocation algorithm, which always creates
796
new allocations after last one and doesn't reuse space from allocations freed in
797
between. It trades memory consumption for simplified algorithm and data
798
structure, which has better performance and uses less memory for metadata.
799
800
By using this flag, you can achieve behavior of free-at-once, stack,
801
ring buffer, and double stack.
802
For details, see documentation chapter \ref linear_algorithm.
803
*/
804
VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,
805
806
/** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.
807
*/
808
VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =
809
VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT,
810
811
VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
812
} VmaVirtualBlockCreateFlagBits;
813
/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.
814
typedef VkFlags VmaVirtualBlockCreateFlags;
815
816
/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.
817
typedef enum VmaVirtualAllocationCreateFlagBits
818
{
819
/** \brief Allocation will be created from upper stack in a double stack pool.
820
821
This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
822
*/
823
VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
824
/** \brief Allocation strategy that tries to minimize memory usage.
825
*/
826
VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
827
/** \brief Allocation strategy that tries to minimize allocation time.
828
*/
829
VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
830
/** Allocation strategy that chooses always the lowest offset in available space.
831
This is not the most efficient strategy but achieves highly packed data.
832
*/
833
VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
834
/** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.
835
836
These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.
837
*/
838
VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
839
840
VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
841
} VmaVirtualAllocationCreateFlagBits;
842
/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.
843
typedef VkFlags VmaVirtualAllocationCreateFlags;
844
845
/** @} */
846
847
#endif // _VMA_ENUM_DECLARATIONS
848
849
#ifndef _VMA_DATA_TYPES_DECLARATIONS
850
851
/**
852
\addtogroup group_init
853
@{ */
854
855
/** \struct VmaAllocator
856
\brief Represents main object of this library initialized.
857
858
Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
859
Call function vmaDestroyAllocator() to destroy it.
860
861
It is recommended to create just one object of this type per `VkDevice` object,
862
right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
863
*/
864
VK_DEFINE_HANDLE(VmaAllocator)
865
866
/** @} */
867
868
/**
869
\addtogroup group_alloc
870
@{
871
*/
872
873
/** \struct VmaPool
874
\brief Represents custom memory pool
875
876
Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
877
Call function vmaDestroyPool() to destroy it.
878
879
For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
880
*/
881
VK_DEFINE_HANDLE(VmaPool)
882
883
/** \struct VmaAllocation
884
\brief Represents single memory allocation.
885
886
It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
887
plus unique offset.
888
889
There are multiple ways to create such object.
890
You need to fill structure VmaAllocationCreateInfo.
891
For more information see [Choosing memory type](@ref choosing_memory_type).
892
893
Although the library provides convenience functions that create Vulkan buffer or image,
894
allocate memory for it and bind them together,
895
binding of the allocation to a buffer or an image is out of scope of the allocation itself.
896
Allocation object can exist without buffer/image bound,
897
binding can be done manually by the user, and destruction of it can be done
898
independently of destruction of the allocation.
899
900
The object also remembers its size and some other information.
901
To retrieve this information, use function vmaGetAllocationInfo() and inspect
902
returned structure VmaAllocationInfo.
903
*/
904
VK_DEFINE_HANDLE(VmaAllocation)
905
906
/** \struct VmaDefragmentationContext
907
\brief An opaque object that represents started defragmentation process.
908
909
Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it.
910
Call function vmaEndDefragmentation() to destroy it.
911
*/
912
VK_DEFINE_HANDLE(VmaDefragmentationContext)
913
914
/** @} */
915
916
/**
917
\addtogroup group_virtual
918
@{
919
*/
920
921
/** \struct VmaVirtualAllocation
922
\brief Represents single memory allocation done inside VmaVirtualBlock.
923
924
Use it as a unique identifier to virtual allocation within the single block.
925
926
Use value `VK_NULL_HANDLE` to represent a null/invalid allocation.
927
*/
928
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation)
929
930
/** @} */
931
932
/**
933
\addtogroup group_virtual
934
@{
935
*/
936
937
/** \struct VmaVirtualBlock
938
\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.
939
940
Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.
941
For more information, see documentation chapter \ref virtual_allocator.
942
943
This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally.
944
*/
945
VK_DEFINE_HANDLE(VmaVirtualBlock)
946
947
/** @} */
948
949
/**
950
\addtogroup group_init
951
@{
952
*/
953
954
/// Callback function called after successful vkAllocateMemory.
955
typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(
956
VmaAllocator VMA_NOT_NULL allocator,
957
uint32_t memoryType,
958
VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
959
VkDeviceSize size,
960
void* VMA_NULLABLE pUserData);
961
962
/// Callback function called before vkFreeMemory.
963
typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(
964
VmaAllocator VMA_NOT_NULL allocator,
965
uint32_t memoryType,
966
VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
967
VkDeviceSize size,
968
void* VMA_NULLABLE pUserData);
969
970
/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
971
972
Provided for informative purpose, e.g. to gather statistics about number of
973
allocations or total amount of memory allocated in Vulkan.
974
975
Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
976
*/
977
typedef struct VmaDeviceMemoryCallbacks
978
{
979
/// Optional, can be null.
980
PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
981
/// Optional, can be null.
982
PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
983
/// Optional, can be null.
984
void* VMA_NULLABLE pUserData;
985
} VmaDeviceMemoryCallbacks;
986
987
/** \brief Pointers to some Vulkan functions - a subset used by the library.
988
989
Used in VmaAllocatorCreateInfo::pVulkanFunctions.
990
*/
991
typedef struct VmaVulkanFunctions
992
{
993
/// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
994
PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;
995
/// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
996
PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;
997
PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
998
PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
999
PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
1000
PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
1001
PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
1002
PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
1003
PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
1004
PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
1005
PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
1006
PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
1007
PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
1008
PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
1009
PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
1010
PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
1011
PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
1012
PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
1013
PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
1014
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
1015
/// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
1016
PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
1017
/// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
1018
PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
1019
#endif
1020
#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
1021
/// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.
1022
PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
1023
/// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.
1024
PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
1025
#endif
1026
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
1027
/// Fetch from "vkGetPhysicalDeviceMemoryProperties2" on Vulkan >= 1.1, but you can also fetch it from "vkGetPhysicalDeviceMemoryProperties2KHR" if you enabled extension VK_KHR_get_physical_device_properties2.
1028
PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
1029
#endif
1030
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
1031
/// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
1032
PFN_vkGetDeviceBufferMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceBufferMemoryRequirements;
1033
/// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
1034
PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
1035
#endif
1036
} VmaVulkanFunctions;
1037
1038
/// Description of a Allocator to be created.
1039
typedef struct VmaAllocatorCreateInfo
1040
{
1041
/// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1042
VmaAllocatorCreateFlags flags;
1043
/// Vulkan physical device.
1044
/** It must be valid throughout whole lifetime of created allocator. */
1045
VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1046
/// Vulkan device.
1047
/** It must be valid throughout whole lifetime of created allocator. */
1048
VkDevice VMA_NOT_NULL device;
1049
/// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1050
/** Set to 0 to use default, which is currently 256 MiB. */
1051
VkDeviceSize preferredLargeHeapBlockSize;
1052
/// Custom CPU memory allocation callbacks. Optional.
1053
/** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1054
const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1055
/// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1056
/** Optional, can be null. */
1057
const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
1058
/** \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.
1059
1060
If not NULL, it must be a pointer to an array of
1061
`VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1062
maximum number of bytes that can be allocated out of particular Vulkan memory
1063
heap.
1064
1065
Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1066
heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1067
1068
If there is a limit defined for a heap:
1069
1070
- If user tries to allocate more memory from that heap using this allocator,
1071
the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1072
- If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1073
value of this limit will be reported instead when using vmaGetMemoryProperties().
1074
1075
Warning! Using this feature may not be equivalent to installing a GPU with
1076
smaller amount of memory, because graphics driver doesn't necessary fail new
1077
allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1078
exceeded. It may return success and just silently migrate some device memory
1079
blocks to system RAM. This driver behavior can also be controlled using
1080
VK_AMD_memory_overallocation_behavior extension.
1081
*/
1082
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
1083
1084
/** \brief Pointers to Vulkan functions. Can be null.
1085
1086
For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
1087
*/
1088
const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
1089
/** \brief Handle to Vulkan instance object.
1090
1091
Starting from version 3.0.0 this member is no longer optional, it must be set!
1092
*/
1093
VkInstance VMA_NOT_NULL instance;
1094
/** \brief Optional. Vulkan version that the application uses.
1095
1096
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`.
1097
The patch version number specified is ignored. Only the major and minor versions are considered.
1098
Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation.
1099
Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
1100
It must match the Vulkan version used by the application and supported on the selected physical device,
1101
so it must be no higher than `VkApplicationInfo::apiVersion` passed to `vkCreateInstance`
1102
and no higher than `VkPhysicalDeviceProperties::apiVersion` found on the physical device used.
1103
*/
1104
uint32_t vulkanApiVersion;
1105
#if VMA_EXTERNAL_MEMORY
1106
/** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.
1107
1108
If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`
1109
elements, defining external memory handle types of particular Vulkan memory type,
1110
to be passed using `VkExportMemoryAllocateInfoKHR`.
1111
1112
Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.
1113
This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.
1114
*/
1115
const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;
1116
#endif // #if VMA_EXTERNAL_MEMORY
1117
} VmaAllocatorCreateInfo;
1118
1119
/// Information about existing #VmaAllocator object.
1120
typedef struct VmaAllocatorInfo
1121
{
1122
/** \brief Handle to Vulkan instance object.
1123
1124
This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
1125
*/
1126
VkInstance VMA_NOT_NULL instance;
1127
/** \brief Handle to Vulkan physical device object.
1128
1129
This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
1130
*/
1131
VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1132
/** \brief Handle to Vulkan device object.
1133
1134
This is the same value as has been passed through VmaAllocatorCreateInfo::device.
1135
*/
1136
VkDevice VMA_NOT_NULL device;
1137
} VmaAllocatorInfo;
1138
1139
/** @} */
1140
1141
/**
1142
\addtogroup group_stats
1143
@{
1144
*/
1145
1146
/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total.
1147
1148
These are fast to calculate.
1149
See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics().
1150
*/
1151
typedef struct VmaStatistics
1152
{
1153
/** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated.
1154
*/
1155
uint32_t blockCount;
1156
/** \brief Number of #VmaAllocation objects allocated.
1157
1158
Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`.
1159
*/
1160
uint32_t allocationCount;
1161
/** \brief Number of bytes allocated in `VkDeviceMemory` blocks.
1162
1163
\note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object
1164
(e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls
1165
"allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image.
1166
*/
1167
VkDeviceSize blockBytes;
1168
/** \brief Total number of bytes occupied by all #VmaAllocation objects.
1169
1170
Always less or equal than `blockBytes`.
1171
Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan
1172
but unused by any #VmaAllocation.
1173
*/
1174
VkDeviceSize allocationBytes;
1175
} VmaStatistics;
1176
1177
/** \brief More detailed statistics than #VmaStatistics.
1178
1179
These are slower to calculate. Use for debugging purposes.
1180
See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics().
1181
1182
Previous version of the statistics API provided averages, but they have been removed
1183
because they can be easily calculated as:
1184
1185
\code
1186
VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount;
1187
VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes;
1188
VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount;
1189
\endcode
1190
*/
1191
typedef struct VmaDetailedStatistics
1192
{
1193
/// Basic statistics.
1194
VmaStatistics statistics;
1195
/// Number of free ranges of memory between allocations.
1196
uint32_t unusedRangeCount;
1197
/// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations.
1198
VkDeviceSize allocationSizeMin;
1199
/// Largest allocation size. 0 if there are 0 allocations.
1200
VkDeviceSize allocationSizeMax;
1201
/// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges.
1202
VkDeviceSize unusedRangeSizeMin;
1203
/// Largest empty range size. 0 if there are 0 empty ranges.
1204
VkDeviceSize unusedRangeSizeMax;
1205
} VmaDetailedStatistics;
1206
1207
/** \brief General statistics from current state of the Allocator -
1208
total memory usage across all memory heaps and types.
1209
1210
These are slower to calculate. Use for debugging purposes.
1211
See function vmaCalculateStatistics().
1212
*/
1213
typedef struct VmaTotalStatistics
1214
{
1215
VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES];
1216
VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS];
1217
VmaDetailedStatistics total;
1218
} VmaTotalStatistics;
1219
1220
/** \brief Statistics of current memory usage and available budget for a specific memory heap.
1221
1222
These are fast to calculate.
1223
See function vmaGetHeapBudgets().
1224
*/
1225
typedef struct VmaBudget
1226
{
1227
/** \brief Statistics fetched from the library.
1228
*/
1229
VmaStatistics statistics;
1230
/** \brief Estimated current memory usage of the program, in bytes.
1231
1232
Fetched from system using VK_EXT_memory_budget extension if enabled.
1233
1234
It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects
1235
also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
1236
`VkDeviceMemory` blocks allocated outside of this library, if any.
1237
*/
1238
VkDeviceSize usage;
1239
/** \brief Estimated amount of memory available to the program, in bytes.
1240
1241
Fetched from system using VK_EXT_memory_budget extension if enabled.
1242
1243
It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
1244
external to the program, decided by the operating system.
1245
Difference `budget - usage` is the amount of additional memory that can probably
1246
be allocated without problems. Exceeding the budget may result in various problems.
1247
*/
1248
VkDeviceSize budget;
1249
} VmaBudget;
1250
1251
/** @} */
1252
1253
/**
1254
\addtogroup group_alloc
1255
@{
1256
*/
1257
1258
/** \brief Parameters of new #VmaAllocation.
1259
1260
To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others.
1261
*/
1262
typedef struct VmaAllocationCreateInfo
1263
{
1264
/// Use #VmaAllocationCreateFlagBits enum.
1265
VmaAllocationCreateFlags flags;
1266
/** \brief Intended usage of memory.
1267
1268
You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1269
If `pool` is not null, this member is ignored.
1270
*/
1271
VmaMemoryUsage usage;
1272
/** \brief Flags that must be set in a Memory Type chosen for an allocation.
1273
1274
Leave 0 if you specify memory requirements in other way. \n
1275
If `pool` is not null, this member is ignored.*/
1276
VkMemoryPropertyFlags requiredFlags;
1277
/** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1278
1279
Set to 0 if no additional flags are preferred. \n
1280
If `pool` is not null, this member is ignored. */
1281
VkMemoryPropertyFlags preferredFlags;
1282
/** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1283
1284
Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1285
it meets other requirements specified by this structure, with no further
1286
restrictions on memory type index. \n
1287
If `pool` is not null, this member is ignored.
1288
*/
1289
uint32_t memoryTypeBits;
1290
/** \brief Pool that this allocation should be created in.
1291
1292
Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1293
`usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1294
*/
1295
VmaPool VMA_NULLABLE pool;
1296
/** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1297
1298
If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1299
null or pointer to a null-terminated string. The string will be then copied to
1300
internal buffer, so it doesn't need to be valid after allocation call.
1301
*/
1302
void* VMA_NULLABLE pUserData;
1303
/** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
1304
1305
It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object
1306
and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1307
Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.
1308
*/
1309
float priority;
1310
} VmaAllocationCreateInfo;
1311
1312
/// Describes parameter of created #VmaPool.
1313
typedef struct VmaPoolCreateInfo
1314
{
1315
/** \brief Vulkan memory type index to allocate this pool from.
1316
*/
1317
uint32_t memoryTypeIndex;
1318
/** \brief Use combination of #VmaPoolCreateFlagBits.
1319
*/
1320
VmaPoolCreateFlags flags;
1321
/** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
1322
1323
Specify nonzero to set explicit, constant size of memory blocks used by this
1324
pool.
1325
1326
Leave 0 to use default and let the library manage block sizes automatically.
1327
Sizes of particular blocks may vary.
1328
In this case, the pool will also support dedicated allocations.
1329
*/
1330
VkDeviceSize blockSize;
1331
/** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1332
1333
Set to 0 to have no preallocated blocks and allow the pool be completely empty.
1334
*/
1335
size_t minBlockCount;
1336
/** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1337
1338
Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
1339
1340
Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
1341
throughout whole lifetime of this pool.
1342
*/
1343
size_t maxBlockCount;
1344
/** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.
1345
1346
It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.
1347
Otherwise, this variable is ignored.
1348
*/
1349
float priority;
1350
/** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
1351
1352
Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
1353
It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,
1354
e.g. when doing interop with OpenGL.
1355
*/
1356
VkDeviceSize minAllocationAlignment;
1357
/** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.
1358
1359
Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.
1360
It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.
1361
Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.
1362
1363
Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,
1364
can be attached automatically by this library when using other, more convenient of its features.
1365
*/
1366
void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkMemoryAllocateInfo) pMemoryAllocateNext;
1367
} VmaPoolCreateInfo;
1368
1369
/** @} */
1370
1371
/**
1372
\addtogroup group_alloc
1373
@{
1374
*/
1375
1376
/**
1377
Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1378
1379
There is also an extended version of this structure that carries additional parameters: #VmaAllocationInfo2.
1380
*/
1381
typedef struct VmaAllocationInfo
1382
{
1383
/** \brief Memory type index that this allocation was allocated from.
1384
1385
It never changes.
1386
*/
1387
uint32_t memoryType;
1388
/** \brief Handle to Vulkan memory object.
1389
1390
Same memory object can be shared by multiple allocations.
1391
1392
It can change after the allocation is moved during \ref defragmentation.
1393
*/
1394
VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
1395
/** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.
1396
1397
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
1398
vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,
1399
not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation
1400
and apply this offset automatically.
1401
1402
It can change after the allocation is moved during \ref defragmentation.
1403
*/
1404
VkDeviceSize offset;
1405
/** \brief Size of this allocation, in bytes.
1406
1407
It never changes.
1408
1409
\note Allocation size returned in this variable may be greater than the size
1410
requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the
1411
allocation is accessible for operations on memory e.g. using a pointer after
1412
mapping with vmaMapMemory(), but operations on the resource e.g. using
1413
`vkCmdCopyBuffer` must be limited to the size of the resource.
1414
*/
1415
VkDeviceSize size;
1416
/** \brief Pointer to the beginning of this allocation as mapped data.
1417
1418
If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1419
created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
1420
1421
It can change after call to vmaMapMemory(), vmaUnmapMemory().
1422
It can also change after the allocation is moved during \ref defragmentation.
1423
*/
1424
void* VMA_NULLABLE pMappedData;
1425
/** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1426
1427
It can change after call to vmaSetAllocationUserData() for this allocation.
1428
*/
1429
void* VMA_NULLABLE pUserData;
1430
/** \brief Custom allocation name that was set with vmaSetAllocationName().
1431
1432
It can change after call to vmaSetAllocationName() for this allocation.
1433
1434
Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with
1435
additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED].
1436
*/
1437
const char* VMA_NULLABLE pName;
1438
} VmaAllocationInfo;
1439
1440
/// Extended parameters of a #VmaAllocation object that can be retrieved using function vmaGetAllocationInfo2().
1441
typedef struct VmaAllocationInfo2
1442
{
1443
/** \brief Basic parameters of the allocation.
1444
1445
If you need only these, you can use function vmaGetAllocationInfo() and structure #VmaAllocationInfo instead.
1446
*/
1447
VmaAllocationInfo allocationInfo;
1448
/** \brief Size of the `VkDeviceMemory` block that the allocation belongs to.
1449
1450
In case of an allocation with dedicated memory, it will be equal to `allocationInfo.size`.
1451
*/
1452
VkDeviceSize blockSize;
1453
/** \brief `VK_TRUE` if the allocation has dedicated memory, `VK_FALSE` if it was placed as part of a larger memory block.
1454
1455
When `VK_TRUE`, it also means `VkMemoryDedicatedAllocateInfo` was used when creating the allocation
1456
(if VK_KHR_dedicated_allocation extension or Vulkan version >= 1.1 is enabled).
1457
*/
1458
VkBool32 dedicatedMemory;
1459
} VmaAllocationInfo2;
1460
1461
/** Callback function called during vmaBeginDefragmentation() to check custom criterion about ending current defragmentation pass.
1462
1463
Should return true if the defragmentation needs to stop current pass.
1464
*/
1465
typedef VkBool32 (VKAPI_PTR* PFN_vmaCheckDefragmentationBreakFunction)(void* VMA_NULLABLE pUserData);
1466
1467
/** \brief Parameters for defragmentation.
1468
1469
To be used with function vmaBeginDefragmentation().
1470
*/
1471
typedef struct VmaDefragmentationInfo
1472
{
1473
/// \brief Use combination of #VmaDefragmentationFlagBits.
1474
VmaDefragmentationFlags flags;
1475
/** \brief Custom pool to be defragmented.
1476
1477
If null then default pools will undergo defragmentation process.
1478
*/
1479
VmaPool VMA_NULLABLE pool;
1480
/** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places.
1481
1482
`0` means no limit.
1483
*/
1484
VkDeviceSize maxBytesPerPass;
1485
/** \brief Maximum number of allocations that can be moved during single pass to a different place.
1486
1487
`0` means no limit.
1488
*/
1489
uint32_t maxAllocationsPerPass;
1490
/** \brief Optional custom callback for stopping vmaBeginDefragmentation().
1491
1492
Have to return true for breaking current defragmentation pass.
1493
*/
1494
PFN_vmaCheckDefragmentationBreakFunction VMA_NULLABLE pfnBreakCallback;
1495
/// \brief Optional data to pass to custom callback for stopping pass of defragmentation.
1496
void* VMA_NULLABLE pBreakCallbackUserData;
1497
} VmaDefragmentationInfo;
1498
1499
/// Single move of an allocation to be done for defragmentation.
1500
typedef struct VmaDefragmentationMove
1501
{
1502
/// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it.
1503
VmaDefragmentationMoveOperation operation;
1504
/// Allocation that should be moved.
1505
VmaAllocation VMA_NOT_NULL srcAllocation;
1506
/** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`.
1507
1508
\warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass,
1509
to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory().
1510
vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory.
1511
*/
1512
VmaAllocation VMA_NOT_NULL dstTmpAllocation;
1513
} VmaDefragmentationMove;
1514
1515
/** \brief Parameters for incremental defragmentation steps.
1516
1517
To be used with function vmaBeginDefragmentationPass().
1518
*/
1519
typedef struct VmaDefragmentationPassMoveInfo
1520
{
1521
/// Number of elements in the `pMoves` array.
1522
uint32_t moveCount;
1523
/** \brief Array of moves to be performed by the user in the current defragmentation pass.
1524
1525
Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass().
1526
1527
For each element, you should:
1528
1529
1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset.
1530
2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`.
1531
3. Make sure these commands finished executing on the GPU.
1532
4. Destroy the old buffer/image.
1533
1534
Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass().
1535
After this call, the allocation will point to the new place in memory.
1536
1537
Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
1538
1539
Alternatively, if you decide you want to completely remove the allocation:
1540
1541
1. Destroy its buffer/image.
1542
2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
1543
1544
Then, after vmaEndDefragmentationPass() the allocation will be freed.
1545
*/
1546
VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
1547
} VmaDefragmentationPassMoveInfo;
1548
1549
/// Statistics returned for defragmentation process in function vmaEndDefragmentation().
1550
typedef struct VmaDefragmentationStats
1551
{
1552
/// Total number of bytes that have been copied while moving allocations to different places.
1553
VkDeviceSize bytesMoved;
1554
/// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1555
VkDeviceSize bytesFreed;
1556
/// Number of allocations that have been moved to different places.
1557
uint32_t allocationsMoved;
1558
/// Number of empty `VkDeviceMemory` objects that have been released to the system.
1559
uint32_t deviceMemoryBlocksFreed;
1560
} VmaDefragmentationStats;
1561
1562
/** @} */
1563
1564
/**
1565
\addtogroup group_virtual
1566
@{
1567
*/
1568
1569
/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().
1570
typedef struct VmaVirtualBlockCreateInfo
1571
{
1572
/** \brief Total size of the virtual block.
1573
1574
Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
1575
For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
1576
*/
1577
VkDeviceSize size;
1578
1579
/** \brief Use combination of #VmaVirtualBlockCreateFlagBits.
1580
*/
1581
VmaVirtualBlockCreateFlags flags;
1582
1583
/** \brief Custom CPU memory allocation callbacks. Optional.
1584
1585
Optional, can be null. When specified, they will be used for all CPU-side memory allocations.
1586
*/
1587
const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1588
} VmaVirtualBlockCreateInfo;
1589
1590
/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().
1591
typedef struct VmaVirtualAllocationCreateInfo
1592
{
1593
/** \brief Size of the allocation.
1594
1595
Cannot be zero.
1596
*/
1597
VkDeviceSize size;
1598
/** \brief Required alignment of the allocation. Optional.
1599
1600
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.
1601
*/
1602
VkDeviceSize alignment;
1603
/** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.
1604
*/
1605
VmaVirtualAllocationCreateFlags flags;
1606
/** \brief Custom pointer to be associated with the allocation. Optional.
1607
1608
It can be any value and can be used for user-defined purposes. It can be fetched or changed later.
1609
*/
1610
void* VMA_NULLABLE pUserData;
1611
} VmaVirtualAllocationCreateInfo;
1612
1613
/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().
1614
typedef struct VmaVirtualAllocationInfo
1615
{
1616
/** \brief Offset of the allocation.
1617
1618
Offset at which the allocation was made.
1619
*/
1620
VkDeviceSize offset;
1621
/** \brief Size of the allocation.
1622
1623
Same value as passed in VmaVirtualAllocationCreateInfo::size.
1624
*/
1625
VkDeviceSize size;
1626
/** \brief Custom pointer associated with the allocation.
1627
1628
Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().
1629
*/
1630
void* VMA_NULLABLE pUserData;
1631
} VmaVirtualAllocationInfo;
1632
1633
/** @} */
1634
1635
#endif // _VMA_DATA_TYPES_DECLARATIONS
1636
1637
#ifndef _VMA_FUNCTION_HEADERS
1638
1639
/**
1640
\addtogroup group_init
1641
@{
1642
*/
1643
1644
/// Creates #VmaAllocator object.
1645
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
1646
const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
1647
VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);
1648
1649
/// Destroys allocator object.
1650
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
1651
VmaAllocator VMA_NULLABLE allocator);
1652
1653
/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
1654
1655
It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
1656
`VkPhysicalDevice`, `VkDevice` etc. every time using this function.
1657
*/
1658
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(
1659
VmaAllocator VMA_NOT_NULL allocator,
1660
VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
1661
1662
/**
1663
PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1664
You can access it here, without fetching it again on your own.
1665
*/
1666
VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
1667
VmaAllocator VMA_NOT_NULL allocator,
1668
const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);
1669
1670
/**
1671
PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1672
You can access it here, without fetching it again on your own.
1673
*/
1674
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
1675
VmaAllocator VMA_NOT_NULL allocator,
1676
const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
1677
1678
/**
1679
\brief Given Memory Type Index, returns Property Flags of this memory type.
1680
1681
This is just a convenience function. Same information can be obtained using
1682
vmaGetMemoryProperties().
1683
*/
1684
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
1685
VmaAllocator VMA_NOT_NULL allocator,
1686
uint32_t memoryTypeIndex,
1687
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
1688
1689
/** \brief Sets index of the current frame.
1690
*/
1691
VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
1692
VmaAllocator VMA_NOT_NULL allocator,
1693
uint32_t frameIndex);
1694
1695
/** @} */
1696
1697
/**
1698
\addtogroup group_stats
1699
@{
1700
*/
1701
1702
/** \brief Retrieves statistics from current state of the Allocator.
1703
1704
This function is called "calculate" not "get" because it has to traverse all
1705
internal data structures, so it may be quite slow. Use it for debugging purposes.
1706
For faster but more brief statistics suitable to be called every frame or every allocation,
1707
use vmaGetHeapBudgets().
1708
1709
Note that when using allocator from multiple threads, returned information may immediately
1710
become outdated.
1711
*/
1712
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
1713
VmaAllocator VMA_NOT_NULL allocator,
1714
VmaTotalStatistics* VMA_NOT_NULL pStats);
1715
1716
/** \brief Retrieves lazily allocated bytes
1717
1718
This function is called "calculate" not "get" because it has to traverse all
1719
internal data structures, so it may be quite slow. Use it for debugging purposes.
1720
For faster but more brief statistics suitable to be called every frame or every allocation,
1721
use vmaGetHeapBudgets().
1722
1723
Note that when using allocator from multiple threads, returned information may immediately
1724
become outdated.
1725
*/
1726
VMA_CALL_PRE uint64_t VMA_CALL_POST vmaCalculateLazilyAllocatedBytes(
1727
VmaAllocator VMA_NOT_NULL allocator);
1728
1729
/** \brief Retrieves information about current memory usage and budget for all memory heaps.
1730
1731
\param allocator
1732
\param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used.
1733
1734
This function is called "get" not "calculate" because it is very fast, suitable to be called
1735
every frame or every allocation. For more detailed statistics use vmaCalculateStatistics().
1736
1737
Note that when using allocator from multiple threads, returned information may immediately
1738
become outdated.
1739
*/
1740
VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
1741
VmaAllocator VMA_NOT_NULL allocator,
1742
VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets);
1743
1744
/** @} */
1745
1746
/**
1747
\addtogroup group_alloc
1748
@{
1749
*/
1750
1751
/**
1752
\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1753
1754
This algorithm tries to find a memory type that:
1755
1756
- Is allowed by memoryTypeBits.
1757
- Contains all the flags from pAllocationCreateInfo->requiredFlags.
1758
- Matches intended usage.
1759
- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1760
1761
\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1762
from this function or any other allocating function probably means that your
1763
device doesn't support any memory type with requested features for the specific
1764
type of resource you want to use it for. Please check parameters of your
1765
resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1766
*/
1767
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1768
VmaAllocator VMA_NOT_NULL allocator,
1769
uint32_t memoryTypeBits,
1770
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1771
uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1772
1773
/**
1774
\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1775
1776
It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1777
It internally creates a temporary, dummy buffer that never has memory bound.
1778
*/
1779
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
1780
VmaAllocator VMA_NOT_NULL allocator,
1781
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
1782
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1783
uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1784
1785
/**
1786
\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1787
1788
It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1789
It internally creates a temporary, dummy image that never has memory bound.
1790
*/
1791
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
1792
VmaAllocator VMA_NOT_NULL allocator,
1793
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
1794
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1795
uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1796
1797
/** \brief Allocates Vulkan device memory and creates #VmaPool object.
1798
1799
\param allocator Allocator object.
1800
\param pCreateInfo Parameters of pool to create.
1801
\param[out] pPool Handle to created pool.
1802
*/
1803
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
1804
VmaAllocator VMA_NOT_NULL allocator,
1805
const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
1806
VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);
1807
1808
/** \brief Destroys #VmaPool object and frees Vulkan device memory.
1809
*/
1810
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
1811
VmaAllocator VMA_NOT_NULL allocator,
1812
VmaPool VMA_NULLABLE pool);
1813
1814
/** @} */
1815
1816
/**
1817
\addtogroup group_stats
1818
@{
1819
*/
1820
1821
/** \brief Retrieves statistics of existing #VmaPool object.
1822
1823
\param allocator Allocator object.
1824
\param pool Pool object.
1825
\param[out] pPoolStats Statistics of specified pool.
1826
*/
1827
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
1828
VmaAllocator VMA_NOT_NULL allocator,
1829
VmaPool VMA_NOT_NULL pool,
1830
VmaStatistics* VMA_NOT_NULL pPoolStats);
1831
1832
/** \brief Retrieves detailed statistics of existing #VmaPool object.
1833
1834
\param allocator Allocator object.
1835
\param pool Pool object.
1836
\param[out] pPoolStats Statistics of specified pool.
1837
*/
1838
VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
1839
VmaAllocator VMA_NOT_NULL allocator,
1840
VmaPool VMA_NOT_NULL pool,
1841
VmaDetailedStatistics* VMA_NOT_NULL pPoolStats);
1842
1843
/** @} */
1844
1845
/**
1846
\addtogroup group_alloc
1847
@{
1848
*/
1849
1850
/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
1851
1852
Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1853
`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
1854
`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1855
1856
Possible return values:
1857
1858
- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
1859
- `VK_SUCCESS` - corruption detection has been performed and succeeded.
1860
- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1861
`VMA_ASSERT` is also fired in that case.
1862
- Other value: Error returned by Vulkan, e.g. memory mapping failure.
1863
*/
1864
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(
1865
VmaAllocator VMA_NOT_NULL allocator,
1866
VmaPool VMA_NOT_NULL pool);
1867
1868
/** \brief Retrieves name of a custom pool.
1869
1870
After the call `ppName` is either null or points to an internally-owned null-terminated string
1871
containing name of the pool that was previously set. The pointer becomes invalid when the pool is
1872
destroyed or its name is changed using vmaSetPoolName().
1873
*/
1874
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
1875
VmaAllocator VMA_NOT_NULL allocator,
1876
VmaPool VMA_NOT_NULL pool,
1877
const char* VMA_NULLABLE* VMA_NOT_NULL ppName);
1878
1879
/** \brief Sets name of a custom pool.
1880
1881
`pName` can be either null or pointer to a null-terminated string with new name for the pool.
1882
Function makes internal copy of the string, so it can be changed or freed immediately after this call.
1883
*/
1884
VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
1885
VmaAllocator VMA_NOT_NULL allocator,
1886
VmaPool VMA_NOT_NULL pool,
1887
const char* VMA_NULLABLE pName);
1888
1889
/** \brief General purpose memory allocation.
1890
1891
\param allocator
1892
\param pVkMemoryRequirements
1893
\param pCreateInfo
1894
\param[out] pAllocation Handle to allocated memory.
1895
\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1896
1897
You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1898
1899
It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1900
vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1901
*/
1902
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
1903
VmaAllocator VMA_NOT_NULL allocator,
1904
const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
1905
const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1906
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1907
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1908
1909
/** \brief General purpose memory allocation for multiple allocation objects at once.
1910
1911
\param allocator Allocator object.
1912
\param pVkMemoryRequirements Memory requirements for each allocation.
1913
\param pCreateInfo Creation parameters for each allocation.
1914
\param allocationCount Number of allocations to make.
1915
\param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
1916
\param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
1917
1918
You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1919
1920
Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
1921
It is just a general purpose allocation function able to make multiple allocations at once.
1922
It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
1923
1924
All allocations are made using same parameters. All of them are created out of the same memory pool and type.
1925
If any allocation fails, all allocations already made within this function call are also freed, so that when
1926
returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
1927
*/
1928
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
1929
VmaAllocator VMA_NOT_NULL allocator,
1930
const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
1931
const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
1932
size_t allocationCount,
1933
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1934
VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
1935
1936
/** \brief Allocates memory suitable for given `VkBuffer`.
1937
1938
\param allocator
1939
\param buffer
1940
\param pCreateInfo
1941
\param[out] pAllocation Handle to allocated memory.
1942
\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1943
1944
It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory().
1945
1946
This is a special-purpose function. In most cases you should use vmaCreateBuffer().
1947
1948
You must free the allocation using vmaFreeMemory() when no longer needed.
1949
*/
1950
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
1951
VmaAllocator VMA_NOT_NULL allocator,
1952
VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
1953
const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1954
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1955
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1956
1957
/** \brief Allocates memory suitable for given `VkImage`.
1958
1959
\param allocator
1960
\param image
1961
\param pCreateInfo
1962
\param[out] pAllocation Handle to allocated memory.
1963
\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1964
1965
It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory().
1966
1967
This is a special-purpose function. In most cases you should use vmaCreateImage().
1968
1969
You must free the allocation using vmaFreeMemory() when no longer needed.
1970
*/
1971
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
1972
VmaAllocator VMA_NOT_NULL allocator,
1973
VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
1974
const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1975
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1976
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1977
1978
/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1979
1980
Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
1981
*/
1982
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
1983
VmaAllocator VMA_NOT_NULL allocator,
1984
const VmaAllocation VMA_NULLABLE allocation);
1985
1986
/** \brief Frees memory and destroys multiple allocations.
1987
1988
Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
1989
It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
1990
vmaAllocateMemoryPages() and other functions.
1991
It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
1992
1993
Allocations in `pAllocations` array can come from any memory pools and types.
1994
Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
1995
*/
1996
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
1997
VmaAllocator VMA_NOT_NULL allocator,
1998
size_t allocationCount,
1999
const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
2000
2001
/** \brief Returns current information about specified allocation.
2002
2003
Current parameters of given allocation are returned in `pAllocationInfo`.
2004
2005
Although this function doesn't lock any mutex, so it should be quite efficient,
2006
you should avoid calling it too often.
2007
You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2008
vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2009
(e.g. due to defragmentation).
2010
2011
There is also a new function vmaGetAllocationInfo2() that offers extended information
2012
about the allocation, returned using new structure #VmaAllocationInfo2.
2013
*/
2014
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
2015
VmaAllocator VMA_NOT_NULL allocator,
2016
VmaAllocation VMA_NOT_NULL allocation,
2017
VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
2018
2019
/** \brief Returns extended information about specified allocation.
2020
2021
Current parameters of given allocation are returned in `pAllocationInfo`.
2022
Extended parameters in structure #VmaAllocationInfo2 include memory block size
2023
and a flag telling whether the allocation has dedicated memory.
2024
It can be useful e.g. for interop with OpenGL.
2025
*/
2026
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2(
2027
VmaAllocator VMA_NOT_NULL allocator,
2028
VmaAllocation VMA_NOT_NULL allocation,
2029
VmaAllocationInfo2* VMA_NOT_NULL pAllocationInfo);
2030
2031
/** \brief Sets pUserData in given allocation to new value.
2032
2033
The value of pointer `pUserData` is copied to allocation's `pUserData`.
2034
It is opaque, so you can use it however you want - e.g.
2035
as a pointer, ordinal number or some handle to you own data.
2036
*/
2037
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
2038
VmaAllocator VMA_NOT_NULL allocator,
2039
VmaAllocation VMA_NOT_NULL allocation,
2040
void* VMA_NULLABLE pUserData);
2041
2042
/** \brief Sets pName in given allocation to new value.
2043
2044
`pName` must be either null, or pointer to a null-terminated string. The function
2045
makes local copy of the string and sets it as allocation's `pName`. String
2046
passed as pName doesn't need to be valid for whole lifetime of the allocation -
2047
you can free it after this call. String previously pointed by allocation's
2048
`pName` is freed from memory.
2049
*/
2050
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
2051
VmaAllocator VMA_NOT_NULL allocator,
2052
VmaAllocation VMA_NOT_NULL allocation,
2053
const char* VMA_NULLABLE pName);
2054
2055
/**
2056
\brief Given an allocation, returns Property Flags of its memory type.
2057
2058
This is just a convenience function. Same information can be obtained using
2059
vmaGetAllocationInfo() + vmaGetMemoryProperties().
2060
*/
2061
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
2062
VmaAllocator VMA_NOT_NULL allocator,
2063
VmaAllocation VMA_NOT_NULL allocation,
2064
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2065
2066
/** \brief Maps memory represented by given allocation and returns pointer to it.
2067
2068
Maps memory represented by given allocation to make it accessible to CPU code.
2069
When succeeded, `*ppData` contains pointer to first byte of this memory.
2070
2071
\warning
2072
If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is
2073
correctly offsetted to the beginning of region assigned to this particular allocation.
2074
Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block.
2075
You should not add VmaAllocationInfo::offset to it!
2076
2077
Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2078
function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2079
multiple times simultaneously, it is safe to call this function on allocations
2080
assigned to the same memory block. Actual Vulkan memory will be mapped on first
2081
mapping and unmapped on last unmapping.
2082
2083
If the function succeeded, you must call vmaUnmapMemory() to unmap the
2084
allocation when mapping is no longer needed or before freeing the allocation, at
2085
the latest.
2086
2087
It also safe to call this function multiple times on the same allocation. You
2088
must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2089
2090
It is also safe to call this function on allocation created with
2091
#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2092
You must still call vmaUnmapMemory() same number of times as you called
2093
vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2094
"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2095
2096
This function fails when used on allocation made in memory type that is not
2097
`HOST_VISIBLE`.
2098
2099
This function doesn't automatically flush or invalidate caches.
2100
If the allocation is made from a memory types that is not `HOST_COHERENT`,
2101
you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2102
*/
2103
VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
2104
VmaAllocator VMA_NOT_NULL allocator,
2105
VmaAllocation VMA_NOT_NULL allocation,
2106
void* VMA_NULLABLE* VMA_NOT_NULL ppData);
2107
2108
/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2109
2110
For details, see description of vmaMapMemory().
2111
2112
This function doesn't automatically flush or invalidate caches.
2113
If the allocation is made from a memory types that is not `HOST_COHERENT`,
2114
you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2115
*/
2116
VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
2117
VmaAllocator VMA_NOT_NULL allocator,
2118
VmaAllocation VMA_NOT_NULL allocation);
2119
2120
/** \brief Flushes memory of given allocation.
2121
2122
Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2123
It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
2124
Unmap operation doesn't do that automatically.
2125
2126
- `offset` must be relative to the beginning of allocation.
2127
- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2128
- `offset` and `size` don't have to be aligned.
2129
They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2130
- If `size` is 0, this call is ignored.
2131
- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2132
this call is ignored.
2133
2134
Warning! `offset` and `size` are relative to the contents of given `allocation`.
2135
If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2136
Do not pass allocation's offset as `offset`!!!
2137
2138
This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2139
called, otherwise `VK_SUCCESS`.
2140
*/
2141
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
2142
VmaAllocator VMA_NOT_NULL allocator,
2143
VmaAllocation VMA_NOT_NULL allocation,
2144
VkDeviceSize offset,
2145
VkDeviceSize size);
2146
2147
/** \brief Invalidates memory of given allocation.
2148
2149
Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2150
It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
2151
Map operation doesn't do that automatically.
2152
2153
- `offset` must be relative to the beginning of allocation.
2154
- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2155
- `offset` and `size` don't have to be aligned.
2156
They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2157
- If `size` is 0, this call is ignored.
2158
- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2159
this call is ignored.
2160
2161
Warning! `offset` and `size` are relative to the contents of given `allocation`.
2162
If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2163
Do not pass allocation's offset as `offset`!!!
2164
2165
This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
2166
it is called, otherwise `VK_SUCCESS`.
2167
*/
2168
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
2169
VmaAllocator VMA_NOT_NULL allocator,
2170
VmaAllocation VMA_NOT_NULL allocation,
2171
VkDeviceSize offset,
2172
VkDeviceSize size);
2173
2174
/** \brief Flushes memory of given set of allocations.
2175
2176
Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2177
For more information, see documentation of vmaFlushAllocation().
2178
2179
\param allocator
2180
\param allocationCount
2181
\param allocations
2182
\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 offsets are zero.
2183
\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.
2184
2185
This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2186
called, otherwise `VK_SUCCESS`.
2187
*/
2188
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
2189
VmaAllocator VMA_NOT_NULL allocator,
2190
uint32_t allocationCount,
2191
const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2192
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2193
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2194
2195
/** \brief Invalidates memory of given set of allocations.
2196
2197
Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2198
For more information, see documentation of vmaInvalidateAllocation().
2199
2200
\param allocator
2201
\param allocationCount
2202
\param allocations
2203
\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 offsets are zero.
2204
\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.
2205
2206
This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
2207
called, otherwise `VK_SUCCESS`.
2208
*/
2209
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
2210
VmaAllocator VMA_NOT_NULL allocator,
2211
uint32_t allocationCount,
2212
const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2213
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2214
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2215
2216
/** \brief Maps the allocation temporarily if needed, copies data from specified host pointer to it, and flushes the memory from the host caches if needed.
2217
2218
\param allocator
2219
\param pSrcHostPointer Pointer to the host data that become source of the copy.
2220
\param dstAllocation Handle to the allocation that becomes destination of the copy.
2221
\param dstAllocationLocalOffset Offset within `dstAllocation` where to write copied data, in bytes.
2222
\param size Number of bytes to copy.
2223
2224
This is a convenience function that allows to copy data from a host pointer to an allocation easily.
2225
Same behavior can be achieved by calling vmaMapMemory(), `memcpy()`, vmaUnmapMemory(), vmaFlushAllocation().
2226
2227
This function can be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
2228
It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or
2229
#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
2230
Otherwise, the function will fail and generate a Validation Layers error.
2231
2232
`dstAllocationLocalOffset` is relative to the contents of given `dstAllocation`.
2233
If you mean whole allocation, you should pass 0.
2234
Do not pass allocation's offset within device memory block this parameter!
2235
*/
2236
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(
2237
VmaAllocator VMA_NOT_NULL allocator,
2238
const void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pSrcHostPointer,
2239
VmaAllocation VMA_NOT_NULL dstAllocation,
2240
VkDeviceSize dstAllocationLocalOffset,
2241
VkDeviceSize size);
2242
2243
/** \brief Invalidates memory in the host caches if needed, maps the allocation temporarily if needed, and copies data from it to a specified host pointer.
2244
2245
\param allocator
2246
\param srcAllocation Handle to the allocation that becomes source of the copy.
2247
\param srcAllocationLocalOffset Offset within `srcAllocation` where to read copied data, in bytes.
2248
\param pDstHostPointer Pointer to the host memory that become destination of the copy.
2249
\param size Number of bytes to copy.
2250
2251
This is a convenience function that allows to copy data from an allocation to a host pointer easily.
2252
Same behavior can be achieved by calling vmaInvalidateAllocation(), vmaMapMemory(), `memcpy()`, vmaUnmapMemory().
2253
2254
This function should be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
2255
and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT` flag.
2256
It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
2257
Otherwise, the function may fail and generate a Validation Layers error.
2258
It may also work very slowly when reading from an uncached memory.
2259
2260
`srcAllocationLocalOffset` is relative to the contents of given `srcAllocation`.
2261
If you mean whole allocation, you should pass 0.
2262
Do not pass allocation's offset within device memory block as this parameter!
2263
*/
2264
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(
2265
VmaAllocator VMA_NOT_NULL allocator,
2266
VmaAllocation VMA_NOT_NULL srcAllocation,
2267
VkDeviceSize srcAllocationLocalOffset,
2268
void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pDstHostPointer,
2269
VkDeviceSize size);
2270
2271
/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2272
2273
\param allocator
2274
\param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2275
2276
Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2277
`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2278
`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2279
2280
Possible return values:
2281
2282
- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2283
- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2284
- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
2285
`VMA_ASSERT` is also fired in that case.
2286
- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2287
*/
2288
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
2289
VmaAllocator VMA_NOT_NULL allocator,
2290
uint32_t memoryTypeBits);
2291
2292
/** \brief Begins defragmentation process.
2293
2294
\param allocator Allocator object.
2295
\param pInfo Structure filled with parameters of defragmentation.
2296
\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation.
2297
\returns
2298
- `VK_SUCCESS` if defragmentation can begin.
2299
- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.
2300
2301
For more information about defragmentation, see documentation chapter:
2302
[Defragmentation](@ref defragmentation).
2303
*/
2304
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
2305
VmaAllocator VMA_NOT_NULL allocator,
2306
const VmaDefragmentationInfo* VMA_NOT_NULL pInfo,
2307
VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);
2308
2309
/** \brief Ends defragmentation process.
2310
2311
\param allocator Allocator object.
2312
\param context Context object that has been created by vmaBeginDefragmentation().
2313
\param[out] pStats Optional stats for the defragmentation. Can be null.
2314
2315
Use this function to finish defragmentation started by vmaBeginDefragmentation().
2316
*/
2317
VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
2318
VmaAllocator VMA_NOT_NULL allocator,
2319
VmaDefragmentationContext VMA_NOT_NULL context,
2320
VmaDefragmentationStats* VMA_NULLABLE pStats);
2321
2322
/** \brief Starts single defragmentation pass.
2323
2324
\param allocator Allocator object.
2325
\param context Context object that has been created by vmaBeginDefragmentation().
2326
\param[out] pPassInfo Computed information for current pass.
2327
\returns
2328
- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation.
2329
- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(),
2330
and then preferably try another pass with vmaBeginDefragmentationPass().
2331
*/
2332
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
2333
VmaAllocator VMA_NOT_NULL allocator,
2334
VmaDefragmentationContext VMA_NOT_NULL context,
2335
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2336
2337
/** \brief Ends single defragmentation pass.
2338
2339
\param allocator Allocator object.
2340
\param context Context object that has been created by vmaBeginDefragmentation().
2341
\param pPassInfo Computed information for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you.
2342
2343
Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible.
2344
2345
Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`.
2346
After this call:
2347
2348
- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY
2349
(which is the default) will be pointing to the new destination place.
2350
- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY
2351
will be freed.
2352
2353
If no more moves are possible you can end whole defragmentation.
2354
*/
2355
VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
2356
VmaAllocator VMA_NOT_NULL allocator,
2357
VmaDefragmentationContext VMA_NOT_NULL context,
2358
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2359
2360
/** \brief Binds buffer to allocation.
2361
2362
Binds specified buffer to region of memory represented by specified allocation.
2363
Gets `VkDeviceMemory` handle and offset from the allocation.
2364
If you want to create a buffer, allocate memory for it and bind them together separately,
2365
you should use this function for binding instead of standard `vkBindBufferMemory()`,
2366
because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2367
allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2368
(which is illegal in Vulkan).
2369
2370
It is recommended to use function vmaCreateBuffer() instead of this one.
2371
*/
2372
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
2373
VmaAllocator VMA_NOT_NULL allocator,
2374
VmaAllocation VMA_NOT_NULL allocation,
2375
VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
2376
2377
/** \brief Binds buffer to allocation with additional parameters.
2378
2379
\param allocator
2380
\param allocation
2381
\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2382
\param buffer
2383
\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
2384
2385
This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
2386
2387
If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2388
or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2389
*/
2390
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
2391
VmaAllocator VMA_NOT_NULL allocator,
2392
VmaAllocation VMA_NOT_NULL allocation,
2393
VkDeviceSize allocationLocalOffset,
2394
VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
2395
const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindBufferMemoryInfoKHR) pNext);
2396
2397
/** \brief Binds image to allocation.
2398
2399
Binds specified image to region of memory represented by specified allocation.
2400
Gets `VkDeviceMemory` handle and offset from the allocation.
2401
If you want to create an image, allocate memory for it and bind them together separately,
2402
you should use this function for binding instead of standard `vkBindImageMemory()`,
2403
because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2404
allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2405
(which is illegal in Vulkan).
2406
2407
It is recommended to use function vmaCreateImage() instead of this one.
2408
*/
2409
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
2410
VmaAllocator VMA_NOT_NULL allocator,
2411
VmaAllocation VMA_NOT_NULL allocation,
2412
VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
2413
2414
/** \brief Binds image to allocation with additional parameters.
2415
2416
\param allocator
2417
\param allocation
2418
\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2419
\param image
2420
\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
2421
2422
This function is similar to vmaBindImageMemory(), but it provides additional parameters.
2423
2424
If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2425
or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2426
*/
2427
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
2428
VmaAllocator VMA_NOT_NULL allocator,
2429
VmaAllocation VMA_NOT_NULL allocation,
2430
VkDeviceSize allocationLocalOffset,
2431
VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
2432
const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindImageMemoryInfoKHR) pNext);
2433
2434
/** \brief Creates a new `VkBuffer`, allocates and binds memory for it.
2435
2436
\param allocator
2437
\param pBufferCreateInfo
2438
\param pAllocationCreateInfo
2439
\param[out] pBuffer Buffer that was created.
2440
\param[out] pAllocation Allocation that was created.
2441
\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2442
2443
This function automatically:
2444
2445
-# Creates buffer.
2446
-# Allocates appropriate memory for it.
2447
-# Binds the buffer with the memory.
2448
2449
If any of these operations fail, buffer and allocation are not created,
2450
returned value is negative error code, `*pBuffer` and `*pAllocation` are null.
2451
2452
If the function succeeded, you must destroy both buffer and allocation when you
2453
no longer need them using either convenience function vmaDestroyBuffer() or
2454
separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2455
2456
If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2457
VK_KHR_dedicated_allocation extension is used internally to query driver whether
2458
it requires or prefers the new buffer to have dedicated allocation. If yes,
2459
and if dedicated allocation is possible
2460
(#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2461
allocation for this buffer, just like when using
2462
#VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2463
2464
\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,
2465
although recommended as a good practice, is out of scope of this library and could be implemented
2466
by the user as a higher-level logic on top of VMA.
2467
*/
2468
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
2469
VmaAllocator VMA_NOT_NULL allocator,
2470
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2471
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2472
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2473
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2474
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2475
2476
/** \brief Creates a buffer with additional minimum alignment.
2477
2478
Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,
2479
minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.
2480
for interop with OpenGL.
2481
*/
2482
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
2483
VmaAllocator VMA_NOT_NULL allocator,
2484
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2485
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2486
VkDeviceSize minAlignment,
2487
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2488
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2489
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2490
2491
/** \brief Creates a new `VkBuffer`, binds already created memory for it.
2492
2493
\param allocator
2494
\param allocation Allocation that provides memory to be used for binding new buffer to it.
2495
\param pBufferCreateInfo
2496
\param[out] pBuffer Buffer that was created.
2497
2498
This function automatically:
2499
2500
-# Creates buffer.
2501
-# Binds the buffer with the supplied memory.
2502
2503
If any of these operations fail, buffer is not created,
2504
returned value is negative error code and `*pBuffer` is null.
2505
2506
If the function succeeded, you must destroy the buffer when you
2507
no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding
2508
allocation you can use convenience function vmaDestroyBuffer().
2509
2510
\note There is a new version of this function augmented with parameter `allocationLocalOffset` - see vmaCreateAliasingBuffer2().
2511
*/
2512
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
2513
VmaAllocator VMA_NOT_NULL allocator,
2514
VmaAllocation VMA_NOT_NULL allocation,
2515
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2516
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);
2517
2518
/** \brief Creates a new `VkBuffer`, binds already created memory for it.
2519
2520
\param allocator
2521
\param allocation Allocation that provides memory to be used for binding new buffer to it.
2522
\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the allocation. Normally it should be 0.
2523
\param pBufferCreateInfo
2524
\param[out] pBuffer Buffer that was created.
2525
2526
This function automatically:
2527
2528
-# Creates buffer.
2529
-# Binds the buffer with the supplied memory.
2530
2531
If any of these operations fail, buffer is not created,
2532
returned value is negative error code and `*pBuffer` is null.
2533
2534
If the function succeeded, you must destroy the buffer when you
2535
no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding
2536
allocation you can use convenience function vmaDestroyBuffer().
2537
2538
\note This is a new version of the function augmented with parameter `allocationLocalOffset`.
2539
*/
2540
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(
2541
VmaAllocator VMA_NOT_NULL allocator,
2542
VmaAllocation VMA_NOT_NULL allocation,
2543
VkDeviceSize allocationLocalOffset,
2544
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2545
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);
2546
2547
/** \brief Destroys Vulkan buffer and frees allocated memory.
2548
2549
This is just a convenience function equivalent to:
2550
2551
\code
2552
vkDestroyBuffer(device, buffer, allocationCallbacks);
2553
vmaFreeMemory(allocator, allocation);
2554
\endcode
2555
2556
It is safe to pass null as buffer and/or allocation.
2557
*/
2558
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
2559
VmaAllocator VMA_NOT_NULL allocator,
2560
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
2561
VmaAllocation VMA_NULLABLE allocation);
2562
2563
/// Function similar to vmaCreateBuffer().
2564
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
2565
VmaAllocator VMA_NOT_NULL allocator,
2566
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2567
const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2568
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,
2569
VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2570
VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2571
2572
/// Function similar to vmaCreateAliasingBuffer() but for images.
2573
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
2574
VmaAllocator VMA_NOT_NULL allocator,
2575
VmaAllocation VMA_NOT_NULL allocation,
2576
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2577
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);
2578
2579
/// Function similar to vmaCreateAliasingBuffer2() but for images.
2580
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(
2581
VmaAllocator VMA_NOT_NULL allocator,
2582
VmaAllocation VMA_NOT_NULL allocation,
2583
VkDeviceSize allocationLocalOffset,
2584
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2585
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);
2586
2587
/** \brief Destroys Vulkan image and frees allocated memory.
2588
2589
This is just a convenience function equivalent to:
2590
2591
\code
2592
vkDestroyImage(device, image, allocationCallbacks);
2593
vmaFreeMemory(allocator, allocation);
2594
\endcode
2595
2596
It is safe to pass null as image and/or allocation.
2597
*/
2598
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
2599
VmaAllocator VMA_NOT_NULL allocator,
2600
VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
2601
VmaAllocation VMA_NULLABLE allocation);
2602
2603
/** @} */
2604
2605
/**
2606
\addtogroup group_virtual
2607
@{
2608
*/
2609
2610
/** \brief Creates new #VmaVirtualBlock object.
2611
2612
\param pCreateInfo Parameters for creation.
2613
\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.
2614
*/
2615
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
2616
const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
2617
VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock);
2618
2619
/** \brief Destroys #VmaVirtualBlock object.
2620
2621
Please note that you should consciously handle virtual allocations that could remain unfreed in the block.
2622
You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()
2623
if you are sure this is what you want. If you do neither, an assert is called.
2624
2625
If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,
2626
don't forget to free them.
2627
*/
2628
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(
2629
VmaVirtualBlock VMA_NULLABLE virtualBlock);
2630
2631
/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.
2632
*/
2633
VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(
2634
VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2635
2636
/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.
2637
*/
2638
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(
2639
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2640
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);
2641
2642
/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.
2643
2644
If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned
2645
(despite the function doesn't ever allocate actual GPU memory).
2646
`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`.
2647
2648
\param virtualBlock Virtual block
2649
\param pCreateInfo Parameters for the allocation
2650
\param[out] pAllocation Returned handle of the new allocation
2651
\param[out] pOffset Returned offset of the new allocation. Optional, can be null.
2652
*/
2653
VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(
2654
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2655
const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
2656
VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
2657
VkDeviceSize* VMA_NULLABLE pOffset);
2658
2659
/** \brief Frees virtual allocation inside given #VmaVirtualBlock.
2660
2661
It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing.
2662
*/
2663
VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(
2664
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2665
VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation);
2666
2667
/** \brief Frees all virtual allocations inside given #VmaVirtualBlock.
2668
2669
You must either call this function or free each virtual allocation individually with vmaVirtualFree()
2670
before destroying a virtual block. Otherwise, an assert is called.
2671
2672
If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,
2673
don't forget to free it as well.
2674
*/
2675
VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(
2676
VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2677
2678
/** \brief Changes custom pointer associated with given virtual allocation.
2679
*/
2680
VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(
2681
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2682
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,
2683
void* VMA_NULLABLE pUserData);
2684
2685
/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2686
2687
This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics().
2688
*/
2689
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(
2690
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2691
VmaStatistics* VMA_NOT_NULL pStats);
2692
2693
/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2694
2695
This function is slow to call. Use for debugging purposes.
2696
For less detailed statistics, see vmaGetVirtualBlockStatistics().
2697
*/
2698
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(
2699
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2700
VmaDetailedStatistics* VMA_NOT_NULL pStats);
2701
2702
/** @} */
2703
2704
#if VMA_STATS_STRING_ENABLED
2705
/**
2706
\addtogroup group_stats
2707
@{
2708
*/
2709
2710
/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.
2711
\param virtualBlock Virtual block.
2712
\param[out] ppStatsString Returned string.
2713
\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.
2714
2715
Returned string must be freed using vmaFreeVirtualBlockStatsString().
2716
*/
2717
VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(
2718
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2719
char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2720
VkBool32 detailedMap);
2721
2722
/// Frees a string returned by vmaBuildVirtualBlockStatsString().
2723
VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(
2724
VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2725
char* VMA_NULLABLE pStatsString);
2726
2727
/** \brief Builds and returns statistics as a null-terminated string in JSON format.
2728
\param allocator
2729
\param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2730
\param detailedMap
2731
*/
2732
VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2733
VmaAllocator VMA_NOT_NULL allocator,
2734
char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2735
VkBool32 detailedMap);
2736
2737
VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2738
VmaAllocator VMA_NOT_NULL allocator,
2739
char* VMA_NULLABLE pStatsString);
2740
2741
/** @} */
2742
2743
#endif // VMA_STATS_STRING_ENABLED
2744
2745
#endif // _VMA_FUNCTION_HEADERS
2746
2747
#ifdef __cplusplus
2748
}
2749
#endif
2750
2751
#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2752
2753
////////////////////////////////////////////////////////////////////////////////
2754
////////////////////////////////////////////////////////////////////////////////
2755
//
2756
// IMPLEMENTATION
2757
//
2758
////////////////////////////////////////////////////////////////////////////////
2759
////////////////////////////////////////////////////////////////////////////////
2760
2761
// For Visual Studio IntelliSense.
2762
#if defined(__cplusplus) && defined(__INTELLISENSE__)
2763
#define VMA_IMPLEMENTATION
2764
#endif
2765
2766
#ifdef VMA_IMPLEMENTATION
2767
#undef VMA_IMPLEMENTATION
2768
2769
#include <cstdint>
2770
#include <cstdlib>
2771
#include <cstring>
2772
#include <cinttypes>
2773
#include <utility>
2774
#include <type_traits>
2775
2776
#if !defined(VMA_CPP20)
2777
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20
2778
#define VMA_CPP20 1
2779
#else
2780
#define VMA_CPP20 0
2781
#endif
2782
#endif
2783
2784
#ifdef _MSC_VER
2785
#include <intrin.h> // For functions like __popcnt, _BitScanForward etc.
2786
#endif
2787
#if VMA_CPP20
2788
#include <bit>
2789
#endif
2790
2791
#if VMA_STATS_STRING_ENABLED
2792
#include <cstdio> // For snprintf
2793
#endif
2794
2795
/*******************************************************************************
2796
CONFIGURATION SECTION
2797
2798
Define some of these macros before each #include of this header or change them
2799
here if you need other then default behavior depending on your environment.
2800
*/
2801
#ifndef _VMA_CONFIGURATION
2802
2803
/*
2804
Define this macro to 1 to make the library fetch pointers to Vulkan functions
2805
internally, like:
2806
2807
vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2808
*/
2809
#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2810
#define VMA_STATIC_VULKAN_FUNCTIONS 1
2811
#endif
2812
2813
/*
2814
Define this macro to 1 to make the library fetch pointers to Vulkan functions
2815
internally, like:
2816
2817
vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory");
2818
2819
To use this feature in new versions of VMA you now have to pass
2820
VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as
2821
VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null.
2822
*/
2823
#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
2824
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
2825
#endif
2826
2827
#ifndef VMA_USE_STL_SHARED_MUTEX
2828
#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17
2829
#define VMA_USE_STL_SHARED_MUTEX 1
2830
// Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
2831
// Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
2832
#elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
2833
#define VMA_USE_STL_SHARED_MUTEX 1
2834
#else
2835
#define VMA_USE_STL_SHARED_MUTEX 0
2836
#endif
2837
#endif
2838
2839
/*
2840
Define this macro to include custom header files without having to edit this file directly, e.g.:
2841
2842
// Inside of "my_vma_configuration_user_includes.h":
2843
2844
#include "my_custom_assert.h" // for MY_CUSTOM_ASSERT
2845
#include "my_custom_min.h" // for my_custom_min
2846
#include <algorithm>
2847
#include <mutex>
2848
2849
// Inside a different file, which includes "vk_mem_alloc.h":
2850
2851
#define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h"
2852
#define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr)
2853
#define VMA_MIN(v1, v2) (my_custom_min(v1, v2))
2854
#include "vk_mem_alloc.h"
2855
...
2856
2857
The following headers are used in this CONFIGURATION section only, so feel free to
2858
remove them if not needed.
2859
*/
2860
#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)
2861
#include <cassert> // for assert
2862
#include <algorithm> // for min, max, swap
2863
#include <mutex>
2864
#else
2865
#include VMA_CONFIGURATION_USER_INCLUDES_H
2866
#endif
2867
2868
#ifndef VMA_NULL
2869
// Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2870
#define VMA_NULL nullptr
2871
#endif
2872
2873
#ifndef VMA_FALLTHROUGH
2874
#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17
2875
#define VMA_FALLTHROUGH [[fallthrough]]
2876
#else
2877
#define VMA_FALLTHROUGH
2878
#endif
2879
#endif
2880
2881
// Normal assert to check for programmer's errors, especially in Debug configuration.
2882
#ifndef VMA_ASSERT
2883
#ifdef NDEBUG
2884
#define VMA_ASSERT(expr)
2885
#else
2886
#define VMA_ASSERT(expr) assert(expr)
2887
#endif
2888
#endif
2889
2890
// Assert that will be called very often, like inside data structures e.g. operator[].
2891
// Making it non-empty can make program slow.
2892
#ifndef VMA_HEAVY_ASSERT
2893
#ifdef NDEBUG
2894
#define VMA_HEAVY_ASSERT(expr)
2895
#else
2896
#define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
2897
#endif
2898
#endif
2899
2900
// Assert used for reporting memory leaks - unfreed allocations.
2901
#ifndef VMA_ASSERT_LEAK
2902
#define VMA_ASSERT_LEAK(expr) VMA_ASSERT(expr)
2903
#endif
2904
2905
// If your compiler is not compatible with C++17 and definition of
2906
// aligned_alloc() function is missing, uncommenting following line may help:
2907
2908
//#include <malloc.h>
2909
2910
#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
2911
#include <cstdlib>
2912
static void* vma_aligned_alloc(size_t alignment, size_t size)
2913
{
2914
// alignment must be >= sizeof(void*)
2915
if(alignment < sizeof(void*))
2916
{
2917
alignment = sizeof(void*);
2918
}
2919
2920
return memalign(alignment, size);
2921
}
2922
#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
2923
#include <cstdlib>
2924
2925
#if defined(__APPLE__)
2926
#include <AvailabilityMacros.h>
2927
#endif
2928
2929
static void* vma_aligned_alloc(size_t alignment, size_t size)
2930
{
2931
// Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4)
2932
// Therefore, for now disable this specific exception until a proper solution is found.
2933
//#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
2934
//#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
2935
// // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
2936
// // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
2937
// // MAC_OS_X_VERSION_10_16), even though the function is marked
2938
// // available for 10.15. That is why the preprocessor checks for 10.16 but
2939
// // the __builtin_available checks for 10.15.
2940
// // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
2941
// if (__builtin_available(macOS 10.15, iOS 13, *))
2942
// return aligned_alloc(alignment, size);
2943
//#endif
2944
//#endif
2945
2946
// alignment must be >= sizeof(void*)
2947
if(alignment < sizeof(void*))
2948
{
2949
alignment = sizeof(void*);
2950
}
2951
2952
void *pointer;
2953
if(posix_memalign(&pointer, alignment, size) == 0)
2954
return pointer;
2955
return VMA_NULL;
2956
}
2957
#elif defined(_WIN32)
2958
static void* vma_aligned_alloc(size_t alignment, size_t size)
2959
{
2960
return _aligned_malloc(size, alignment);
2961
}
2962
#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17
2963
static void* vma_aligned_alloc(size_t alignment, size_t size)
2964
{
2965
return aligned_alloc(alignment, size);
2966
}
2967
#else
2968
static void* vma_aligned_alloc(size_t alignment, size_t size)
2969
{
2970
VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system.");
2971
return VMA_NULL;
2972
}
2973
#endif
2974
2975
#if defined(_WIN32)
2976
static void vma_aligned_free(void* ptr)
2977
{
2978
_aligned_free(ptr);
2979
}
2980
#else
2981
static void vma_aligned_free(void* VMA_NULLABLE ptr)
2982
{
2983
free(ptr);
2984
}
2985
#endif
2986
2987
#ifndef VMA_ALIGN_OF
2988
#define VMA_ALIGN_OF(type) (alignof(type))
2989
#endif
2990
2991
#ifndef VMA_SYSTEM_ALIGNED_MALLOC
2992
#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
2993
#endif
2994
2995
#ifndef VMA_SYSTEM_ALIGNED_FREE
2996
// VMA_SYSTEM_FREE is the old name, but might have been defined by the user
2997
#if defined(VMA_SYSTEM_FREE)
2998
#define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
2999
#else
3000
#define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
3001
#endif
3002
#endif
3003
3004
#ifndef VMA_COUNT_BITS_SET
3005
// Returns number of bits set to 1 in (v)
3006
#define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v)
3007
#endif
3008
3009
#ifndef VMA_BITSCAN_LSB
3010
// Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX
3011
#define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask)
3012
#endif
3013
3014
#ifndef VMA_BITSCAN_MSB
3015
// Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX
3016
#define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask)
3017
#endif
3018
3019
#ifndef VMA_MIN
3020
#define VMA_MIN(v1, v2) ((std::min)((v1), (v2)))
3021
#endif
3022
3023
#ifndef VMA_MAX
3024
#define VMA_MAX(v1, v2) ((std::max)((v1), (v2)))
3025
#endif
3026
3027
#ifndef VMA_SORT
3028
#define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3029
#endif
3030
3031
#ifndef VMA_DEBUG_LOG_FORMAT
3032
#define VMA_DEBUG_LOG_FORMAT(format, ...)
3033
/*
3034
#define VMA_DEBUG_LOG_FORMAT(format, ...) do { \
3035
printf((format), __VA_ARGS__); \
3036
printf("\n"); \
3037
} while(false)
3038
*/
3039
#endif
3040
3041
#ifndef VMA_DEBUG_LOG
3042
#define VMA_DEBUG_LOG(str) VMA_DEBUG_LOG_FORMAT("%s", (str))
3043
#endif
3044
3045
#ifndef VMA_LEAK_LOG_FORMAT
3046
#define VMA_LEAK_LOG_FORMAT(format, ...) VMA_DEBUG_LOG_FORMAT(format, __VA_ARGS__)
3047
#endif
3048
3049
#ifndef VMA_CLASS_NO_COPY
3050
#define VMA_CLASS_NO_COPY(className) \
3051
private: \
3052
className(const className&) = delete; \
3053
className& operator=(const className&) = delete;
3054
#endif
3055
#ifndef VMA_CLASS_NO_COPY_NO_MOVE
3056
#define VMA_CLASS_NO_COPY_NO_MOVE(className) \
3057
private: \
3058
className(const className&) = delete; \
3059
className(className&&) = delete; \
3060
className& operator=(const className&) = delete; \
3061
className& operator=(className&&) = delete;
3062
#endif
3063
3064
// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3065
#if VMA_STATS_STRING_ENABLED
3066
static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
3067
{
3068
snprintf(outStr, strLen, "%" PRIu32, num);
3069
}
3070
static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
3071
{
3072
snprintf(outStr, strLen, "%" PRIu64, num);
3073
}
3074
static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
3075
{
3076
snprintf(outStr, strLen, "%p", ptr);
3077
}
3078
#endif
3079
3080
#ifndef VMA_MUTEX
3081
class VmaMutex
3082
{
3083
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutex)
3084
public:
3085
VmaMutex() { }
3086
void Lock() { m_Mutex.lock(); }
3087
void Unlock() { m_Mutex.unlock(); }
3088
bool TryLock() { return m_Mutex.try_lock(); }
3089
private:
3090
std::mutex m_Mutex;
3091
};
3092
#define VMA_MUTEX VmaMutex
3093
#endif
3094
3095
// Read-write mutex, where "read" is shared access, "write" is exclusive access.
3096
#ifndef VMA_RW_MUTEX
3097
#if VMA_USE_STL_SHARED_MUTEX
3098
// Use std::shared_mutex from C++17.
3099
#include <shared_mutex>
3100
class VmaRWMutex
3101
{
3102
public:
3103
void LockRead() { m_Mutex.lock_shared(); }
3104
void UnlockRead() { m_Mutex.unlock_shared(); }
3105
bool TryLockRead() { return m_Mutex.try_lock_shared(); }
3106
void LockWrite() { m_Mutex.lock(); }
3107
void UnlockWrite() { m_Mutex.unlock(); }
3108
bool TryLockWrite() { return m_Mutex.try_lock(); }
3109
private:
3110
std::shared_mutex m_Mutex;
3111
};
3112
#define VMA_RW_MUTEX VmaRWMutex
3113
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
3114
// Use SRWLOCK from WinAPI.
3115
// Minimum supported client = Windows Vista, server = Windows Server 2008.
3116
class VmaRWMutex
3117
{
3118
public:
3119
VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3120
void LockRead() { AcquireSRWLockShared(&m_Lock); }
3121
void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3122
bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
3123
void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3124
void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3125
bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
3126
private:
3127
SRWLOCK m_Lock;
3128
};
3129
#define VMA_RW_MUTEX VmaRWMutex
3130
#else
3131
// Less efficient fallback: Use normal mutex.
3132
class VmaRWMutex
3133
{
3134
public:
3135
void LockRead() { m_Mutex.Lock(); }
3136
void UnlockRead() { m_Mutex.Unlock(); }
3137
bool TryLockRead() { return m_Mutex.TryLock(); }
3138
void LockWrite() { m_Mutex.Lock(); }
3139
void UnlockWrite() { m_Mutex.Unlock(); }
3140
bool TryLockWrite() { return m_Mutex.TryLock(); }
3141
private:
3142
VMA_MUTEX m_Mutex;
3143
};
3144
#define VMA_RW_MUTEX VmaRWMutex
3145
#endif // #if VMA_USE_STL_SHARED_MUTEX
3146
#endif // #ifndef VMA_RW_MUTEX
3147
3148
/*
3149
If providing your own implementation, you need to implement a subset of std::atomic.
3150
*/
3151
#ifndef VMA_ATOMIC_UINT32
3152
#include <atomic>
3153
#define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3154
#endif
3155
3156
#ifndef VMA_ATOMIC_UINT64
3157
#include <atomic>
3158
#define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
3159
#endif
3160
3161
#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3162
/**
3163
Every allocation will have its own memory block.
3164
Define to 1 for debugging purposes only.
3165
*/
3166
#define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3167
#endif
3168
3169
#ifndef VMA_MIN_ALIGNMENT
3170
/**
3171
Minimum alignment of all allocations, in bytes.
3172
Set to more than 1 for debugging purposes. Must be power of two.
3173
*/
3174
#ifdef VMA_DEBUG_ALIGNMENT // Old name
3175
#define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
3176
#else
3177
#define VMA_MIN_ALIGNMENT (1)
3178
#endif
3179
#endif
3180
3181
#ifndef VMA_DEBUG_MARGIN
3182
/**
3183
Minimum margin after every allocation, in bytes.
3184
Set nonzero for debugging purposes only.
3185
*/
3186
#define VMA_DEBUG_MARGIN (0)
3187
#endif
3188
3189
#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3190
/**
3191
Define this macro to 1 to automatically fill new allocations and destroyed
3192
allocations with some bit pattern.
3193
*/
3194
#define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3195
#endif
3196
3197
#ifndef VMA_DEBUG_DETECT_CORRUPTION
3198
/**
3199
Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3200
enable writing magic value to the margin after every allocation and
3201
validating it, so that memory corruptions (out-of-bounds writes) are detected.
3202
*/
3203
#define VMA_DEBUG_DETECT_CORRUPTION (0)
3204
#endif
3205
3206
#ifndef VMA_DEBUG_GLOBAL_MUTEX
3207
/**
3208
Set this to 1 for debugging purposes only, to enable single mutex protecting all
3209
entry calls to the library. Can be useful for debugging multithreading issues.
3210
*/
3211
#define VMA_DEBUG_GLOBAL_MUTEX (0)
3212
#endif
3213
3214
#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3215
/**
3216
Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3217
Set to more than 1 for debugging purposes only. Must be power of two.
3218
*/
3219
#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3220
#endif
3221
3222
#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
3223
/*
3224
Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
3225
and return error instead of leaving up to Vulkan implementation what to do in such cases.
3226
*/
3227
#define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
3228
#endif
3229
3230
#ifndef VMA_SMALL_HEAP_MAX_SIZE
3231
/// Maximum size of a memory heap in Vulkan to consider it "small".
3232
#define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3233
#endif
3234
3235
#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3236
/// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3237
#define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3238
#endif
3239
3240
/*
3241
Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called
3242
or a persistently mapped allocation is created and destroyed several times in a row.
3243
It keeps additional +1 mapping of a device memory block to prevent calling actual
3244
vkMapMemory/vkUnmapMemory too many times, which may improve performance and help
3245
tools like RenderDoc.
3246
*/
3247
#ifndef VMA_MAPPING_HYSTERESIS_ENABLED
3248
#define VMA_MAPPING_HYSTERESIS_ENABLED 1
3249
#endif
3250
3251
#define VMA_VALIDATE(cond) do { if(!(cond)) { \
3252
VMA_ASSERT(0 && "Validation failed: " #cond); \
3253
return false; \
3254
} } while(false)
3255
3256
/*******************************************************************************
3257
END OF CONFIGURATION
3258
*/
3259
#endif // _VMA_CONFIGURATION
3260
3261
3262
static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3263
static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3264
// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3265
static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3266
3267
// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
3268
static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
3269
static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
3270
static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
3271
static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;
3272
static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;
3273
static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3274
static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
3275
static const uint32_t VMA_VENDOR_ID_AMD = 4098;
3276
3277
// This one is tricky. Vulkan specification defines this code as available since
3278
// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131.
3279
// See pull request #207.
3280
#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13)
3281
3282
3283
#if VMA_STATS_STRING_ENABLED
3284
// Correspond to values of enum VmaSuballocationType.
3285
static const char* VMA_SUBALLOCATION_TYPE_NAMES[] =
3286
{
3287
"FREE",
3288
"UNKNOWN",
3289
"BUFFER",
3290
"IMAGE_UNKNOWN",
3291
"IMAGE_LINEAR",
3292
"IMAGE_OPTIMAL",
3293
};
3294
#endif
3295
3296
static VkAllocationCallbacks VmaEmptyAllocationCallbacks =
3297
{ VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3298
3299
3300
#ifndef _VMA_ENUM_DECLARATIONS
3301
3302
enum VmaSuballocationType
3303
{
3304
VMA_SUBALLOCATION_TYPE_FREE = 0,
3305
VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3306
VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3307
VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3308
VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3309
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3310
VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3311
};
3312
3313
enum VMA_CACHE_OPERATION
3314
{
3315
VMA_CACHE_FLUSH,
3316
VMA_CACHE_INVALIDATE
3317
};
3318
3319
enum class VmaAllocationRequestType
3320
{
3321
Normal,
3322
TLSF,
3323
// Used by "Linear" algorithm.
3324
UpperAddress,
3325
EndOf1st,
3326
EndOf2nd,
3327
};
3328
3329
#endif // _VMA_ENUM_DECLARATIONS
3330
3331
#ifndef _VMA_FORWARD_DECLARATIONS
3332
// Opaque handle used by allocation algorithms to identify single allocation in any conforming way.
3333
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle);
3334
3335
struct VmaMutexLock;
3336
struct VmaMutexLockRead;
3337
struct VmaMutexLockWrite;
3338
3339
template<typename T>
3340
struct AtomicTransactionalIncrement;
3341
3342
template<typename T>
3343
struct VmaStlAllocator;
3344
3345
template<typename T, typename AllocatorT>
3346
class VmaVector;
3347
3348
template<typename T, typename AllocatorT, size_t N>
3349
class VmaSmallVector;
3350
3351
template<typename T>
3352
class VmaPoolAllocator;
3353
3354
template<typename T>
3355
struct VmaListItem;
3356
3357
template<typename T>
3358
class VmaRawList;
3359
3360
template<typename T, typename AllocatorT>
3361
class VmaList;
3362
3363
template<typename ItemTypeTraits>
3364
class VmaIntrusiveLinkedList;
3365
3366
#if VMA_STATS_STRING_ENABLED
3367
class VmaStringBuilder;
3368
class VmaJsonWriter;
3369
#endif
3370
3371
class VmaDeviceMemoryBlock;
3372
3373
struct VmaDedicatedAllocationListItemTraits;
3374
class VmaDedicatedAllocationList;
3375
3376
struct VmaSuballocation;
3377
struct VmaSuballocationOffsetLess;
3378
struct VmaSuballocationOffsetGreater;
3379
struct VmaSuballocationItemSizeLess;
3380
3381
typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;
3382
3383
struct VmaAllocationRequest;
3384
3385
class VmaBlockMetadata;
3386
class VmaBlockMetadata_Linear;
3387
class VmaBlockMetadata_TLSF;
3388
3389
class VmaBlockVector;
3390
3391
struct VmaPoolListItemTraits;
3392
3393
struct VmaCurrentBudgetData;
3394
3395
class VmaAllocationObjectAllocator;
3396
3397
#endif // _VMA_FORWARD_DECLARATIONS
3398
3399
3400
#ifndef _VMA_FUNCTIONS
3401
3402
/*
3403
Returns number of bits set to 1 in (v).
3404
3405
On specific platforms and compilers you can use intrinsics like:
3406
3407
Visual Studio:
3408
return __popcnt(v);
3409
GCC, Clang:
3410
return static_cast<uint32_t>(__builtin_popcount(v));
3411
3412
Define macro VMA_COUNT_BITS_SET to provide your optimized implementation.
3413
But you need to check in runtime whether user's CPU supports these, as some old processors don't.
3414
*/
3415
static inline uint32_t VmaCountBitsSet(uint32_t v)
3416
{
3417
#if VMA_CPP20
3418
return std::popcount(v);
3419
#else
3420
uint32_t c = v - ((v >> 1) & 0x55555555);
3421
c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3422
c = ((c >> 4) + c) & 0x0F0F0F0F;
3423
c = ((c >> 8) + c) & 0x00FF00FF;
3424
c = ((c >> 16) + c) & 0x0000FFFF;
3425
return c;
3426
#endif
3427
}
3428
3429
static inline uint8_t VmaBitScanLSB(uint64_t mask)
3430
{
3431
#if defined(_MSC_VER) && defined(_WIN64)
3432
unsigned long pos;
3433
if (_BitScanForward64(&pos, mask))
3434
return static_cast<uint8_t>(pos);
3435
return UINT8_MAX;
3436
#elif VMA_CPP20
3437
if(mask)
3438
return static_cast<uint8_t>(std::countr_zero(mask));
3439
return UINT8_MAX;
3440
#elif defined __GNUC__ || defined __clang__
3441
return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;
3442
#else
3443
uint8_t pos = 0;
3444
uint64_t bit = 1;
3445
do
3446
{
3447
if (mask & bit)
3448
return pos;
3449
bit <<= 1;
3450
} while (pos++ < 63);
3451
return UINT8_MAX;
3452
#endif
3453
}
3454
3455
static inline uint8_t VmaBitScanLSB(uint32_t mask)
3456
{
3457
#ifdef _MSC_VER
3458
unsigned long pos;
3459
if (_BitScanForward(&pos, mask))
3460
return static_cast<uint8_t>(pos);
3461
return UINT8_MAX;
3462
#elif VMA_CPP20
3463
if(mask)
3464
return static_cast<uint8_t>(std::countr_zero(mask));
3465
return UINT8_MAX;
3466
#elif defined __GNUC__ || defined __clang__
3467
return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;
3468
#else
3469
uint8_t pos = 0;
3470
uint32_t bit = 1;
3471
do
3472
{
3473
if (mask & bit)
3474
return pos;
3475
bit <<= 1;
3476
} while (pos++ < 31);
3477
return UINT8_MAX;
3478
#endif
3479
}
3480
3481
static inline uint8_t VmaBitScanMSB(uint64_t mask)
3482
{
3483
#if defined(_MSC_VER) && defined(_WIN64)
3484
unsigned long pos;
3485
if (_BitScanReverse64(&pos, mask))
3486
return static_cast<uint8_t>(pos);
3487
#elif VMA_CPP20
3488
if(mask)
3489
return 63 - static_cast<uint8_t>(std::countl_zero(mask));
3490
#elif defined __GNUC__ || defined __clang__
3491
if (mask)
3492
return 63 - static_cast<uint8_t>(__builtin_clzll(mask));
3493
#else
3494
uint8_t pos = 63;
3495
uint64_t bit = 1ULL << 63;
3496
do
3497
{
3498
if (mask & bit)
3499
return pos;
3500
bit >>= 1;
3501
} while (pos-- > 0);
3502
#endif
3503
return UINT8_MAX;
3504
}
3505
3506
static inline uint8_t VmaBitScanMSB(uint32_t mask)
3507
{
3508
#ifdef _MSC_VER
3509
unsigned long pos;
3510
if (_BitScanReverse(&pos, mask))
3511
return static_cast<uint8_t>(pos);
3512
#elif VMA_CPP20
3513
if(mask)
3514
return 31 - static_cast<uint8_t>(std::countl_zero(mask));
3515
#elif defined __GNUC__ || defined __clang__
3516
if (mask)
3517
return 31 - static_cast<uint8_t>(__builtin_clz(mask));
3518
#else
3519
uint8_t pos = 31;
3520
uint32_t bit = 1UL << 31;
3521
do
3522
{
3523
if (mask & bit)
3524
return pos;
3525
bit >>= 1;
3526
} while (pos-- > 0);
3527
#endif
3528
return UINT8_MAX;
3529
}
3530
3531
/*
3532
Returns true if given number is a power of two.
3533
T must be unsigned integer number or signed integer but always nonnegative.
3534
For 0 returns true.
3535
*/
3536
template <typename T>
3537
inline bool VmaIsPow2(T x)
3538
{
3539
return (x & (x - 1)) == 0;
3540
}
3541
3542
// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3543
// Use types like uint32_t, uint64_t as T.
3544
template <typename T>
3545
static inline T VmaAlignUp(T val, T alignment)
3546
{
3547
VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3548
return (val + alignment - 1) & ~(alignment - 1);
3549
}
3550
3551
// Aligns given value down to nearest multiply of align value. For example: VmaAlignDown(11, 8) = 8.
3552
// Use types like uint32_t, uint64_t as T.
3553
template <typename T>
3554
static inline T VmaAlignDown(T val, T alignment)
3555
{
3556
VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3557
return val & ~(alignment - 1);
3558
}
3559
3560
// Division with mathematical rounding to nearest number.
3561
template <typename T>
3562
static inline T VmaRoundDiv(T x, T y)
3563
{
3564
return (x + (y / (T)2)) / y;
3565
}
3566
3567
// Divide by 'y' and round up to nearest integer.
3568
template <typename T>
3569
static inline T VmaDivideRoundingUp(T x, T y)
3570
{
3571
return (x + y - (T)1) / y;
3572
}
3573
3574
// Returns smallest power of 2 greater or equal to v.
3575
static inline uint32_t VmaNextPow2(uint32_t v)
3576
{
3577
v--;
3578
v |= v >> 1;
3579
v |= v >> 2;
3580
v |= v >> 4;
3581
v |= v >> 8;
3582
v |= v >> 16;
3583
v++;
3584
return v;
3585
}
3586
3587
static inline uint64_t VmaNextPow2(uint64_t v)
3588
{
3589
v--;
3590
v |= v >> 1;
3591
v |= v >> 2;
3592
v |= v >> 4;
3593
v |= v >> 8;
3594
v |= v >> 16;
3595
v |= v >> 32;
3596
v++;
3597
return v;
3598
}
3599
3600
// Returns largest power of 2 less or equal to v.
3601
static inline uint32_t VmaPrevPow2(uint32_t v)
3602
{
3603
v |= v >> 1;
3604
v |= v >> 2;
3605
v |= v >> 4;
3606
v |= v >> 8;
3607
v |= v >> 16;
3608
v = v ^ (v >> 1);
3609
return v;
3610
}
3611
3612
static inline uint64_t VmaPrevPow2(uint64_t v)
3613
{
3614
v |= v >> 1;
3615
v |= v >> 2;
3616
v |= v >> 4;
3617
v |= v >> 8;
3618
v |= v >> 16;
3619
v |= v >> 32;
3620
v = v ^ (v >> 1);
3621
return v;
3622
}
3623
3624
static inline bool VmaStrIsEmpty(const char* pStr)
3625
{
3626
return pStr == VMA_NULL || *pStr == '\0';
3627
}
3628
3629
/*
3630
Returns true if two memory blocks occupy overlapping pages.
3631
ResourceA must be in less memory offset than ResourceB.
3632
3633
Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3634
chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3635
*/
3636
static inline bool VmaBlocksOnSamePage(
3637
VkDeviceSize resourceAOffset,
3638
VkDeviceSize resourceASize,
3639
VkDeviceSize resourceBOffset,
3640
VkDeviceSize pageSize)
3641
{
3642
VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3643
VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3644
VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3645
VkDeviceSize resourceBStart = resourceBOffset;
3646
VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3647
return resourceAEndPage == resourceBStartPage;
3648
}
3649
3650
/*
3651
Returns true if given suballocation types could conflict and must respect
3652
VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3653
or linear image and another one is optimal image. If type is unknown, behave
3654
conservatively.
3655
*/
3656
static inline bool VmaIsBufferImageGranularityConflict(
3657
VmaSuballocationType suballocType1,
3658
VmaSuballocationType suballocType2)
3659
{
3660
if (suballocType1 > suballocType2)
3661
{
3662
std::swap(suballocType1, suballocType2);
3663
}
3664
3665
switch (suballocType1)
3666
{
3667
case VMA_SUBALLOCATION_TYPE_FREE:
3668
return false;
3669
case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3670
return true;
3671
case VMA_SUBALLOCATION_TYPE_BUFFER:
3672
return
3673
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3674
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3675
case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3676
return
3677
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3678
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3679
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3680
case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3681
return
3682
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3683
case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3684
return false;
3685
default:
3686
VMA_ASSERT(0);
3687
return true;
3688
}
3689
}
3690
3691
static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3692
{
3693
#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3694
uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3695
const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3696
for (size_t i = 0; i < numberCount; ++i, ++pDst)
3697
{
3698
*pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3699
}
3700
#else
3701
// no-op
3702
#endif
3703
}
3704
3705
static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3706
{
3707
#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3708
const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3709
const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3710
for (size_t i = 0; i < numberCount; ++i, ++pSrc)
3711
{
3712
if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3713
{
3714
return false;
3715
}
3716
}
3717
#endif
3718
return true;
3719
}
3720
3721
/*
3722
Fills structure with parameters of an example buffer to be used for transfers
3723
during GPU memory defragmentation.
3724
*/
3725
static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
3726
{
3727
memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
3728
outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3729
outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3730
outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
3731
}
3732
3733
3734
/*
3735
Performs binary search and returns iterator to first element that is greater or
3736
equal to (key), according to comparison (cmp).
3737
3738
Cmp should return true if first argument is less than second argument.
3739
3740
Returned value is the found element, if present in the collection or place where
3741
new element with value (key) should be inserted.
3742
*/
3743
template <typename CmpLess, typename IterT, typename KeyT>
3744
static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
3745
{
3746
size_t down = 0, up = size_t(end - beg);
3747
while (down < up)
3748
{
3749
const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
3750
if (cmp(*(beg + mid), key))
3751
{
3752
down = mid + 1;
3753
}
3754
else
3755
{
3756
up = mid;
3757
}
3758
}
3759
return beg + down;
3760
}
3761
3762
template<typename CmpLess, typename IterT, typename KeyT>
3763
IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
3764
{
3765
IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
3766
beg, end, value, cmp);
3767
if (it == end ||
3768
(!cmp(*it, value) && !cmp(value, *it)))
3769
{
3770
return it;
3771
}
3772
return end;
3773
}
3774
3775
/*
3776
Returns true if all pointers in the array are not-null and unique.
3777
Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3778
T must be pointer type, e.g. VmaAllocation, VmaPool.
3779
*/
3780
template<typename T>
3781
static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3782
{
3783
for (uint32_t i = 0; i < count; ++i)
3784
{
3785
const T iPtr = arr[i];
3786
if (iPtr == VMA_NULL)
3787
{
3788
return false;
3789
}
3790
for (uint32_t j = i + 1; j < count; ++j)
3791
{
3792
if (iPtr == arr[j])
3793
{
3794
return false;
3795
}
3796
}
3797
}
3798
return true;
3799
}
3800
3801
template<typename MainT, typename NewT>
3802
static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
3803
{
3804
newStruct->pNext = mainStruct->pNext;
3805
mainStruct->pNext = newStruct;
3806
}
3807
// Finds structure with s->sType == sType in mainStruct->pNext chain.
3808
// Returns pointer to it. If not found, returns null.
3809
template<typename FindT, typename MainT>
3810
static inline const FindT* VmaPnextChainFind(const MainT* mainStruct, VkStructureType sType)
3811
{
3812
for(const VkBaseInStructure* s = (const VkBaseInStructure*)mainStruct->pNext;
3813
s != VMA_NULL; s = s->pNext)
3814
{
3815
if(s->sType == sType)
3816
{
3817
return (const FindT*)s;
3818
}
3819
}
3820
return VMA_NULL;
3821
}
3822
3823
// An abstraction over buffer or image `usage` flags, depending on available extensions.
3824
struct VmaBufferImageUsage
3825
{
3826
#if VMA_KHR_MAINTENANCE5
3827
typedef uint64_t BaseType; // VkFlags64
3828
#else
3829
typedef uint32_t BaseType; // VkFlags32
3830
#endif
3831
3832
static const VmaBufferImageUsage UNKNOWN;
3833
3834
BaseType Value;
3835
3836
VmaBufferImageUsage() { *this = UNKNOWN; }
3837
explicit VmaBufferImageUsage(BaseType usage) : Value(usage) { }
3838
VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5);
3839
explicit VmaBufferImageUsage(const VkImageCreateInfo &createInfo);
3840
3841
bool operator==(const VmaBufferImageUsage& rhs) const { return Value == rhs.Value; }
3842
bool operator!=(const VmaBufferImageUsage& rhs) const { return Value != rhs.Value; }
3843
3844
bool Contains(BaseType flag) const { return (Value & flag) != 0; }
3845
bool ContainsDeviceAccess() const
3846
{
3847
// This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same as VK_BUFFER_IMAGE_TRANSFER*.
3848
return (Value & ~BaseType(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;
3849
}
3850
};
3851
3852
const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);
3853
3854
static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept
3855
{
3856
using std::swap;
3857
swap(lhs.Value, rhs.Value);
3858
}
3859
3860
VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,
3861
bool useKhrMaintenance5)
3862
{
3863
#if VMA_KHR_MAINTENANCE5
3864
if(useKhrMaintenance5)
3865
{
3866
// If VkBufferCreateInfo::pNext chain contains VkBufferUsageFlags2CreateInfoKHR,
3867
// take usage from it and ignore VkBufferCreateInfo::usage, per specification
3868
// of the VK_KHR_maintenance5 extension.
3869
const VkBufferUsageFlags2CreateInfoKHR* const usageFlags2 =
3870
VmaPnextChainFind<VkBufferUsageFlags2CreateInfoKHR>(&createInfo, VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO_KHR);
3871
if(usageFlags2)
3872
{
3873
this->Value = usageFlags2->usage;
3874
return;
3875
}
3876
}
3877
#endif
3878
3879
this->Value = (BaseType)createInfo.usage;
3880
}
3881
3882
VmaBufferImageUsage::VmaBufferImageUsage(const VkImageCreateInfo &createInfo)
3883
{
3884
// Maybe in the future there will be VK_KHR_maintenanceN extension with structure
3885
// VkImageUsageFlags2CreateInfoKHR, like the one for buffers...
3886
3887
this->Value = (BaseType)createInfo.usage;
3888
}
3889
3890
// This is the main algorithm that guides the selection of a memory type best for an allocation -
3891
// converts usage to required/preferred/not preferred flags.
3892
static bool FindMemoryPreferences(
3893
bool isIntegratedGPU,
3894
const VmaAllocationCreateInfo& allocCreateInfo,
3895
VmaBufferImageUsage bufImgUsage,
3896
VkMemoryPropertyFlags& outRequiredFlags,
3897
VkMemoryPropertyFlags& outPreferredFlags,
3898
VkMemoryPropertyFlags& outNotPreferredFlags)
3899
{
3900
outRequiredFlags = allocCreateInfo.requiredFlags;
3901
outPreferredFlags = allocCreateInfo.preferredFlags;
3902
outNotPreferredFlags = 0;
3903
3904
switch(allocCreateInfo.usage)
3905
{
3906
case VMA_MEMORY_USAGE_UNKNOWN:
3907
break;
3908
case VMA_MEMORY_USAGE_GPU_ONLY:
3909
if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3910
{
3911
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3912
}
3913
break;
3914
case VMA_MEMORY_USAGE_CPU_ONLY:
3915
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
3916
break;
3917
case VMA_MEMORY_USAGE_CPU_TO_GPU:
3918
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3919
if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3920
{
3921
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3922
}
3923
break;
3924
case VMA_MEMORY_USAGE_GPU_TO_CPU:
3925
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3926
outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3927
break;
3928
case VMA_MEMORY_USAGE_CPU_COPY:
3929
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3930
break;
3931
case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
3932
outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
3933
break;
3934
case VMA_MEMORY_USAGE_AUTO:
3935
case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:
3936
case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:
3937
{
3938
if(bufImgUsage == VmaBufferImageUsage::UNKNOWN)
3939
{
3940
VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known."
3941
" Maybe you use VkBufferUsageFlags2CreateInfoKHR but forgot to use VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT?" );
3942
return false;
3943
}
3944
3945
const bool deviceAccess = bufImgUsage.ContainsDeviceAccess();
3946
const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;
3947
const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;
3948
const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;
3949
const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
3950
const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
3951
3952
// CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU.
3953
if(hostAccessRandom)
3954
{
3955
// Prefer cached. Cannot require it, because some platforms don't have it (e.g. Raspberry Pi - see #362)!
3956
outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3957
3958
if (!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3959
{
3960
// Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL.
3961
// Omitting HOST_VISIBLE here is intentional.
3962
// In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one.
3963
// Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list.
3964
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3965
}
3966
else
3967
{
3968
// Always CPU memory.
3969
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3970
}
3971
}
3972
// CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined.
3973
else if(hostAccessSequentialWrite)
3974
{
3975
// Want uncached and write-combined.
3976
outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3977
3978
if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3979
{
3980
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3981
}
3982
else
3983
{
3984
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3985
// Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame)
3986
if(deviceAccess)
3987
{
3988
// Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory.
3989
if(preferHost)
3990
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3991
else
3992
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3993
}
3994
// GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU)
3995
else
3996
{
3997
// Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory.
3998
if(preferDevice)
3999
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
4000
else
4001
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
4002
}
4003
}
4004
}
4005
// No CPU access
4006
else
4007
{
4008
// if(deviceAccess)
4009
//
4010
// GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory,
4011
// unless there is a clear preference from the user not to do so.
4012
//
4013
// else:
4014
//
4015
// No direct GPU access, no CPU access, just transfers.
4016
// It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or
4017
// a "swap file" copy to free some GPU memory (then better CPU memory).
4018
// Up to the user to decide. If no preferece, assume the former and choose GPU memory.
4019
4020
if(preferHost)
4021
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
4022
else
4023
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
4024
}
4025
break;
4026
}
4027
default:
4028
VMA_ASSERT(0);
4029
}
4030
4031
// Avoid DEVICE_COHERENT unless explicitly requested.
4032
if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &
4033
(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
4034
{
4035
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;
4036
}
4037
4038
return true;
4039
}
4040
4041
////////////////////////////////////////////////////////////////////////////////
4042
// Memory allocation
4043
4044
static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4045
{
4046
void* result = VMA_NULL;
4047
if ((pAllocationCallbacks != VMA_NULL) &&
4048
(pAllocationCallbacks->pfnAllocation != VMA_NULL))
4049
{
4050
result = (*pAllocationCallbacks->pfnAllocation)(
4051
pAllocationCallbacks->pUserData,
4052
size,
4053
alignment,
4054
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4055
}
4056
else
4057
{
4058
result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4059
}
4060
VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4061
return result;
4062
}
4063
4064
static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4065
{
4066
if ((pAllocationCallbacks != VMA_NULL) &&
4067
(pAllocationCallbacks->pfnFree != VMA_NULL))
4068
{
4069
(*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4070
}
4071
else
4072
{
4073
VMA_SYSTEM_ALIGNED_FREE(ptr);
4074
}
4075
}
4076
4077
template<typename T>
4078
static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4079
{
4080
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4081
}
4082
4083
template<typename T>
4084
static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4085
{
4086
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4087
}
4088
4089
#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4090
4091
#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4092
4093
template<typename T>
4094
static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4095
{
4096
ptr->~T();
4097
VmaFree(pAllocationCallbacks, ptr);
4098
}
4099
4100
template<typename T>
4101
static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4102
{
4103
if (ptr != VMA_NULL)
4104
{
4105
for (size_t i = count; i--; )
4106
{
4107
ptr[i].~T();
4108
}
4109
VmaFree(pAllocationCallbacks, ptr);
4110
}
4111
}
4112
4113
static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4114
{
4115
if (srcStr != VMA_NULL)
4116
{
4117
const size_t len = strlen(srcStr);
4118
char* const result = vma_new_array(allocs, char, len + 1);
4119
memcpy(result, srcStr, len + 1);
4120
return result;
4121
}
4122
return VMA_NULL;
4123
}
4124
4125
#if VMA_STATS_STRING_ENABLED
4126
static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
4127
{
4128
if (srcStr != VMA_NULL)
4129
{
4130
char* const result = vma_new_array(allocs, char, strLen + 1);
4131
memcpy(result, srcStr, strLen);
4132
result[strLen] = '\0';
4133
return result;
4134
}
4135
return VMA_NULL;
4136
}
4137
#endif // VMA_STATS_STRING_ENABLED
4138
4139
static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4140
{
4141
if (str != VMA_NULL)
4142
{
4143
const size_t len = strlen(str);
4144
vma_delete_array(allocs, str, len + 1);
4145
}
4146
}
4147
4148
template<typename CmpLess, typename VectorT>
4149
size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4150
{
4151
const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4152
vector.data(),
4153
vector.data() + vector.size(),
4154
value,
4155
CmpLess()) - vector.data();
4156
VmaVectorInsert(vector, indexToInsert, value);
4157
return indexToInsert;
4158
}
4159
4160
template<typename CmpLess, typename VectorT>
4161
bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4162
{
4163
CmpLess comparator;
4164
typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4165
vector.begin(),
4166
vector.end(),
4167
value,
4168
comparator);
4169
if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4170
{
4171
size_t indexToRemove = it - vector.begin();
4172
VmaVectorRemove(vector, indexToRemove);
4173
return true;
4174
}
4175
return false;
4176
}
4177
#endif // _VMA_FUNCTIONS
4178
4179
#ifndef _VMA_STATISTICS_FUNCTIONS
4180
4181
static void VmaClearStatistics(VmaStatistics& outStats)
4182
{
4183
outStats.blockCount = 0;
4184
outStats.allocationCount = 0;
4185
outStats.blockBytes = 0;
4186
outStats.allocationBytes = 0;
4187
}
4188
4189
static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)
4190
{
4191
inoutStats.blockCount += src.blockCount;
4192
inoutStats.allocationCount += src.allocationCount;
4193
inoutStats.blockBytes += src.blockBytes;
4194
inoutStats.allocationBytes += src.allocationBytes;
4195
}
4196
4197
static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)
4198
{
4199
VmaClearStatistics(outStats.statistics);
4200
outStats.unusedRangeCount = 0;
4201
outStats.allocationSizeMin = VK_WHOLE_SIZE;
4202
outStats.allocationSizeMax = 0;
4203
outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;
4204
outStats.unusedRangeSizeMax = 0;
4205
}
4206
4207
static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
4208
{
4209
inoutStats.statistics.allocationCount++;
4210
inoutStats.statistics.allocationBytes += size;
4211
inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);
4212
inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);
4213
}
4214
4215
static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
4216
{
4217
inoutStats.unusedRangeCount++;
4218
inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);
4219
inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);
4220
}
4221
4222
static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)
4223
{
4224
VmaAddStatistics(inoutStats.statistics, src.statistics);
4225
inoutStats.unusedRangeCount += src.unusedRangeCount;
4226
inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);
4227
inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);
4228
inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);
4229
inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);
4230
}
4231
4232
#endif // _VMA_STATISTICS_FUNCTIONS
4233
4234
#ifndef _VMA_MUTEX_LOCK
4235
// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4236
struct VmaMutexLock
4237
{
4238
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLock)
4239
public:
4240
VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4241
m_pMutex(useMutex ? &mutex : VMA_NULL)
4242
{
4243
if (m_pMutex) { m_pMutex->Lock(); }
4244
}
4245
~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } }
4246
4247
private:
4248
VMA_MUTEX* m_pMutex;
4249
};
4250
4251
// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4252
struct VmaMutexLockRead
4253
{
4254
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockRead)
4255
public:
4256
VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4257
m_pMutex(useMutex ? &mutex : VMA_NULL)
4258
{
4259
if (m_pMutex) { m_pMutex->LockRead(); }
4260
}
4261
~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }
4262
4263
private:
4264
VMA_RW_MUTEX* m_pMutex;
4265
};
4266
4267
// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4268
struct VmaMutexLockWrite
4269
{
4270
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockWrite)
4271
public:
4272
VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)
4273
: m_pMutex(useMutex ? &mutex : VMA_NULL)
4274
{
4275
if (m_pMutex) { m_pMutex->LockWrite(); }
4276
}
4277
~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }
4278
4279
private:
4280
VMA_RW_MUTEX* m_pMutex;
4281
};
4282
4283
#if VMA_DEBUG_GLOBAL_MUTEX
4284
static VMA_MUTEX gDebugGlobalMutex;
4285
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4286
#else
4287
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4288
#endif
4289
#endif // _VMA_MUTEX_LOCK
4290
4291
#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
4292
// An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
4293
template<typename AtomicT>
4294
struct AtomicTransactionalIncrement
4295
{
4296
public:
4297
using T = decltype(AtomicT().load());
4298
4299
~AtomicTransactionalIncrement()
4300
{
4301
if(m_Atomic)
4302
--(*m_Atomic);
4303
}
4304
4305
void Commit() { m_Atomic = VMA_NULL; }
4306
T Increment(AtomicT* atomic)
4307
{
4308
m_Atomic = atomic;
4309
return m_Atomic->fetch_add(1);
4310
}
4311
4312
private:
4313
AtomicT* m_Atomic = VMA_NULL;
4314
};
4315
#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
4316
4317
#ifndef _VMA_STL_ALLOCATOR
4318
// STL-compatible allocator.
4319
template<typename T>
4320
struct VmaStlAllocator
4321
{
4322
const VkAllocationCallbacks* const m_pCallbacks;
4323
typedef T value_type;
4324
4325
VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}
4326
template<typename U>
4327
VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}
4328
VmaStlAllocator(const VmaStlAllocator&) = default;
4329
VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;
4330
4331
T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4332
void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4333
4334
template<typename U>
4335
bool operator==(const VmaStlAllocator<U>& rhs) const
4336
{
4337
return m_pCallbacks == rhs.m_pCallbacks;
4338
}
4339
template<typename U>
4340
bool operator!=(const VmaStlAllocator<U>& rhs) const
4341
{
4342
return m_pCallbacks != rhs.m_pCallbacks;
4343
}
4344
};
4345
#endif // _VMA_STL_ALLOCATOR
4346
4347
#ifndef _VMA_VECTOR
4348
/* Class with interface compatible with subset of std::vector.
4349
T must be POD because constructors and destructors are not called and memcpy is
4350
used for these objects. */
4351
template<typename T, typename AllocatorT>
4352
class VmaVector
4353
{
4354
public:
4355
typedef T value_type;
4356
typedef T* iterator;
4357
typedef const T* const_iterator;
4358
4359
VmaVector(const AllocatorT& allocator);
4360
VmaVector(size_t count, const AllocatorT& allocator);
4361
// This version of the constructor is here for compatibility with pre-C++14 std::vector.
4362
// value is unused.
4363
VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}
4364
VmaVector(const VmaVector<T, AllocatorT>& src);
4365
VmaVector& operator=(const VmaVector& rhs);
4366
~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }
4367
4368
bool empty() const { return m_Count == 0; }
4369
size_t size() const { return m_Count; }
4370
T* data() { return m_pArray; }
4371
T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
4372
T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
4373
const T* data() const { return m_pArray; }
4374
const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
4375
const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
4376
4377
iterator begin() { return m_pArray; }
4378
iterator end() { return m_pArray + m_Count; }
4379
const_iterator cbegin() const { return m_pArray; }
4380
const_iterator cend() const { return m_pArray + m_Count; }
4381
const_iterator begin() const { return cbegin(); }
4382
const_iterator end() const { return cend(); }
4383
4384
void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
4385
void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
4386
void push_front(const T& src) { insert(0, src); }
4387
4388
void push_back(const T& src);
4389
void reserve(size_t newCapacity, bool freeMemory = false);
4390
void resize(size_t newCount);
4391
void clear() { resize(0); }
4392
void shrink_to_fit();
4393
void insert(size_t index, const T& src);
4394
void remove(size_t index);
4395
4396
T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4397
const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4398
4399
private:
4400
AllocatorT m_Allocator;
4401
T* m_pArray;
4402
size_t m_Count;
4403
size_t m_Capacity;
4404
};
4405
4406
#ifndef _VMA_VECTOR_FUNCTIONS
4407
template<typename T, typename AllocatorT>
4408
VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)
4409
: m_Allocator(allocator),
4410
m_pArray(VMA_NULL),
4411
m_Count(0),
4412
m_Capacity(0) {}
4413
4414
template<typename T, typename AllocatorT>
4415
VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)
4416
: m_Allocator(allocator),
4417
m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4418
m_Count(count),
4419
m_Capacity(count) {}
4420
4421
template<typename T, typename AllocatorT>
4422
VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)
4423
: m_Allocator(src.m_Allocator),
4424
m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4425
m_Count(src.m_Count),
4426
m_Capacity(src.m_Count)
4427
{
4428
if (m_Count != 0)
4429
{
4430
memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4431
}
4432
}
4433
4434
template<typename T, typename AllocatorT>
4435
VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)
4436
{
4437
if (&rhs != this)
4438
{
4439
resize(rhs.m_Count);
4440
if (m_Count != 0)
4441
{
4442
memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4443
}
4444
}
4445
return *this;
4446
}
4447
4448
template<typename T, typename AllocatorT>
4449
void VmaVector<T, AllocatorT>::push_back(const T& src)
4450
{
4451
const size_t newIndex = size();
4452
resize(newIndex + 1);
4453
m_pArray[newIndex] = src;
4454
}
4455
4456
template<typename T, typename AllocatorT>
4457
void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)
4458
{
4459
newCapacity = VMA_MAX(newCapacity, m_Count);
4460
4461
if ((newCapacity < m_Capacity) && !freeMemory)
4462
{
4463
newCapacity = m_Capacity;
4464
}
4465
4466
if (newCapacity != m_Capacity)
4467
{
4468
T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4469
if (m_Count != 0)
4470
{
4471
memcpy(newArray, m_pArray, m_Count * sizeof(T));
4472
}
4473
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4474
m_Capacity = newCapacity;
4475
m_pArray = newArray;
4476
}
4477
}
4478
4479
template<typename T, typename AllocatorT>
4480
void VmaVector<T, AllocatorT>::resize(size_t newCount)
4481
{
4482
size_t newCapacity = m_Capacity;
4483
if (newCount > m_Capacity)
4484
{
4485
newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4486
}
4487
4488
if (newCapacity != m_Capacity)
4489
{
4490
T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4491
const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4492
if (elementsToCopy != 0)
4493
{
4494
memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4495
}
4496
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4497
m_Capacity = newCapacity;
4498
m_pArray = newArray;
4499
}
4500
4501
m_Count = newCount;
4502
}
4503
4504
template<typename T, typename AllocatorT>
4505
void VmaVector<T, AllocatorT>::shrink_to_fit()
4506
{
4507
if (m_Capacity > m_Count)
4508
{
4509
T* newArray = VMA_NULL;
4510
if (m_Count > 0)
4511
{
4512
newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
4513
memcpy(newArray, m_pArray, m_Count * sizeof(T));
4514
}
4515
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4516
m_Capacity = m_Count;
4517
m_pArray = newArray;
4518
}
4519
}
4520
4521
template<typename T, typename AllocatorT>
4522
void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)
4523
{
4524
VMA_HEAVY_ASSERT(index <= m_Count);
4525
const size_t oldCount = size();
4526
resize(oldCount + 1);
4527
if (index < oldCount)
4528
{
4529
memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4530
}
4531
m_pArray[index] = src;
4532
}
4533
4534
template<typename T, typename AllocatorT>
4535
void VmaVector<T, AllocatorT>::remove(size_t index)
4536
{
4537
VMA_HEAVY_ASSERT(index < m_Count);
4538
const size_t oldCount = size();
4539
if (index < oldCount - 1)
4540
{
4541
memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4542
}
4543
resize(oldCount - 1);
4544
}
4545
#endif // _VMA_VECTOR_FUNCTIONS
4546
4547
template<typename T, typename allocatorT>
4548
static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4549
{
4550
vec.insert(index, item);
4551
}
4552
4553
template<typename T, typename allocatorT>
4554
static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4555
{
4556
vec.remove(index);
4557
}
4558
#endif // _VMA_VECTOR
4559
4560
#ifndef _VMA_SMALL_VECTOR
4561
/*
4562
This is a vector (a variable-sized array), optimized for the case when the array is small.
4563
4564
It contains some number of elements in-place, which allows it to avoid heap allocation
4565
when the actual number of elements is below that threshold. This allows normal "small"
4566
cases to be fast without losing generality for large inputs.
4567
*/
4568
template<typename T, typename AllocatorT, size_t N>
4569
class VmaSmallVector
4570
{
4571
public:
4572
typedef T value_type;
4573
typedef T* iterator;
4574
4575
VmaSmallVector(const AllocatorT& allocator);
4576
VmaSmallVector(size_t count, const AllocatorT& allocator);
4577
template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4578
VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4579
template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4580
VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4581
~VmaSmallVector() = default;
4582
4583
bool empty() const { return m_Count == 0; }
4584
size_t size() const { return m_Count; }
4585
T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
4586
T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
4587
T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
4588
const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
4589
const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
4590
const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
4591
4592
iterator begin() { return data(); }
4593
iterator end() { return data() + m_Count; }
4594
4595
void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
4596
void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
4597
void push_front(const T& src) { insert(0, src); }
4598
4599
void push_back(const T& src);
4600
void resize(size_t newCount, bool freeMemory = false);
4601
void clear(bool freeMemory = false);
4602
void insert(size_t index, const T& src);
4603
void remove(size_t index);
4604
4605
T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4606
const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4607
4608
private:
4609
size_t m_Count;
4610
T m_StaticArray[N]; // Used when m_Size <= N
4611
VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
4612
};
4613
4614
#ifndef _VMA_SMALL_VECTOR_FUNCTIONS
4615
template<typename T, typename AllocatorT, size_t N>
4616
VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)
4617
: m_Count(0),
4618
m_DynamicArray(allocator) {}
4619
4620
template<typename T, typename AllocatorT, size_t N>
4621
VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)
4622
: m_Count(count),
4623
m_DynamicArray(count > N ? count : 0, allocator) {}
4624
4625
template<typename T, typename AllocatorT, size_t N>
4626
void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)
4627
{
4628
const size_t newIndex = size();
4629
resize(newIndex + 1);
4630
data()[newIndex] = src;
4631
}
4632
4633
template<typename T, typename AllocatorT, size_t N>
4634
void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)
4635
{
4636
if (newCount > N && m_Count > N)
4637
{
4638
// Any direction, staying in m_DynamicArray
4639
m_DynamicArray.resize(newCount);
4640
if (freeMemory)
4641
{
4642
m_DynamicArray.shrink_to_fit();
4643
}
4644
}
4645
else if (newCount > N && m_Count <= N)
4646
{
4647
// Growing, moving from m_StaticArray to m_DynamicArray
4648
m_DynamicArray.resize(newCount);
4649
if (m_Count > 0)
4650
{
4651
memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
4652
}
4653
}
4654
else if (newCount <= N && m_Count > N)
4655
{
4656
// Shrinking, moving from m_DynamicArray to m_StaticArray
4657
if (newCount > 0)
4658
{
4659
memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
4660
}
4661
m_DynamicArray.resize(0);
4662
if (freeMemory)
4663
{
4664
m_DynamicArray.shrink_to_fit();
4665
}
4666
}
4667
else
4668
{
4669
// Any direction, staying in m_StaticArray - nothing to do here
4670
}
4671
m_Count = newCount;
4672
}
4673
4674
template<typename T, typename AllocatorT, size_t N>
4675
void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)
4676
{
4677
m_DynamicArray.clear();
4678
if (freeMemory)
4679
{
4680
m_DynamicArray.shrink_to_fit();
4681
}
4682
m_Count = 0;
4683
}
4684
4685
template<typename T, typename AllocatorT, size_t N>
4686
void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)
4687
{
4688
VMA_HEAVY_ASSERT(index <= m_Count);
4689
const size_t oldCount = size();
4690
resize(oldCount + 1);
4691
T* const dataPtr = data();
4692
if (index < oldCount)
4693
{
4694
// I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
4695
memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
4696
}
4697
dataPtr[index] = src;
4698
}
4699
4700
template<typename T, typename AllocatorT, size_t N>
4701
void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)
4702
{
4703
VMA_HEAVY_ASSERT(index < m_Count);
4704
const size_t oldCount = size();
4705
if (index < oldCount - 1)
4706
{
4707
// I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
4708
T* const dataPtr = data();
4709
memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
4710
}
4711
resize(oldCount - 1);
4712
}
4713
#endif // _VMA_SMALL_VECTOR_FUNCTIONS
4714
#endif // _VMA_SMALL_VECTOR
4715
4716
#ifndef _VMA_POOL_ALLOCATOR
4717
/*
4718
Allocator for objects of type T using a list of arrays (pools) to speed up
4719
allocation. Number of elements that can be allocated is not bounded because
4720
allocator can create multiple blocks.
4721
*/
4722
template<typename T>
4723
class VmaPoolAllocator
4724
{
4725
VMA_CLASS_NO_COPY_NO_MOVE(VmaPoolAllocator)
4726
public:
4727
VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
4728
~VmaPoolAllocator();
4729
template<typename... Types> T* Alloc(Types&&... args);
4730
void Free(T* ptr);
4731
4732
private:
4733
union Item
4734
{
4735
uint32_t NextFreeIndex;
4736
alignas(T) char Value[sizeof(T)];
4737
};
4738
struct ItemBlock
4739
{
4740
Item* pItems;
4741
uint32_t Capacity;
4742
uint32_t FirstFreeIndex;
4743
};
4744
4745
const VkAllocationCallbacks* m_pAllocationCallbacks;
4746
const uint32_t m_FirstBlockCapacity;
4747
VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;
4748
4749
ItemBlock& CreateNewBlock();
4750
};
4751
4752
#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS
4753
template<typename T>
4754
VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)
4755
: m_pAllocationCallbacks(pAllocationCallbacks),
4756
m_FirstBlockCapacity(firstBlockCapacity),
4757
m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4758
{
4759
VMA_ASSERT(m_FirstBlockCapacity > 1);
4760
}
4761
4762
template<typename T>
4763
VmaPoolAllocator<T>::~VmaPoolAllocator()
4764
{
4765
for (size_t i = m_ItemBlocks.size(); i--;)
4766
vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
4767
m_ItemBlocks.clear();
4768
}
4769
4770
template<typename T>
4771
template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
4772
{
4773
for (size_t i = m_ItemBlocks.size(); i--; )
4774
{
4775
ItemBlock& block = m_ItemBlocks[i];
4776
// This block has some free items: Use first one.
4777
if (block.FirstFreeIndex != UINT32_MAX)
4778
{
4779
Item* const pItem = &block.pItems[block.FirstFreeIndex];
4780
block.FirstFreeIndex = pItem->NextFreeIndex;
4781
T* result = (T*)&pItem->Value;
4782
new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
4783
return result;
4784
}
4785
}
4786
4787
// No block has free item: Create new one and use it.
4788
ItemBlock& newBlock = CreateNewBlock();
4789
Item* const pItem = &newBlock.pItems[0];
4790
newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4791
T* result = (T*)&pItem->Value;
4792
new(result) T(std::forward<Types>(args)...); // Explicit constructor call.
4793
return result;
4794
}
4795
4796
template<typename T>
4797
void VmaPoolAllocator<T>::Free(T* ptr)
4798
{
4799
// Search all memory blocks to find ptr.
4800
for (size_t i = m_ItemBlocks.size(); i--; )
4801
{
4802
ItemBlock& block = m_ItemBlocks[i];
4803
4804
// Casting to union.
4805
Item* pItemPtr;
4806
memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4807
4808
// Check if pItemPtr is in address range of this block.
4809
if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
4810
{
4811
ptr->~T(); // Explicit destructor call.
4812
const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4813
pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4814
block.FirstFreeIndex = index;
4815
return;
4816
}
4817
}
4818
VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4819
}
4820
4821
template<typename T>
4822
typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4823
{
4824
const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
4825
m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
4826
4827
const ItemBlock newBlock =
4828
{
4829
vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
4830
newBlockCapacity,
4831
0
4832
};
4833
4834
m_ItemBlocks.push_back(newBlock);
4835
4836
// Setup singly-linked list of all free items in this block.
4837
for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)
4838
newBlock.pItems[i].NextFreeIndex = i + 1;
4839
newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
4840
return m_ItemBlocks.back();
4841
}
4842
#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS
4843
#endif // _VMA_POOL_ALLOCATOR
4844
4845
#ifndef _VMA_RAW_LIST
4846
template<typename T>
4847
struct VmaListItem
4848
{
4849
VmaListItem* pPrev;
4850
VmaListItem* pNext;
4851
T Value;
4852
};
4853
4854
// Doubly linked list.
4855
template<typename T>
4856
class VmaRawList
4857
{
4858
VMA_CLASS_NO_COPY_NO_MOVE(VmaRawList)
4859
public:
4860
typedef VmaListItem<T> ItemType;
4861
4862
VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4863
// Intentionally not calling Clear, because that would be unnecessary
4864
// computations to return all items to m_ItemAllocator as free.
4865
~VmaRawList() = default;
4866
4867
size_t GetCount() const { return m_Count; }
4868
bool IsEmpty() const { return m_Count == 0; }
4869
4870
ItemType* Front() { return m_pFront; }
4871
ItemType* Back() { return m_pBack; }
4872
const ItemType* Front() const { return m_pFront; }
4873
const ItemType* Back() const { return m_pBack; }
4874
4875
ItemType* PushFront();
4876
ItemType* PushBack();
4877
ItemType* PushFront(const T& value);
4878
ItemType* PushBack(const T& value);
4879
void PopFront();
4880
void PopBack();
4881
4882
// Item can be null - it means PushBack.
4883
ItemType* InsertBefore(ItemType* pItem);
4884
// Item can be null - it means PushFront.
4885
ItemType* InsertAfter(ItemType* pItem);
4886
ItemType* InsertBefore(ItemType* pItem, const T& value);
4887
ItemType* InsertAfter(ItemType* pItem, const T& value);
4888
4889
void Clear();
4890
void Remove(ItemType* pItem);
4891
4892
private:
4893
const VkAllocationCallbacks* const m_pAllocationCallbacks;
4894
VmaPoolAllocator<ItemType> m_ItemAllocator;
4895
ItemType* m_pFront;
4896
ItemType* m_pBack;
4897
size_t m_Count;
4898
};
4899
4900
#ifndef _VMA_RAW_LIST_FUNCTIONS
4901
template<typename T>
4902
VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)
4903
: m_pAllocationCallbacks(pAllocationCallbacks),
4904
m_ItemAllocator(pAllocationCallbacks, 128),
4905
m_pFront(VMA_NULL),
4906
m_pBack(VMA_NULL),
4907
m_Count(0) {}
4908
4909
template<typename T>
4910
VmaListItem<T>* VmaRawList<T>::PushFront()
4911
{
4912
ItemType* const pNewItem = m_ItemAllocator.Alloc();
4913
pNewItem->pPrev = VMA_NULL;
4914
if (IsEmpty())
4915
{
4916
pNewItem->pNext = VMA_NULL;
4917
m_pFront = pNewItem;
4918
m_pBack = pNewItem;
4919
m_Count = 1;
4920
}
4921
else
4922
{
4923
pNewItem->pNext = m_pFront;
4924
m_pFront->pPrev = pNewItem;
4925
m_pFront = pNewItem;
4926
++m_Count;
4927
}
4928
return pNewItem;
4929
}
4930
4931
template<typename T>
4932
VmaListItem<T>* VmaRawList<T>::PushBack()
4933
{
4934
ItemType* const pNewItem = m_ItemAllocator.Alloc();
4935
pNewItem->pNext = VMA_NULL;
4936
if(IsEmpty())
4937
{
4938
pNewItem->pPrev = VMA_NULL;
4939
m_pFront = pNewItem;
4940
m_pBack = pNewItem;
4941
m_Count = 1;
4942
}
4943
else
4944
{
4945
pNewItem->pPrev = m_pBack;
4946
m_pBack->pNext = pNewItem;
4947
m_pBack = pNewItem;
4948
++m_Count;
4949
}
4950
return pNewItem;
4951
}
4952
4953
template<typename T>
4954
VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4955
{
4956
ItemType* const pNewItem = PushFront();
4957
pNewItem->Value = value;
4958
return pNewItem;
4959
}
4960
4961
template<typename T>
4962
VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4963
{
4964
ItemType* const pNewItem = PushBack();
4965
pNewItem->Value = value;
4966
return pNewItem;
4967
}
4968
4969
template<typename T>
4970
void VmaRawList<T>::PopFront()
4971
{
4972
VMA_HEAVY_ASSERT(m_Count > 0);
4973
ItemType* const pFrontItem = m_pFront;
4974
ItemType* const pNextItem = pFrontItem->pNext;
4975
if (pNextItem != VMA_NULL)
4976
{
4977
pNextItem->pPrev = VMA_NULL;
4978
}
4979
m_pFront = pNextItem;
4980
m_ItemAllocator.Free(pFrontItem);
4981
--m_Count;
4982
}
4983
4984
template<typename T>
4985
void VmaRawList<T>::PopBack()
4986
{
4987
VMA_HEAVY_ASSERT(m_Count > 0);
4988
ItemType* const pBackItem = m_pBack;
4989
ItemType* const pPrevItem = pBackItem->pPrev;
4990
if(pPrevItem != VMA_NULL)
4991
{
4992
pPrevItem->pNext = VMA_NULL;
4993
}
4994
m_pBack = pPrevItem;
4995
m_ItemAllocator.Free(pBackItem);
4996
--m_Count;
4997
}
4998
4999
template<typename T>
5000
void VmaRawList<T>::Clear()
5001
{
5002
if (IsEmpty() == false)
5003
{
5004
ItemType* pItem = m_pBack;
5005
while (pItem != VMA_NULL)
5006
{
5007
ItemType* const pPrevItem = pItem->pPrev;
5008
m_ItemAllocator.Free(pItem);
5009
pItem = pPrevItem;
5010
}
5011
m_pFront = VMA_NULL;
5012
m_pBack = VMA_NULL;
5013
m_Count = 0;
5014
}
5015
}
5016
5017
template<typename T>
5018
void VmaRawList<T>::Remove(ItemType* pItem)
5019
{
5020
VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5021
VMA_HEAVY_ASSERT(m_Count > 0);
5022
5023
if(pItem->pPrev != VMA_NULL)
5024
{
5025
pItem->pPrev->pNext = pItem->pNext;
5026
}
5027
else
5028
{
5029
VMA_HEAVY_ASSERT(m_pFront == pItem);
5030
m_pFront = pItem->pNext;
5031
}
5032
5033
if(pItem->pNext != VMA_NULL)
5034
{
5035
pItem->pNext->pPrev = pItem->pPrev;
5036
}
5037
else
5038
{
5039
VMA_HEAVY_ASSERT(m_pBack == pItem);
5040
m_pBack = pItem->pPrev;
5041
}
5042
5043
m_ItemAllocator.Free(pItem);
5044
--m_Count;
5045
}
5046
5047
template<typename T>
5048
VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5049
{
5050
if(pItem != VMA_NULL)
5051
{
5052
ItemType* const prevItem = pItem->pPrev;
5053
ItemType* const newItem = m_ItemAllocator.Alloc();
5054
newItem->pPrev = prevItem;
5055
newItem->pNext = pItem;
5056
pItem->pPrev = newItem;
5057
if(prevItem != VMA_NULL)
5058
{
5059
prevItem->pNext = newItem;
5060
}
5061
else
5062
{
5063
VMA_HEAVY_ASSERT(m_pFront == pItem);
5064
m_pFront = newItem;
5065
}
5066
++m_Count;
5067
return newItem;
5068
}
5069
else
5070
return PushBack();
5071
}
5072
5073
template<typename T>
5074
VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5075
{
5076
if(pItem != VMA_NULL)
5077
{
5078
ItemType* const nextItem = pItem->pNext;
5079
ItemType* const newItem = m_ItemAllocator.Alloc();
5080
newItem->pNext = nextItem;
5081
newItem->pPrev = pItem;
5082
pItem->pNext = newItem;
5083
if(nextItem != VMA_NULL)
5084
{
5085
nextItem->pPrev = newItem;
5086
}
5087
else
5088
{
5089
VMA_HEAVY_ASSERT(m_pBack == pItem);
5090
m_pBack = newItem;
5091
}
5092
++m_Count;
5093
return newItem;
5094
}
5095
else
5096
return PushFront();
5097
}
5098
5099
template<typename T>
5100
VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5101
{
5102
ItemType* const newItem = InsertBefore(pItem);
5103
newItem->Value = value;
5104
return newItem;
5105
}
5106
5107
template<typename T>
5108
VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5109
{
5110
ItemType* const newItem = InsertAfter(pItem);
5111
newItem->Value = value;
5112
return newItem;
5113
}
5114
#endif // _VMA_RAW_LIST_FUNCTIONS
5115
#endif // _VMA_RAW_LIST
5116
5117
#ifndef _VMA_LIST
5118
template<typename T, typename AllocatorT>
5119
class VmaList
5120
{
5121
VMA_CLASS_NO_COPY_NO_MOVE(VmaList)
5122
public:
5123
class reverse_iterator;
5124
class const_iterator;
5125
class const_reverse_iterator;
5126
5127
class iterator
5128
{
5129
friend class const_iterator;
5130
friend class VmaList<T, AllocatorT>;
5131
public:
5132
iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
5133
iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5134
5135
T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
5136
T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
5137
5138
bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
5139
bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
5140
5141
iterator operator++(int) { iterator result = *this; ++*this; return result; }
5142
iterator operator--(int) { iterator result = *this; --*this; return result; }
5143
5144
iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
5145
iterator& operator--();
5146
5147
private:
5148
VmaRawList<T>* m_pList;
5149
VmaListItem<T>* m_pItem;
5150
5151
iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
5152
};
5153
class reverse_iterator
5154
{
5155
friend class const_reverse_iterator;
5156
friend class VmaList<T, AllocatorT>;
5157
public:
5158
reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
5159
reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5160
5161
T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
5162
T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
5163
5164
bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
5165
bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
5166
5167
reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }
5168
reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }
5169
5170
reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
5171
reverse_iterator& operator--();
5172
5173
private:
5174
VmaRawList<T>* m_pList;
5175
VmaListItem<T>* m_pItem;
5176
5177
reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
5178
};
5179
class const_iterator
5180
{
5181
friend class VmaList<T, AllocatorT>;
5182
public:
5183
const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
5184
const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5185
const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5186
5187
iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
5188
5189
const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
5190
const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
5191
5192
bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
5193
bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
5194
5195
const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }
5196
const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }
5197
5198
const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
5199
const_iterator& operator--();
5200
5201
private:
5202
const VmaRawList<T>* m_pList;
5203
const VmaListItem<T>* m_pItem;
5204
5205
const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
5206
};
5207
class const_reverse_iterator
5208
{
5209
friend class VmaList<T, AllocatorT>;
5210
public:
5211
const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
5212
const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5213
const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
5214
5215
reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
5216
5217
const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
5218
const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
5219
5220
bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
5221
bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
5222
5223
const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }
5224
const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }
5225
5226
const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
5227
const_reverse_iterator& operator--();
5228
5229
private:
5230
const VmaRawList<T>* m_pList;
5231
const VmaListItem<T>* m_pItem;
5232
5233
const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
5234
};
5235
5236
VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}
5237
5238
bool empty() const { return m_RawList.IsEmpty(); }
5239
size_t size() const { return m_RawList.GetCount(); }
5240
5241
iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
5242
iterator end() { return iterator(&m_RawList, VMA_NULL); }
5243
5244
const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
5245
const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5246
5247
const_iterator begin() const { return cbegin(); }
5248
const_iterator end() const { return cend(); }
5249
5250
reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }
5251
reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }
5252
5253
const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }
5254
const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }
5255
5256
const_reverse_iterator rbegin() const { return crbegin(); }
5257
const_reverse_iterator rend() const { return crend(); }
5258
5259
void push_back(const T& value) { m_RawList.PushBack(value); }
5260
iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5261
5262
void clear() { m_RawList.Clear(); }
5263
void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
5264
5265
private:
5266
VmaRawList<T> m_RawList;
5267
};
5268
5269
#ifndef _VMA_LIST_FUNCTIONS
5270
template<typename T, typename AllocatorT>
5271
typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()
5272
{
5273
if (m_pItem != VMA_NULL)
5274
{
5275
m_pItem = m_pItem->pPrev;
5276
}
5277
else
5278
{
5279
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5280
m_pItem = m_pList->Back();
5281
}
5282
return *this;
5283
}
5284
5285
template<typename T, typename AllocatorT>
5286
typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()
5287
{
5288
if (m_pItem != VMA_NULL)
5289
{
5290
m_pItem = m_pItem->pNext;
5291
}
5292
else
5293
{
5294
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5295
m_pItem = m_pList->Front();
5296
}
5297
return *this;
5298
}
5299
5300
template<typename T, typename AllocatorT>
5301
typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()
5302
{
5303
if (m_pItem != VMA_NULL)
5304
{
5305
m_pItem = m_pItem->pPrev;
5306
}
5307
else
5308
{
5309
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5310
m_pItem = m_pList->Back();
5311
}
5312
return *this;
5313
}
5314
5315
template<typename T, typename AllocatorT>
5316
typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()
5317
{
5318
if (m_pItem != VMA_NULL)
5319
{
5320
m_pItem = m_pItem->pNext;
5321
}
5322
else
5323
{
5324
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5325
m_pItem = m_pList->Back();
5326
}
5327
return *this;
5328
}
5329
#endif // _VMA_LIST_FUNCTIONS
5330
#endif // _VMA_LIST
5331
5332
#ifndef _VMA_INTRUSIVE_LINKED_LIST
5333
/*
5334
Expected interface of ItemTypeTraits:
5335
struct MyItemTypeTraits
5336
{
5337
typedef MyItem ItemType;
5338
static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
5339
static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
5340
static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
5341
static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
5342
};
5343
*/
5344
template<typename ItemTypeTraits>
5345
class VmaIntrusiveLinkedList
5346
{
5347
public:
5348
typedef typename ItemTypeTraits::ItemType ItemType;
5349
static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
5350
static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
5351
5352
// Movable, not copyable.
5353
VmaIntrusiveLinkedList() = default;
5354
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src);
5355
VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;
5356
VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src);
5357
VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;
5358
~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }
5359
5360
size_t GetCount() const { return m_Count; }
5361
bool IsEmpty() const { return m_Count == 0; }
5362
ItemType* Front() { return m_Front; }
5363
ItemType* Back() { return m_Back; }
5364
const ItemType* Front() const { return m_Front; }
5365
const ItemType* Back() const { return m_Back; }
5366
5367
void PushBack(ItemType* item);
5368
void PushFront(ItemType* item);
5369
ItemType* PopBack();
5370
ItemType* PopFront();
5371
5372
// MyItem can be null - it means PushBack.
5373
void InsertBefore(ItemType* existingItem, ItemType* newItem);
5374
// MyItem can be null - it means PushFront.
5375
void InsertAfter(ItemType* existingItem, ItemType* newItem);
5376
void Remove(ItemType* item);
5377
void RemoveAll();
5378
5379
private:
5380
ItemType* m_Front = VMA_NULL;
5381
ItemType* m_Back = VMA_NULL;
5382
size_t m_Count = 0;
5383
};
5384
5385
#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5386
template<typename ItemTypeTraits>
5387
VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src)
5388
: m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
5389
{
5390
src.m_Front = src.m_Back = VMA_NULL;
5391
src.m_Count = 0;
5392
}
5393
5394
template<typename ItemTypeTraits>
5395
VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src)
5396
{
5397
if (&src != this)
5398
{
5399
VMA_HEAVY_ASSERT(IsEmpty());
5400
m_Front = src.m_Front;
5401
m_Back = src.m_Back;
5402
m_Count = src.m_Count;
5403
src.m_Front = src.m_Back = VMA_NULL;
5404
src.m_Count = 0;
5405
}
5406
return *this;
5407
}
5408
5409
template<typename ItemTypeTraits>
5410
void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)
5411
{
5412
VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5413
if (IsEmpty())
5414
{
5415
m_Front = item;
5416
m_Back = item;
5417
m_Count = 1;
5418
}
5419
else
5420
{
5421
ItemTypeTraits::AccessPrev(item) = m_Back;
5422
ItemTypeTraits::AccessNext(m_Back) = item;
5423
m_Back = item;
5424
++m_Count;
5425
}
5426
}
5427
5428
template<typename ItemTypeTraits>
5429
void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)
5430
{
5431
VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5432
if (IsEmpty())
5433
{
5434
m_Front = item;
5435
m_Back = item;
5436
m_Count = 1;
5437
}
5438
else
5439
{
5440
ItemTypeTraits::AccessNext(item) = m_Front;
5441
ItemTypeTraits::AccessPrev(m_Front) = item;
5442
m_Front = item;
5443
++m_Count;
5444
}
5445
}
5446
5447
template<typename ItemTypeTraits>
5448
typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()
5449
{
5450
VMA_HEAVY_ASSERT(m_Count > 0);
5451
ItemType* const backItem = m_Back;
5452
ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
5453
if (prevItem != VMA_NULL)
5454
{
5455
ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
5456
}
5457
m_Back = prevItem;
5458
--m_Count;
5459
ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
5460
ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
5461
return backItem;
5462
}
5463
5464
template<typename ItemTypeTraits>
5465
typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()
5466
{
5467
VMA_HEAVY_ASSERT(m_Count > 0);
5468
ItemType* const frontItem = m_Front;
5469
ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
5470
if (nextItem != VMA_NULL)
5471
{
5472
ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
5473
}
5474
m_Front = nextItem;
5475
--m_Count;
5476
ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
5477
ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
5478
return frontItem;
5479
}
5480
5481
template<typename ItemTypeTraits>
5482
void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)
5483
{
5484
VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5485
if (existingItem != VMA_NULL)
5486
{
5487
ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
5488
ItemTypeTraits::AccessPrev(newItem) = prevItem;
5489
ItemTypeTraits::AccessNext(newItem) = existingItem;
5490
ItemTypeTraits::AccessPrev(existingItem) = newItem;
5491
if (prevItem != VMA_NULL)
5492
{
5493
ItemTypeTraits::AccessNext(prevItem) = newItem;
5494
}
5495
else
5496
{
5497
VMA_HEAVY_ASSERT(m_Front == existingItem);
5498
m_Front = newItem;
5499
}
5500
++m_Count;
5501
}
5502
else
5503
PushBack(newItem);
5504
}
5505
5506
template<typename ItemTypeTraits>
5507
void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)
5508
{
5509
VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5510
if (existingItem != VMA_NULL)
5511
{
5512
ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
5513
ItemTypeTraits::AccessNext(newItem) = nextItem;
5514
ItemTypeTraits::AccessPrev(newItem) = existingItem;
5515
ItemTypeTraits::AccessNext(existingItem) = newItem;
5516
if (nextItem != VMA_NULL)
5517
{
5518
ItemTypeTraits::AccessPrev(nextItem) = newItem;
5519
}
5520
else
5521
{
5522
VMA_HEAVY_ASSERT(m_Back == existingItem);
5523
m_Back = newItem;
5524
}
5525
++m_Count;
5526
}
5527
else
5528
return PushFront(newItem);
5529
}
5530
5531
template<typename ItemTypeTraits>
5532
void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)
5533
{
5534
VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
5535
if (ItemTypeTraits::GetPrev(item) != VMA_NULL)
5536
{
5537
ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
5538
}
5539
else
5540
{
5541
VMA_HEAVY_ASSERT(m_Front == item);
5542
m_Front = ItemTypeTraits::GetNext(item);
5543
}
5544
5545
if (ItemTypeTraits::GetNext(item) != VMA_NULL)
5546
{
5547
ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
5548
}
5549
else
5550
{
5551
VMA_HEAVY_ASSERT(m_Back == item);
5552
m_Back = ItemTypeTraits::GetPrev(item);
5553
}
5554
ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5555
ItemTypeTraits::AccessNext(item) = VMA_NULL;
5556
--m_Count;
5557
}
5558
5559
template<typename ItemTypeTraits>
5560
void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()
5561
{
5562
if (!IsEmpty())
5563
{
5564
ItemType* item = m_Back;
5565
while (item != VMA_NULL)
5566
{
5567
ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
5568
ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5569
ItemTypeTraits::AccessNext(item) = VMA_NULL;
5570
item = prevItem;
5571
}
5572
m_Front = VMA_NULL;
5573
m_Back = VMA_NULL;
5574
m_Count = 0;
5575
}
5576
}
5577
#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5578
#endif // _VMA_INTRUSIVE_LINKED_LIST
5579
5580
#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED
5581
class VmaStringBuilder
5582
{
5583
public:
5584
VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}
5585
~VmaStringBuilder() = default;
5586
5587
size_t GetLength() const { return m_Data.size(); }
5588
const char* GetData() const { return m_Data.data(); }
5589
void AddNewLine() { Add('\n'); }
5590
void Add(char ch) { m_Data.push_back(ch); }
5591
5592
void Add(const char* pStr);
5593
void AddNumber(uint32_t num);
5594
void AddNumber(uint64_t num);
5595
void AddPointer(const void* ptr);
5596
5597
private:
5598
VmaVector<char, VmaStlAllocator<char>> m_Data;
5599
};
5600
5601
#ifndef _VMA_STRING_BUILDER_FUNCTIONS
5602
void VmaStringBuilder::Add(const char* pStr)
5603
{
5604
const size_t strLen = strlen(pStr);
5605
if (strLen > 0)
5606
{
5607
const size_t oldCount = m_Data.size();
5608
m_Data.resize(oldCount + strLen);
5609
memcpy(m_Data.data() + oldCount, pStr, strLen);
5610
}
5611
}
5612
5613
void VmaStringBuilder::AddNumber(uint32_t num)
5614
{
5615
char buf[11];
5616
buf[10] = '\0';
5617
char* p = &buf[10];
5618
do
5619
{
5620
*--p = '0' + (char)(num % 10);
5621
num /= 10;
5622
} while (num);
5623
Add(p);
5624
}
5625
5626
void VmaStringBuilder::AddNumber(uint64_t num)
5627
{
5628
char buf[21];
5629
buf[20] = '\0';
5630
char* p = &buf[20];
5631
do
5632
{
5633
*--p = '0' + (char)(num % 10);
5634
num /= 10;
5635
} while (num);
5636
Add(p);
5637
}
5638
5639
void VmaStringBuilder::AddPointer(const void* ptr)
5640
{
5641
char buf[21];
5642
VmaPtrToStr(buf, sizeof(buf), ptr);
5643
Add(buf);
5644
}
5645
#endif //_VMA_STRING_BUILDER_FUNCTIONS
5646
#endif // _VMA_STRING_BUILDER
5647
5648
#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED
5649
/*
5650
Allows to conveniently build a correct JSON document to be written to the
5651
VmaStringBuilder passed to the constructor.
5652
*/
5653
class VmaJsonWriter
5654
{
5655
VMA_CLASS_NO_COPY_NO_MOVE(VmaJsonWriter)
5656
public:
5657
// sb - string builder to write the document to. Must remain alive for the whole lifetime of this object.
5658
VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
5659
~VmaJsonWriter();
5660
5661
// Begins object by writing "{".
5662
// Inside an object, you must call pairs of WriteString and a value, e.g.:
5663
// j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();
5664
// Will write: { "A": 1, "B": 2 }
5665
void BeginObject(bool singleLine = false);
5666
// Ends object by writing "}".
5667
void EndObject();
5668
5669
// Begins array by writing "[".
5670
// Inside an array, you can write a sequence of any values.
5671
void BeginArray(bool singleLine = false);
5672
// Ends array by writing "[".
5673
void EndArray();
5674
5675
// Writes a string value inside "".
5676
// pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped.
5677
void WriteString(const char* pStr);
5678
5679
// Begins writing a string value.
5680
// Call BeginString, ContinueString, ContinueString, ..., EndString instead of
5681
// WriteString to conveniently build the string content incrementally, made of
5682
// parts including numbers.
5683
void BeginString(const char* pStr = VMA_NULL);
5684
// Posts next part of an open string.
5685
void ContinueString(const char* pStr);
5686
// Posts next part of an open string. The number is converted to decimal characters.
5687
void ContinueString(uint32_t n);
5688
void ContinueString(uint64_t n);
5689
// Posts next part of an open string. Pointer value is converted to characters
5690
// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
5691
void ContinueString_Pointer(const void* ptr);
5692
// Ends writing a string value by writing '"'.
5693
void EndString(const char* pStr = VMA_NULL);
5694
5695
// Writes a number value.
5696
void WriteNumber(uint32_t n);
5697
void WriteNumber(uint64_t n);
5698
// Writes a boolean value - false or true.
5699
void WriteBool(bool b);
5700
// Writes a null value.
5701
void WriteNull();
5702
5703
private:
5704
enum COLLECTION_TYPE
5705
{
5706
COLLECTION_TYPE_OBJECT,
5707
COLLECTION_TYPE_ARRAY,
5708
};
5709
struct StackItem
5710
{
5711
COLLECTION_TYPE type;
5712
uint32_t valueCount;
5713
bool singleLineMode;
5714
};
5715
5716
static const char* const INDENT;
5717
5718
VmaStringBuilder& m_SB;
5719
VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
5720
bool m_InsideString;
5721
5722
void BeginValue(bool isString);
5723
void WriteIndent(bool oneLess = false);
5724
};
5725
const char* const VmaJsonWriter::INDENT = " ";
5726
5727
#ifndef _VMA_JSON_WRITER_FUNCTIONS
5728
VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)
5729
: m_SB(sb),
5730
m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
5731
m_InsideString(false) {}
5732
5733
VmaJsonWriter::~VmaJsonWriter()
5734
{
5735
VMA_ASSERT(!m_InsideString);
5736
VMA_ASSERT(m_Stack.empty());
5737
}
5738
5739
void VmaJsonWriter::BeginObject(bool singleLine)
5740
{
5741
VMA_ASSERT(!m_InsideString);
5742
5743
BeginValue(false);
5744
m_SB.Add('{');
5745
5746
StackItem item;
5747
item.type = COLLECTION_TYPE_OBJECT;
5748
item.valueCount = 0;
5749
item.singleLineMode = singleLine;
5750
m_Stack.push_back(item);
5751
}
5752
5753
void VmaJsonWriter::EndObject()
5754
{
5755
VMA_ASSERT(!m_InsideString);
5756
5757
WriteIndent(true);
5758
m_SB.Add('}');
5759
5760
VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
5761
m_Stack.pop_back();
5762
}
5763
5764
void VmaJsonWriter::BeginArray(bool singleLine)
5765
{
5766
VMA_ASSERT(!m_InsideString);
5767
5768
BeginValue(false);
5769
m_SB.Add('[');
5770
5771
StackItem item;
5772
item.type = COLLECTION_TYPE_ARRAY;
5773
item.valueCount = 0;
5774
item.singleLineMode = singleLine;
5775
m_Stack.push_back(item);
5776
}
5777
5778
void VmaJsonWriter::EndArray()
5779
{
5780
VMA_ASSERT(!m_InsideString);
5781
5782
WriteIndent(true);
5783
m_SB.Add(']');
5784
5785
VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
5786
m_Stack.pop_back();
5787
}
5788
5789
void VmaJsonWriter::WriteString(const char* pStr)
5790
{
5791
BeginString(pStr);
5792
EndString();
5793
}
5794
5795
void VmaJsonWriter::BeginString(const char* pStr)
5796
{
5797
VMA_ASSERT(!m_InsideString);
5798
5799
BeginValue(true);
5800
m_SB.Add('"');
5801
m_InsideString = true;
5802
if (pStr != VMA_NULL && pStr[0] != '\0')
5803
{
5804
ContinueString(pStr);
5805
}
5806
}
5807
5808
void VmaJsonWriter::ContinueString(const char* pStr)
5809
{
5810
VMA_ASSERT(m_InsideString);
5811
5812
const size_t strLen = strlen(pStr);
5813
for (size_t i = 0; i < strLen; ++i)
5814
{
5815
char ch = pStr[i];
5816
if (ch == '\\')
5817
{
5818
m_SB.Add("\\\\");
5819
}
5820
else if (ch == '"')
5821
{
5822
m_SB.Add("\\\"");
5823
}
5824
else if ((uint8_t)ch >= 32)
5825
{
5826
m_SB.Add(ch);
5827
}
5828
else switch (ch)
5829
{
5830
case '\b':
5831
m_SB.Add("\\b");
5832
break;
5833
case '\f':
5834
m_SB.Add("\\f");
5835
break;
5836
case '\n':
5837
m_SB.Add("\\n");
5838
break;
5839
case '\r':
5840
m_SB.Add("\\r");
5841
break;
5842
case '\t':
5843
m_SB.Add("\\t");
5844
break;
5845
default:
5846
VMA_ASSERT(0 && "Character not currently supported.");
5847
}
5848
}
5849
}
5850
5851
void VmaJsonWriter::ContinueString(uint32_t n)
5852
{
5853
VMA_ASSERT(m_InsideString);
5854
m_SB.AddNumber(n);
5855
}
5856
5857
void VmaJsonWriter::ContinueString(uint64_t n)
5858
{
5859
VMA_ASSERT(m_InsideString);
5860
m_SB.AddNumber(n);
5861
}
5862
5863
void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
5864
{
5865
VMA_ASSERT(m_InsideString);
5866
m_SB.AddPointer(ptr);
5867
}
5868
5869
void VmaJsonWriter::EndString(const char* pStr)
5870
{
5871
VMA_ASSERT(m_InsideString);
5872
if (pStr != VMA_NULL && pStr[0] != '\0')
5873
{
5874
ContinueString(pStr);
5875
}
5876
m_SB.Add('"');
5877
m_InsideString = false;
5878
}
5879
5880
void VmaJsonWriter::WriteNumber(uint32_t n)
5881
{
5882
VMA_ASSERT(!m_InsideString);
5883
BeginValue(false);
5884
m_SB.AddNumber(n);
5885
}
5886
5887
void VmaJsonWriter::WriteNumber(uint64_t n)
5888
{
5889
VMA_ASSERT(!m_InsideString);
5890
BeginValue(false);
5891
m_SB.AddNumber(n);
5892
}
5893
5894
void VmaJsonWriter::WriteBool(bool b)
5895
{
5896
VMA_ASSERT(!m_InsideString);
5897
BeginValue(false);
5898
m_SB.Add(b ? "true" : "false");
5899
}
5900
5901
void VmaJsonWriter::WriteNull()
5902
{
5903
VMA_ASSERT(!m_InsideString);
5904
BeginValue(false);
5905
m_SB.Add("null");
5906
}
5907
5908
void VmaJsonWriter::BeginValue(bool isString)
5909
{
5910
if (!m_Stack.empty())
5911
{
5912
StackItem& currItem = m_Stack.back();
5913
if (currItem.type == COLLECTION_TYPE_OBJECT &&
5914
currItem.valueCount % 2 == 0)
5915
{
5916
VMA_ASSERT(isString);
5917
}
5918
5919
if (currItem.type == COLLECTION_TYPE_OBJECT &&
5920
currItem.valueCount % 2 != 0)
5921
{
5922
m_SB.Add(": ");
5923
}
5924
else if (currItem.valueCount > 0)
5925
{
5926
m_SB.Add(", ");
5927
WriteIndent();
5928
}
5929
else
5930
{
5931
WriteIndent();
5932
}
5933
++currItem.valueCount;
5934
}
5935
}
5936
5937
void VmaJsonWriter::WriteIndent(bool oneLess)
5938
{
5939
if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
5940
{
5941
m_SB.AddNewLine();
5942
5943
size_t count = m_Stack.size();
5944
if (count > 0 && oneLess)
5945
{
5946
--count;
5947
}
5948
for (size_t i = 0; i < count; ++i)
5949
{
5950
m_SB.Add(INDENT);
5951
}
5952
}
5953
}
5954
#endif // _VMA_JSON_WRITER_FUNCTIONS
5955
5956
static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)
5957
{
5958
json.BeginObject();
5959
5960
json.WriteString("BlockCount");
5961
json.WriteNumber(stat.statistics.blockCount);
5962
json.WriteString("BlockBytes");
5963
json.WriteNumber(stat.statistics.blockBytes);
5964
json.WriteString("AllocationCount");
5965
json.WriteNumber(stat.statistics.allocationCount);
5966
json.WriteString("AllocationBytes");
5967
json.WriteNumber(stat.statistics.allocationBytes);
5968
json.WriteString("UnusedRangeCount");
5969
json.WriteNumber(stat.unusedRangeCount);
5970
5971
if (stat.statistics.allocationCount > 1)
5972
{
5973
json.WriteString("AllocationSizeMin");
5974
json.WriteNumber(stat.allocationSizeMin);
5975
json.WriteString("AllocationSizeMax");
5976
json.WriteNumber(stat.allocationSizeMax);
5977
}
5978
if (stat.unusedRangeCount > 1)
5979
{
5980
json.WriteString("UnusedRangeSizeMin");
5981
json.WriteNumber(stat.unusedRangeSizeMin);
5982
json.WriteString("UnusedRangeSizeMax");
5983
json.WriteNumber(stat.unusedRangeSizeMax);
5984
}
5985
json.EndObject();
5986
}
5987
#endif // _VMA_JSON_WRITER
5988
5989
#ifndef _VMA_MAPPING_HYSTERESIS
5990
5991
class VmaMappingHysteresis
5992
{
5993
VMA_CLASS_NO_COPY_NO_MOVE(VmaMappingHysteresis)
5994
public:
5995
VmaMappingHysteresis() = default;
5996
5997
uint32_t GetExtraMapping() const { return m_ExtraMapping; }
5998
5999
// Call when Map was called.
6000
// Returns true if switched to extra +1 mapping reference count.
6001
bool PostMap()
6002
{
6003
#if VMA_MAPPING_HYSTERESIS_ENABLED
6004
if(m_ExtraMapping == 0)
6005
{
6006
++m_MajorCounter;
6007
if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)
6008
{
6009
m_ExtraMapping = 1;
6010
m_MajorCounter = 0;
6011
m_MinorCounter = 0;
6012
return true;
6013
}
6014
}
6015
else // m_ExtraMapping == 1
6016
PostMinorCounter();
6017
#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
6018
return false;
6019
}
6020
6021
// Call when Unmap was called.
6022
void PostUnmap()
6023
{
6024
#if VMA_MAPPING_HYSTERESIS_ENABLED
6025
if(m_ExtraMapping == 0)
6026
++m_MajorCounter;
6027
else // m_ExtraMapping == 1
6028
PostMinorCounter();
6029
#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
6030
}
6031
6032
// Call when allocation was made from the memory block.
6033
void PostAlloc()
6034
{
6035
#if VMA_MAPPING_HYSTERESIS_ENABLED
6036
if(m_ExtraMapping == 1)
6037
++m_MajorCounter;
6038
else // m_ExtraMapping == 0
6039
PostMinorCounter();
6040
#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
6041
}
6042
6043
// Call when allocation was freed from the memory block.
6044
// Returns true if switched to extra -1 mapping reference count.
6045
bool PostFree()
6046
{
6047
#if VMA_MAPPING_HYSTERESIS_ENABLED
6048
if(m_ExtraMapping == 1)
6049
{
6050
++m_MajorCounter;
6051
if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&
6052
m_MajorCounter > m_MinorCounter + 1)
6053
{
6054
m_ExtraMapping = 0;
6055
m_MajorCounter = 0;
6056
m_MinorCounter = 0;
6057
return true;
6058
}
6059
}
6060
else // m_ExtraMapping == 0
6061
PostMinorCounter();
6062
#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
6063
return false;
6064
}
6065
6066
private:
6067
static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;
6068
6069
uint32_t m_MinorCounter = 0;
6070
uint32_t m_MajorCounter = 0;
6071
uint32_t m_ExtraMapping = 0; // 0 or 1.
6072
6073
void PostMinorCounter()
6074
{
6075
if(m_MinorCounter < m_MajorCounter)
6076
{
6077
++m_MinorCounter;
6078
}
6079
else if(m_MajorCounter > 0)
6080
{
6081
--m_MajorCounter;
6082
--m_MinorCounter;
6083
}
6084
}
6085
};
6086
6087
#endif // _VMA_MAPPING_HYSTERESIS
6088
6089
#ifndef _VMA_DEVICE_MEMORY_BLOCK
6090
/*
6091
Represents a single block of device memory (`VkDeviceMemory`) with all the
6092
data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6093
6094
Thread-safety:
6095
- Access to m_pMetadata must be externally synchronized.
6096
- Map, Unmap, Bind* are synchronized internally.
6097
*/
6098
class VmaDeviceMemoryBlock
6099
{
6100
VMA_CLASS_NO_COPY_NO_MOVE(VmaDeviceMemoryBlock)
6101
public:
6102
VmaBlockMetadata* m_pMetadata;
6103
6104
VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6105
~VmaDeviceMemoryBlock();
6106
6107
// Always call after construction.
6108
void Init(
6109
VmaAllocator hAllocator,
6110
VmaPool hParentPool,
6111
uint32_t newMemoryTypeIndex,
6112
VkDeviceMemory newMemory,
6113
VkDeviceSize newSize,
6114
uint32_t id,
6115
uint32_t algorithm,
6116
VkDeviceSize bufferImageGranularity);
6117
// Always call before destruction.
6118
void Destroy(VmaAllocator allocator);
6119
6120
VmaPool GetParentPool() const { return m_hParentPool; }
6121
VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
6122
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6123
uint32_t GetId() const { return m_Id; }
6124
void* GetMappedData() const { return m_pMappedData; }
6125
uint32_t GetMapRefCount() const { return m_MapCount; }
6126
6127
// Call when allocation/free was made from m_pMetadata.
6128
// Used for m_MappingHysteresis.
6129
void PostAlloc(VmaAllocator hAllocator);
6130
void PostFree(VmaAllocator hAllocator);
6131
6132
// Validates all data structures inside this object. If not valid, returns false.
6133
bool Validate() const;
6134
VkResult CheckCorruption(VmaAllocator hAllocator);
6135
6136
// ppData can be null.
6137
VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6138
void Unmap(VmaAllocator hAllocator, uint32_t count);
6139
6140
VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6141
VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6142
6143
VkResult BindBufferMemory(
6144
const VmaAllocator hAllocator,
6145
const VmaAllocation hAllocation,
6146
VkDeviceSize allocationLocalOffset,
6147
VkBuffer hBuffer,
6148
const void* pNext);
6149
VkResult BindImageMemory(
6150
const VmaAllocator hAllocator,
6151
const VmaAllocation hAllocation,
6152
VkDeviceSize allocationLocalOffset,
6153
VkImage hImage,
6154
const void* pNext);
6155
6156
private:
6157
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6158
uint32_t m_MemoryTypeIndex;
6159
uint32_t m_Id;
6160
VkDeviceMemory m_hMemory;
6161
6162
/*
6163
Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6164
Also protects m_MapCount, m_pMappedData.
6165
Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6166
*/
6167
VMA_MUTEX m_MapAndBindMutex;
6168
VmaMappingHysteresis m_MappingHysteresis;
6169
uint32_t m_MapCount;
6170
void* m_pMappedData;
6171
};
6172
#endif // _VMA_DEVICE_MEMORY_BLOCK
6173
6174
#ifndef _VMA_ALLOCATION_T
6175
struct VmaAllocation_T
6176
{
6177
friend struct VmaDedicatedAllocationListItemTraits;
6178
6179
enum FLAGS
6180
{
6181
FLAG_PERSISTENT_MAP = 0x01,
6182
FLAG_MAPPING_ALLOWED = 0x02,
6183
};
6184
6185
public:
6186
enum ALLOCATION_TYPE
6187
{
6188
ALLOCATION_TYPE_NONE,
6189
ALLOCATION_TYPE_BLOCK,
6190
ALLOCATION_TYPE_DEDICATED,
6191
};
6192
6193
// This struct is allocated using VmaPoolAllocator.
6194
VmaAllocation_T(bool mappingAllowed);
6195
~VmaAllocation_T();
6196
6197
void InitBlockAllocation(
6198
VmaDeviceMemoryBlock* block,
6199
VmaAllocHandle allocHandle,
6200
VkDeviceSize alignment,
6201
VkDeviceSize size,
6202
uint32_t memoryTypeIndex,
6203
VmaSuballocationType suballocationType,
6204
bool mapped);
6205
// pMappedData not null means allocation is created with MAPPED flag.
6206
void InitDedicatedAllocation(
6207
VmaPool hParentPool,
6208
uint32_t memoryTypeIndex,
6209
VkDeviceMemory hMemory,
6210
VmaSuballocationType suballocationType,
6211
void* pMappedData,
6212
VkDeviceSize size);
6213
6214
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6215
VkDeviceSize GetAlignment() const { return m_Alignment; }
6216
VkDeviceSize GetSize() const { return m_Size; }
6217
void* GetUserData() const { return m_pUserData; }
6218
const char* GetName() const { return m_pName; }
6219
VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6220
6221
VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }
6222
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6223
bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }
6224
bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }
6225
6226
void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }
6227
void SetName(VmaAllocator hAllocator, const char* pName);
6228
void FreeName(VmaAllocator hAllocator);
6229
uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);
6230
VmaAllocHandle GetAllocHandle() const;
6231
VkDeviceSize GetOffset() const;
6232
VmaPool GetParentPool() const;
6233
VkDeviceMemory GetMemory() const;
6234
void* GetMappedData() const;
6235
6236
void BlockAllocMap();
6237
void BlockAllocUnmap();
6238
VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6239
void DedicatedAllocUnmap(VmaAllocator hAllocator);
6240
6241
#if VMA_STATS_STRING_ENABLED
6242
VmaBufferImageUsage GetBufferImageUsage() const { return m_BufferImageUsage; }
6243
void InitBufferUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5)
6244
{
6245
VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN);
6246
m_BufferImageUsage = VmaBufferImageUsage(createInfo, useKhrMaintenance5);
6247
}
6248
void InitImageUsage(const VkImageCreateInfo &createInfo)
6249
{
6250
VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN);
6251
m_BufferImageUsage = VmaBufferImageUsage(createInfo);
6252
}
6253
void PrintParameters(class VmaJsonWriter& json) const;
6254
#endif
6255
6256
private:
6257
// Allocation out of VmaDeviceMemoryBlock.
6258
struct BlockAllocation
6259
{
6260
VmaDeviceMemoryBlock* m_Block;
6261
VmaAllocHandle m_AllocHandle;
6262
};
6263
// Allocation for an object that has its own private VkDeviceMemory.
6264
struct DedicatedAllocation
6265
{
6266
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6267
VkDeviceMemory m_hMemory;
6268
void* m_pMappedData; // Not null means memory is mapped.
6269
VmaAllocation_T* m_Prev;
6270
VmaAllocation_T* m_Next;
6271
};
6272
union
6273
{
6274
// Allocation out of VmaDeviceMemoryBlock.
6275
BlockAllocation m_BlockAllocation;
6276
// Allocation for an object that has its own private VkDeviceMemory.
6277
DedicatedAllocation m_DedicatedAllocation;
6278
};
6279
6280
VkDeviceSize m_Alignment;
6281
VkDeviceSize m_Size;
6282
void* m_pUserData;
6283
char* m_pName;
6284
uint32_t m_MemoryTypeIndex;
6285
uint8_t m_Type; // ALLOCATION_TYPE
6286
uint8_t m_SuballocationType; // VmaSuballocationType
6287
// Reference counter for vmaMapMemory()/vmaUnmapMemory().
6288
uint8_t m_MapCount;
6289
uint8_t m_Flags; // enum FLAGS
6290
#if VMA_STATS_STRING_ENABLED
6291
VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.
6292
#endif
6293
};
6294
#endif // _VMA_ALLOCATION_T
6295
6296
#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6297
struct VmaDedicatedAllocationListItemTraits
6298
{
6299
typedef VmaAllocation_T ItemType;
6300
6301
static ItemType* GetPrev(const ItemType* item)
6302
{
6303
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6304
return item->m_DedicatedAllocation.m_Prev;
6305
}
6306
static ItemType* GetNext(const ItemType* item)
6307
{
6308
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6309
return item->m_DedicatedAllocation.m_Next;
6310
}
6311
static ItemType*& AccessPrev(ItemType* item)
6312
{
6313
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6314
return item->m_DedicatedAllocation.m_Prev;
6315
}
6316
static ItemType*& AccessNext(ItemType* item)
6317
{
6318
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6319
return item->m_DedicatedAllocation.m_Next;
6320
}
6321
};
6322
#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6323
6324
#ifndef _VMA_DEDICATED_ALLOCATION_LIST
6325
/*
6326
Stores linked list of VmaAllocation_T objects.
6327
Thread-safe, synchronized internally.
6328
*/
6329
class VmaDedicatedAllocationList
6330
{
6331
VMA_CLASS_NO_COPY_NO_MOVE(VmaDedicatedAllocationList)
6332
public:
6333
VmaDedicatedAllocationList() {}
6334
~VmaDedicatedAllocationList();
6335
6336
void Init(bool useMutex) { m_UseMutex = useMutex; }
6337
bool Validate();
6338
6339
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
6340
void AddStatistics(VmaStatistics& inoutStats);
6341
#if VMA_STATS_STRING_ENABLED
6342
// Writes JSON array with the list of allocations.
6343
void BuildStatsString(VmaJsonWriter& json);
6344
#endif
6345
6346
bool IsEmpty();
6347
void Register(VmaAllocation alloc);
6348
void Unregister(VmaAllocation alloc);
6349
6350
private:
6351
typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
6352
6353
bool m_UseMutex = true;
6354
VMA_RW_MUTEX m_Mutex;
6355
DedicatedAllocationLinkedList m_AllocationList;
6356
};
6357
6358
#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6359
6360
VmaDedicatedAllocationList::~VmaDedicatedAllocationList()
6361
{
6362
VMA_HEAVY_ASSERT(Validate());
6363
6364
if (!m_AllocationList.IsEmpty())
6365
{
6366
VMA_ASSERT_LEAK(false && "Unfreed dedicated allocations found!");
6367
}
6368
}
6369
6370
bool VmaDedicatedAllocationList::Validate()
6371
{
6372
const size_t declaredCount = m_AllocationList.GetCount();
6373
size_t actualCount = 0;
6374
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6375
for (VmaAllocation alloc = m_AllocationList.Front();
6376
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6377
{
6378
++actualCount;
6379
}
6380
VMA_VALIDATE(actualCount == declaredCount);
6381
6382
return true;
6383
}
6384
6385
void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
6386
{
6387
for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))
6388
{
6389
const VkDeviceSize size = item->GetSize();
6390
inoutStats.statistics.blockCount++;
6391
inoutStats.statistics.blockBytes += size;
6392
VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());
6393
}
6394
}
6395
6396
void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)
6397
{
6398
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6399
6400
const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();
6401
inoutStats.blockCount += allocCount;
6402
inoutStats.allocationCount += allocCount;
6403
6404
for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))
6405
{
6406
const VkDeviceSize size = item->GetSize();
6407
inoutStats.blockBytes += size;
6408
inoutStats.allocationBytes += size;
6409
}
6410
}
6411
6412
#if VMA_STATS_STRING_ENABLED
6413
void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
6414
{
6415
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6416
json.BeginArray();
6417
for (VmaAllocation alloc = m_AllocationList.Front();
6418
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6419
{
6420
json.BeginObject(true);
6421
alloc->PrintParameters(json);
6422
json.EndObject();
6423
}
6424
json.EndArray();
6425
}
6426
#endif // VMA_STATS_STRING_ENABLED
6427
6428
bool VmaDedicatedAllocationList::IsEmpty()
6429
{
6430
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6431
return m_AllocationList.IsEmpty();
6432
}
6433
6434
void VmaDedicatedAllocationList::Register(VmaAllocation alloc)
6435
{
6436
VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6437
m_AllocationList.PushBack(alloc);
6438
}
6439
6440
void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)
6441
{
6442
VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6443
m_AllocationList.Remove(alloc);
6444
}
6445
#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6446
#endif // _VMA_DEDICATED_ALLOCATION_LIST
6447
6448
#ifndef _VMA_SUBALLOCATION
6449
/*
6450
Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6451
allocated memory block or free.
6452
*/
6453
struct VmaSuballocation
6454
{
6455
VkDeviceSize offset;
6456
VkDeviceSize size;
6457
void* userData;
6458
VmaSuballocationType type;
6459
};
6460
6461
// Comparator for offsets.
6462
struct VmaSuballocationOffsetLess
6463
{
6464
bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6465
{
6466
return lhs.offset < rhs.offset;
6467
}
6468
};
6469
6470
struct VmaSuballocationOffsetGreater
6471
{
6472
bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6473
{
6474
return lhs.offset > rhs.offset;
6475
}
6476
};
6477
6478
struct VmaSuballocationItemSizeLess
6479
{
6480
bool operator()(const VmaSuballocationList::iterator lhs,
6481
const VmaSuballocationList::iterator rhs) const
6482
{
6483
return lhs->size < rhs->size;
6484
}
6485
6486
bool operator()(const VmaSuballocationList::iterator lhs,
6487
VkDeviceSize rhsSize) const
6488
{
6489
return lhs->size < rhsSize;
6490
}
6491
};
6492
#endif // _VMA_SUBALLOCATION
6493
6494
#ifndef _VMA_ALLOCATION_REQUEST
6495
/*
6496
Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6497
item points to a FREE suballocation.
6498
*/
6499
struct VmaAllocationRequest
6500
{
6501
VmaAllocHandle allocHandle;
6502
VkDeviceSize size;
6503
VmaSuballocationList::iterator item;
6504
void* customData;
6505
uint64_t algorithmData;
6506
VmaAllocationRequestType type;
6507
};
6508
#endif // _VMA_ALLOCATION_REQUEST
6509
6510
#ifndef _VMA_BLOCK_METADATA
6511
/*
6512
Data structure used for bookkeeping of allocations and unused ranges of memory
6513
in a single VkDeviceMemory block.
6514
*/
6515
class VmaBlockMetadata
6516
{
6517
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata)
6518
public:
6519
// pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.
6520
VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6521
VkDeviceSize bufferImageGranularity, bool isVirtual);
6522
virtual ~VmaBlockMetadata() = default;
6523
6524
virtual void Init(VkDeviceSize size) { m_Size = size; }
6525
bool IsVirtual() const { return m_IsVirtual; }
6526
VkDeviceSize GetSize() const { return m_Size; }
6527
6528
// Validates all data structures inside this object. If not valid, returns false.
6529
virtual bool Validate() const = 0;
6530
virtual size_t GetAllocationCount() const = 0;
6531
virtual size_t GetFreeRegionsCount() const = 0;
6532
virtual VkDeviceSize GetSumFreeSize() const = 0;
6533
// Returns true if this block is empty - contains only single free suballocation.
6534
virtual bool IsEmpty() const = 0;
6535
virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;
6536
virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;
6537
virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;
6538
6539
virtual VmaAllocHandle GetAllocationListBegin() const = 0;
6540
virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;
6541
virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;
6542
6543
// Shouldn't modify blockCount.
6544
virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;
6545
virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;
6546
6547
#if VMA_STATS_STRING_ENABLED
6548
virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6549
#endif
6550
6551
// Tries to find a place for suballocation with given parameters inside this block.
6552
// If succeeded, fills pAllocationRequest and returns true.
6553
// If failed, returns false.
6554
virtual bool CreateAllocationRequest(
6555
VkDeviceSize allocSize,
6556
VkDeviceSize allocAlignment,
6557
bool upperAddress,
6558
VmaSuballocationType allocType,
6559
// Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6560
uint32_t strategy,
6561
VmaAllocationRequest* pAllocationRequest) = 0;
6562
6563
virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6564
6565
// Makes actual allocation based on request. Request must already be checked and valid.
6566
virtual void Alloc(
6567
const VmaAllocationRequest& request,
6568
VmaSuballocationType type,
6569
void* userData) = 0;
6570
6571
// Frees suballocation assigned to given memory region.
6572
virtual void Free(VmaAllocHandle allocHandle) = 0;
6573
6574
// Frees all allocations.
6575
// Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!
6576
virtual void Clear() = 0;
6577
6578
virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;
6579
virtual void DebugLogAllAllocations() const = 0;
6580
6581
protected:
6582
const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6583
VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
6584
VkDeviceSize GetDebugMargin() const { return VkDeviceSize(IsVirtual() ? 0 : VMA_DEBUG_MARGIN); }
6585
6586
void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6587
#if VMA_STATS_STRING_ENABLED
6588
// mapRefCount == UINT32_MAX means unspecified.
6589
void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6590
VkDeviceSize unusedBytes,
6591
size_t allocationCount,
6592
size_t unusedRangeCount) const;
6593
void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6594
VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6595
void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6596
VkDeviceSize offset,
6597
VkDeviceSize size) const;
6598
void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6599
#endif
6600
6601
private:
6602
VkDeviceSize m_Size;
6603
const VkAllocationCallbacks* m_pAllocationCallbacks;
6604
const VkDeviceSize m_BufferImageGranularity;
6605
const bool m_IsVirtual;
6606
};
6607
6608
#ifndef _VMA_BLOCK_METADATA_FUNCTIONS
6609
VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6610
VkDeviceSize bufferImageGranularity, bool isVirtual)
6611
: m_Size(0),
6612
m_pAllocationCallbacks(pAllocationCallbacks),
6613
m_BufferImageGranularity(bufferImageGranularity),
6614
m_IsVirtual(isVirtual) {}
6615
6616
void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const
6617
{
6618
if (IsVirtual())
6619
{
6620
VMA_LEAK_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p", offset, size, userData);
6621
}
6622
else
6623
{
6624
VMA_ASSERT(userData != VMA_NULL);
6625
VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);
6626
6627
userData = allocation->GetUserData();
6628
const char* name = allocation->GetName();
6629
6630
#if VMA_STATS_STRING_ENABLED
6631
VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %s; Usage: %" PRIu64,
6632
offset, size, userData, name ? name : "vma_empty",
6633
VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],
6634
(uint64_t)allocation->GetBufferImageUsage().Value);
6635
#else
6636
VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %u",
6637
offset, size, userData, name ? name : "vma_empty",
6638
(unsigned)allocation->GetSuballocationType());
6639
#endif // VMA_STATS_STRING_ENABLED
6640
}
6641
6642
}
6643
6644
#if VMA_STATS_STRING_ENABLED
6645
void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
6646
VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
6647
{
6648
json.WriteString("TotalBytes");
6649
json.WriteNumber(GetSize());
6650
6651
json.WriteString("UnusedBytes");
6652
json.WriteNumber(unusedBytes);
6653
6654
json.WriteString("Allocations");
6655
json.WriteNumber((uint64_t)allocationCount);
6656
6657
json.WriteString("UnusedRanges");
6658
json.WriteNumber((uint64_t)unusedRangeCount);
6659
6660
json.WriteString("Suballocations");
6661
json.BeginArray();
6662
}
6663
6664
void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6665
VkDeviceSize offset, VkDeviceSize size, void* userData) const
6666
{
6667
json.BeginObject(true);
6668
6669
json.WriteString("Offset");
6670
json.WriteNumber(offset);
6671
6672
if (IsVirtual())
6673
{
6674
json.WriteString("Size");
6675
json.WriteNumber(size);
6676
if (userData)
6677
{
6678
json.WriteString("CustomData");
6679
json.BeginString();
6680
json.ContinueString_Pointer(userData);
6681
json.EndString();
6682
}
6683
}
6684
else
6685
{
6686
((VmaAllocation)userData)->PrintParameters(json);
6687
}
6688
6689
json.EndObject();
6690
}
6691
6692
void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6693
VkDeviceSize offset, VkDeviceSize size) const
6694
{
6695
json.BeginObject(true);
6696
6697
json.WriteString("Offset");
6698
json.WriteNumber(offset);
6699
6700
json.WriteString("Type");
6701
json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
6702
6703
json.WriteString("Size");
6704
json.WriteNumber(size);
6705
6706
json.EndObject();
6707
}
6708
6709
void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
6710
{
6711
json.EndArray();
6712
}
6713
#endif // VMA_STATS_STRING_ENABLED
6714
#endif // _VMA_BLOCK_METADATA_FUNCTIONS
6715
#endif // _VMA_BLOCK_METADATA
6716
6717
#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6718
// Before deleting object of this class remember to call 'Destroy()'
6719
class VmaBlockBufferImageGranularity final
6720
{
6721
public:
6722
struct ValidationContext
6723
{
6724
const VkAllocationCallbacks* allocCallbacks;
6725
uint16_t* pageAllocs;
6726
};
6727
6728
VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);
6729
~VmaBlockBufferImageGranularity();
6730
6731
bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }
6732
6733
void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);
6734
// Before destroying object you must call free it's memory
6735
void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);
6736
6737
void RoundupAllocRequest(VmaSuballocationType allocType,
6738
VkDeviceSize& inOutAllocSize,
6739
VkDeviceSize& inOutAllocAlignment) const;
6740
6741
bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6742
VkDeviceSize allocSize,
6743
VkDeviceSize blockOffset,
6744
VkDeviceSize blockSize,
6745
VmaSuballocationType allocType) const;
6746
6747
void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);
6748
void FreePages(VkDeviceSize offset, VkDeviceSize size);
6749
void Clear();
6750
6751
ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,
6752
bool isVirutal) const;
6753
bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;
6754
bool FinishValidation(ValidationContext& ctx) const;
6755
6756
private:
6757
static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;
6758
6759
struct RegionInfo
6760
{
6761
uint8_t allocType;
6762
uint16_t allocCount;
6763
};
6764
6765
VkDeviceSize m_BufferImageGranularity;
6766
uint32_t m_RegionCount;
6767
RegionInfo* m_RegionInfo;
6768
6769
uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }
6770
uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }
6771
6772
uint32_t OffsetToPageIndex(VkDeviceSize offset) const;
6773
void AllocPage(RegionInfo& page, uint8_t allocType);
6774
};
6775
6776
#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
6777
VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)
6778
: m_BufferImageGranularity(bufferImageGranularity),
6779
m_RegionCount(0),
6780
m_RegionInfo(VMA_NULL) {}
6781
6782
VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()
6783
{
6784
VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");
6785
}
6786
6787
void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)
6788
{
6789
if (IsEnabled())
6790
{
6791
m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));
6792
m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);
6793
memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6794
}
6795
}
6796
6797
void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)
6798
{
6799
if (m_RegionInfo)
6800
{
6801
vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);
6802
m_RegionInfo = VMA_NULL;
6803
}
6804
}
6805
6806
void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,
6807
VkDeviceSize& inOutAllocSize,
6808
VkDeviceSize& inOutAllocAlignment) const
6809
{
6810
if (m_BufferImageGranularity > 1 &&
6811
m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)
6812
{
6813
if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
6814
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
6815
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
6816
{
6817
inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);
6818
inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);
6819
}
6820
}
6821
}
6822
6823
bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6824
VkDeviceSize allocSize,
6825
VkDeviceSize blockOffset,
6826
VkDeviceSize blockSize,
6827
VmaSuballocationType allocType) const
6828
{
6829
if (IsEnabled())
6830
{
6831
uint32_t startPage = GetStartPage(inOutAllocOffset);
6832
if (m_RegionInfo[startPage].allocCount > 0 &&
6833
VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))
6834
{
6835
inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);
6836
if (blockSize < allocSize + inOutAllocOffset - blockOffset)
6837
return true;
6838
++startPage;
6839
}
6840
uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);
6841
if (endPage != startPage &&
6842
m_RegionInfo[endPage].allocCount > 0 &&
6843
VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))
6844
{
6845
return true;
6846
}
6847
}
6848
return false;
6849
}
6850
6851
void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)
6852
{
6853
if (IsEnabled())
6854
{
6855
uint32_t startPage = GetStartPage(offset);
6856
AllocPage(m_RegionInfo[startPage], allocType);
6857
6858
uint32_t endPage = GetEndPage(offset, size);
6859
if (startPage != endPage)
6860
AllocPage(m_RegionInfo[endPage], allocType);
6861
}
6862
}
6863
6864
void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)
6865
{
6866
if (IsEnabled())
6867
{
6868
uint32_t startPage = GetStartPage(offset);
6869
--m_RegionInfo[startPage].allocCount;
6870
if (m_RegionInfo[startPage].allocCount == 0)
6871
m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6872
uint32_t endPage = GetEndPage(offset, size);
6873
if (startPage != endPage)
6874
{
6875
--m_RegionInfo[endPage].allocCount;
6876
if (m_RegionInfo[endPage].allocCount == 0)
6877
m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6878
}
6879
}
6880
}
6881
6882
void VmaBlockBufferImageGranularity::Clear()
6883
{
6884
if (m_RegionInfo)
6885
memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6886
}
6887
6888
VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(
6889
const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const
6890
{
6891
ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };
6892
if (!isVirutal && IsEnabled())
6893
{
6894
ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);
6895
memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));
6896
}
6897
return ctx;
6898
}
6899
6900
bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,
6901
VkDeviceSize offset, VkDeviceSize size) const
6902
{
6903
if (IsEnabled())
6904
{
6905
uint32_t start = GetStartPage(offset);
6906
++ctx.pageAllocs[start];
6907
VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);
6908
6909
uint32_t end = GetEndPage(offset, size);
6910
if (start != end)
6911
{
6912
++ctx.pageAllocs[end];
6913
VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);
6914
}
6915
}
6916
return true;
6917
}
6918
6919
bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const
6920
{
6921
// Check proper page structure
6922
if (IsEnabled())
6923
{
6924
VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");
6925
6926
for (uint32_t page = 0; page < m_RegionCount; ++page)
6927
{
6928
VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);
6929
}
6930
vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);
6931
ctx.pageAllocs = VMA_NULL;
6932
}
6933
return true;
6934
}
6935
6936
uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const
6937
{
6938
return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));
6939
}
6940
6941
void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)
6942
{
6943
// When current alloc type is free then it can be overridden by new type
6944
if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))
6945
page.allocType = allocType;
6946
6947
++page.allocCount;
6948
}
6949
#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
6950
#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6951
6952
#ifndef _VMA_BLOCK_METADATA_LINEAR
6953
/*
6954
Allocations and their references in internal data structure look like this:
6955
6956
if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6957
6958
0 +-------+
6959
| |
6960
| |
6961
| |
6962
+-------+
6963
| Alloc | 1st[m_1stNullItemsBeginCount]
6964
+-------+
6965
| Alloc | 1st[m_1stNullItemsBeginCount + 1]
6966
+-------+
6967
| ... |
6968
+-------+
6969
| Alloc | 1st[1st.size() - 1]
6970
+-------+
6971
| |
6972
| |
6973
| |
6974
GetSize() +-------+
6975
6976
if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6977
6978
0 +-------+
6979
| Alloc | 2nd[0]
6980
+-------+
6981
| Alloc | 2nd[1]
6982
+-------+
6983
| ... |
6984
+-------+
6985
| Alloc | 2nd[2nd.size() - 1]
6986
+-------+
6987
| |
6988
| |
6989
| |
6990
+-------+
6991
| Alloc | 1st[m_1stNullItemsBeginCount]
6992
+-------+
6993
| Alloc | 1st[m_1stNullItemsBeginCount + 1]
6994
+-------+
6995
| ... |
6996
+-------+
6997
| Alloc | 1st[1st.size() - 1]
6998
+-------+
6999
| |
7000
GetSize() +-------+
7001
7002
if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
7003
7004
0 +-------+
7005
| |
7006
| |
7007
| |
7008
+-------+
7009
| Alloc | 1st[m_1stNullItemsBeginCount]
7010
+-------+
7011
| Alloc | 1st[m_1stNullItemsBeginCount + 1]
7012
+-------+
7013
| ... |
7014
+-------+
7015
| Alloc | 1st[1st.size() - 1]
7016
+-------+
7017
| |
7018
| |
7019
| |
7020
+-------+
7021
| Alloc | 2nd[2nd.size() - 1]
7022
+-------+
7023
| ... |
7024
+-------+
7025
| Alloc | 2nd[1]
7026
+-------+
7027
| Alloc | 2nd[0]
7028
GetSize() +-------+
7029
7030
*/
7031
class VmaBlockMetadata_Linear : public VmaBlockMetadata
7032
{
7033
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Linear)
7034
public:
7035
VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7036
VkDeviceSize bufferImageGranularity, bool isVirtual);
7037
virtual ~VmaBlockMetadata_Linear() = default;
7038
7039
VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
7040
bool IsEmpty() const override { return GetAllocationCount() == 0; }
7041
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }
7042
7043
void Init(VkDeviceSize size) override;
7044
bool Validate() const override;
7045
size_t GetAllocationCount() const override;
7046
size_t GetFreeRegionsCount() const override;
7047
7048
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
7049
void AddStatistics(VmaStatistics& inoutStats) const override;
7050
7051
#if VMA_STATS_STRING_ENABLED
7052
void PrintDetailedMap(class VmaJsonWriter& json) const override;
7053
#endif
7054
7055
bool CreateAllocationRequest(
7056
VkDeviceSize allocSize,
7057
VkDeviceSize allocAlignment,
7058
bool upperAddress,
7059
VmaSuballocationType allocType,
7060
uint32_t strategy,
7061
VmaAllocationRequest* pAllocationRequest) override;
7062
7063
VkResult CheckCorruption(const void* pBlockData) override;
7064
7065
void Alloc(
7066
const VmaAllocationRequest& request,
7067
VmaSuballocationType type,
7068
void* userData) override;
7069
7070
void Free(VmaAllocHandle allocHandle) override;
7071
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
7072
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
7073
VmaAllocHandle GetAllocationListBegin() const override;
7074
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
7075
VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
7076
void Clear() override;
7077
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
7078
void DebugLogAllAllocations() const override;
7079
7080
private:
7081
/*
7082
There are two suballocation vectors, used in ping-pong way.
7083
The one with index m_1stVectorIndex is called 1st.
7084
The one with index (m_1stVectorIndex ^ 1) is called 2nd.
7085
2nd can be non-empty only when 1st is not empty.
7086
When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
7087
*/
7088
typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;
7089
7090
enum SECOND_VECTOR_MODE
7091
{
7092
SECOND_VECTOR_EMPTY,
7093
/*
7094
Suballocations in 2nd vector are created later than the ones in 1st, but they
7095
all have smaller offset.
7096
*/
7097
SECOND_VECTOR_RING_BUFFER,
7098
/*
7099
Suballocations in 2nd vector are upper side of double stack.
7100
They all have offsets higher than those in 1st vector.
7101
Top of this stack means smaller offsets, but higher indices in this vector.
7102
*/
7103
SECOND_VECTOR_DOUBLE_STACK,
7104
};
7105
7106
VkDeviceSize m_SumFreeSize;
7107
SuballocationVectorType m_Suballocations0, m_Suballocations1;
7108
uint32_t m_1stVectorIndex;
7109
SECOND_VECTOR_MODE m_2ndVectorMode;
7110
// Number of items in 1st vector with hAllocation = null at the beginning.
7111
size_t m_1stNullItemsBeginCount;
7112
// Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7113
size_t m_1stNullItemsMiddleCount;
7114
// Number of items in 2nd vector with hAllocation = null.
7115
size_t m_2ndNullItemsCount;
7116
7117
SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7118
SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7119
const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7120
const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7121
7122
VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;
7123
bool ShouldCompact1st() const;
7124
void CleanupAfterFree();
7125
7126
bool CreateAllocationRequest_LowerAddress(
7127
VkDeviceSize allocSize,
7128
VkDeviceSize allocAlignment,
7129
VmaSuballocationType allocType,
7130
uint32_t strategy,
7131
VmaAllocationRequest* pAllocationRequest);
7132
bool CreateAllocationRequest_UpperAddress(
7133
VkDeviceSize allocSize,
7134
VkDeviceSize allocAlignment,
7135
VmaSuballocationType allocType,
7136
uint32_t strategy,
7137
VmaAllocationRequest* pAllocationRequest);
7138
};
7139
7140
#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
7141
VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7142
VkDeviceSize bufferImageGranularity, bool isVirtual)
7143
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
7144
m_SumFreeSize(0),
7145
m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7146
m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7147
m_1stVectorIndex(0),
7148
m_2ndVectorMode(SECOND_VECTOR_EMPTY),
7149
m_1stNullItemsBeginCount(0),
7150
m_1stNullItemsMiddleCount(0),
7151
m_2ndNullItemsCount(0) {}
7152
7153
void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
7154
{
7155
VmaBlockMetadata::Init(size);
7156
m_SumFreeSize = size;
7157
}
7158
7159
bool VmaBlockMetadata_Linear::Validate() const
7160
{
7161
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7162
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7163
7164
VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
7165
VMA_VALIDATE(!suballocations1st.empty() ||
7166
suballocations2nd.empty() ||
7167
m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
7168
7169
if (!suballocations1st.empty())
7170
{
7171
// Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
7172
VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
7173
// Null item at the end should be just pop_back().
7174
VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7175
}
7176
if (!suballocations2nd.empty())
7177
{
7178
// Null item at the end should be just pop_back().
7179
VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7180
}
7181
7182
VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
7183
VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
7184
7185
VkDeviceSize sumUsedSize = 0;
7186
const size_t suballoc1stCount = suballocations1st.size();
7187
const VkDeviceSize debugMargin = GetDebugMargin();
7188
VkDeviceSize offset = 0;
7189
7190
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7191
{
7192
const size_t suballoc2ndCount = suballocations2nd.size();
7193
size_t nullItem2ndCount = 0;
7194
for (size_t i = 0; i < suballoc2ndCount; ++i)
7195
{
7196
const VmaSuballocation& suballoc = suballocations2nd[i];
7197
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7198
7199
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7200
if (!IsVirtual())
7201
{
7202
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7203
}
7204
VMA_VALIDATE(suballoc.offset >= offset);
7205
7206
if (!currFree)
7207
{
7208
if (!IsVirtual())
7209
{
7210
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7211
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7212
}
7213
sumUsedSize += suballoc.size;
7214
}
7215
else
7216
{
7217
++nullItem2ndCount;
7218
}
7219
7220
offset = suballoc.offset + suballoc.size + debugMargin;
7221
}
7222
7223
VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7224
}
7225
7226
for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
7227
{
7228
const VmaSuballocation& suballoc = suballocations1st[i];
7229
VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
7230
suballoc.userData == VMA_NULL);
7231
}
7232
7233
size_t nullItem1stCount = m_1stNullItemsBeginCount;
7234
7235
for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
7236
{
7237
const VmaSuballocation& suballoc = suballocations1st[i];
7238
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7239
7240
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7241
if (!IsVirtual())
7242
{
7243
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7244
}
7245
VMA_VALIDATE(suballoc.offset >= offset);
7246
VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
7247
7248
if (!currFree)
7249
{
7250
if (!IsVirtual())
7251
{
7252
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7253
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7254
}
7255
sumUsedSize += suballoc.size;
7256
}
7257
else
7258
{
7259
++nullItem1stCount;
7260
}
7261
7262
offset = suballoc.offset + suballoc.size + debugMargin;
7263
}
7264
VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
7265
7266
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7267
{
7268
const size_t suballoc2ndCount = suballocations2nd.size();
7269
size_t nullItem2ndCount = 0;
7270
for (size_t i = suballoc2ndCount; i--; )
7271
{
7272
const VmaSuballocation& suballoc = suballocations2nd[i];
7273
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7274
7275
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7276
if (!IsVirtual())
7277
{
7278
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7279
}
7280
VMA_VALIDATE(suballoc.offset >= offset);
7281
7282
if (!currFree)
7283
{
7284
if (!IsVirtual())
7285
{
7286
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7287
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7288
}
7289
sumUsedSize += suballoc.size;
7290
}
7291
else
7292
{
7293
++nullItem2ndCount;
7294
}
7295
7296
offset = suballoc.offset + suballoc.size + debugMargin;
7297
}
7298
7299
VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7300
}
7301
7302
VMA_VALIDATE(offset <= GetSize());
7303
VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
7304
7305
return true;
7306
}
7307
7308
size_t VmaBlockMetadata_Linear::GetAllocationCount() const
7309
{
7310
return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
7311
AccessSuballocations2nd().size() - m_2ndNullItemsCount;
7312
}
7313
7314
size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const
7315
{
7316
// Function only used for defragmentation, which is disabled for this algorithm
7317
VMA_ASSERT(0);
7318
return SIZE_MAX;
7319
}
7320
7321
void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
7322
{
7323
const VkDeviceSize size = GetSize();
7324
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7325
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7326
const size_t suballoc1stCount = suballocations1st.size();
7327
const size_t suballoc2ndCount = suballocations2nd.size();
7328
7329
inoutStats.statistics.blockCount++;
7330
inoutStats.statistics.blockBytes += size;
7331
7332
VkDeviceSize lastOffset = 0;
7333
7334
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7335
{
7336
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7337
size_t nextAlloc2ndIndex = 0;
7338
while (lastOffset < freeSpace2ndTo1stEnd)
7339
{
7340
// Find next non-null allocation or move nextAllocIndex to the end.
7341
while (nextAlloc2ndIndex < suballoc2ndCount &&
7342
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7343
{
7344
++nextAlloc2ndIndex;
7345
}
7346
7347
// Found non-null allocation.
7348
if (nextAlloc2ndIndex < suballoc2ndCount)
7349
{
7350
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7351
7352
// 1. Process free space before this allocation.
7353
if (lastOffset < suballoc.offset)
7354
{
7355
// There is free space from lastOffset to suballoc.offset.
7356
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7357
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7358
}
7359
7360
// 2. Process this allocation.
7361
// There is allocation with suballoc.offset, suballoc.size.
7362
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7363
7364
// 3. Prepare for next iteration.
7365
lastOffset = suballoc.offset + suballoc.size;
7366
++nextAlloc2ndIndex;
7367
}
7368
// We are at the end.
7369
else
7370
{
7371
// There is free space from lastOffset to freeSpace2ndTo1stEnd.
7372
if (lastOffset < freeSpace2ndTo1stEnd)
7373
{
7374
const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
7375
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7376
}
7377
7378
// End of loop.
7379
lastOffset = freeSpace2ndTo1stEnd;
7380
}
7381
}
7382
}
7383
7384
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
7385
const VkDeviceSize freeSpace1stTo2ndEnd =
7386
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
7387
while (lastOffset < freeSpace1stTo2ndEnd)
7388
{
7389
// Find next non-null allocation or move nextAllocIndex to the end.
7390
while (nextAlloc1stIndex < suballoc1stCount &&
7391
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7392
{
7393
++nextAlloc1stIndex;
7394
}
7395
7396
// Found non-null allocation.
7397
if (nextAlloc1stIndex < suballoc1stCount)
7398
{
7399
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7400
7401
// 1. Process free space before this allocation.
7402
if (lastOffset < suballoc.offset)
7403
{
7404
// There is free space from lastOffset to suballoc.offset.
7405
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7406
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7407
}
7408
7409
// 2. Process this allocation.
7410
// There is allocation with suballoc.offset, suballoc.size.
7411
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7412
7413
// 3. Prepare for next iteration.
7414
lastOffset = suballoc.offset + suballoc.size;
7415
++nextAlloc1stIndex;
7416
}
7417
// We are at the end.
7418
else
7419
{
7420
// There is free space from lastOffset to freeSpace1stTo2ndEnd.
7421
if (lastOffset < freeSpace1stTo2ndEnd)
7422
{
7423
const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
7424
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7425
}
7426
7427
// End of loop.
7428
lastOffset = freeSpace1stTo2ndEnd;
7429
}
7430
}
7431
7432
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7433
{
7434
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7435
while (lastOffset < size)
7436
{
7437
// Find next non-null allocation or move nextAllocIndex to the end.
7438
while (nextAlloc2ndIndex != SIZE_MAX &&
7439
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7440
{
7441
--nextAlloc2ndIndex;
7442
}
7443
7444
// Found non-null allocation.
7445
if (nextAlloc2ndIndex != SIZE_MAX)
7446
{
7447
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7448
7449
// 1. Process free space before this allocation.
7450
if (lastOffset < suballoc.offset)
7451
{
7452
// There is free space from lastOffset to suballoc.offset.
7453
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7454
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7455
}
7456
7457
// 2. Process this allocation.
7458
// There is allocation with suballoc.offset, suballoc.size.
7459
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7460
7461
// 3. Prepare for next iteration.
7462
lastOffset = suballoc.offset + suballoc.size;
7463
--nextAlloc2ndIndex;
7464
}
7465
// We are at the end.
7466
else
7467
{
7468
// There is free space from lastOffset to size.
7469
if (lastOffset < size)
7470
{
7471
const VkDeviceSize unusedRangeSize = size - lastOffset;
7472
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7473
}
7474
7475
// End of loop.
7476
lastOffset = size;
7477
}
7478
}
7479
}
7480
}
7481
7482
void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const
7483
{
7484
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7485
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7486
const VkDeviceSize size = GetSize();
7487
const size_t suballoc1stCount = suballocations1st.size();
7488
const size_t suballoc2ndCount = suballocations2nd.size();
7489
7490
inoutStats.blockCount++;
7491
inoutStats.blockBytes += size;
7492
inoutStats.allocationBytes += size - m_SumFreeSize;
7493
7494
VkDeviceSize lastOffset = 0;
7495
7496
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7497
{
7498
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7499
size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
7500
while (lastOffset < freeSpace2ndTo1stEnd)
7501
{
7502
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7503
while (nextAlloc2ndIndex < suballoc2ndCount &&
7504
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7505
{
7506
++nextAlloc2ndIndex;
7507
}
7508
7509
// Found non-null allocation.
7510
if (nextAlloc2ndIndex < suballoc2ndCount)
7511
{
7512
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7513
7514
// Process this allocation.
7515
// There is allocation with suballoc.offset, suballoc.size.
7516
++inoutStats.allocationCount;
7517
7518
// Prepare for next iteration.
7519
lastOffset = suballoc.offset + suballoc.size;
7520
++nextAlloc2ndIndex;
7521
}
7522
// We are at the end.
7523
else
7524
{
7525
// End of loop.
7526
lastOffset = freeSpace2ndTo1stEnd;
7527
}
7528
}
7529
}
7530
7531
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
7532
const VkDeviceSize freeSpace1stTo2ndEnd =
7533
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
7534
while (lastOffset < freeSpace1stTo2ndEnd)
7535
{
7536
// Find next non-null allocation or move nextAllocIndex to the end.
7537
while (nextAlloc1stIndex < suballoc1stCount &&
7538
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7539
{
7540
++nextAlloc1stIndex;
7541
}
7542
7543
// Found non-null allocation.
7544
if (nextAlloc1stIndex < suballoc1stCount)
7545
{
7546
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7547
7548
// Process this allocation.
7549
// There is allocation with suballoc.offset, suballoc.size.
7550
++inoutStats.allocationCount;
7551
7552
// Prepare for next iteration.
7553
lastOffset = suballoc.offset + suballoc.size;
7554
++nextAlloc1stIndex;
7555
}
7556
// We are at the end.
7557
else
7558
{
7559
// End of loop.
7560
lastOffset = freeSpace1stTo2ndEnd;
7561
}
7562
}
7563
7564
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7565
{
7566
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7567
while (lastOffset < size)
7568
{
7569
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7570
while (nextAlloc2ndIndex != SIZE_MAX &&
7571
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7572
{
7573
--nextAlloc2ndIndex;
7574
}
7575
7576
// Found non-null allocation.
7577
if (nextAlloc2ndIndex != SIZE_MAX)
7578
{
7579
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7580
7581
// Process this allocation.
7582
// There is allocation with suballoc.offset, suballoc.size.
7583
++inoutStats.allocationCount;
7584
7585
// Prepare for next iteration.
7586
lastOffset = suballoc.offset + suballoc.size;
7587
--nextAlloc2ndIndex;
7588
}
7589
// We are at the end.
7590
else
7591
{
7592
// End of loop.
7593
lastOffset = size;
7594
}
7595
}
7596
}
7597
}
7598
7599
#if VMA_STATS_STRING_ENABLED
7600
void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
7601
{
7602
const VkDeviceSize size = GetSize();
7603
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7604
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7605
const size_t suballoc1stCount = suballocations1st.size();
7606
const size_t suballoc2ndCount = suballocations2nd.size();
7607
7608
// FIRST PASS
7609
7610
size_t unusedRangeCount = 0;
7611
VkDeviceSize usedBytes = 0;
7612
7613
VkDeviceSize lastOffset = 0;
7614
7615
size_t alloc2ndCount = 0;
7616
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7617
{
7618
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7619
size_t nextAlloc2ndIndex = 0;
7620
while (lastOffset < freeSpace2ndTo1stEnd)
7621
{
7622
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7623
while (nextAlloc2ndIndex < suballoc2ndCount &&
7624
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7625
{
7626
++nextAlloc2ndIndex;
7627
}
7628
7629
// Found non-null allocation.
7630
if (nextAlloc2ndIndex < suballoc2ndCount)
7631
{
7632
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7633
7634
// 1. Process free space before this allocation.
7635
if (lastOffset < suballoc.offset)
7636
{
7637
// There is free space from lastOffset to suballoc.offset.
7638
++unusedRangeCount;
7639
}
7640
7641
// 2. Process this allocation.
7642
// There is allocation with suballoc.offset, suballoc.size.
7643
++alloc2ndCount;
7644
usedBytes += suballoc.size;
7645
7646
// 3. Prepare for next iteration.
7647
lastOffset = suballoc.offset + suballoc.size;
7648
++nextAlloc2ndIndex;
7649
}
7650
// We are at the end.
7651
else
7652
{
7653
if (lastOffset < freeSpace2ndTo1stEnd)
7654
{
7655
// There is free space from lastOffset to freeSpace2ndTo1stEnd.
7656
++unusedRangeCount;
7657
}
7658
7659
// End of loop.
7660
lastOffset = freeSpace2ndTo1stEnd;
7661
}
7662
}
7663
}
7664
7665
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
7666
size_t alloc1stCount = 0;
7667
const VkDeviceSize freeSpace1stTo2ndEnd =
7668
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
7669
while (lastOffset < freeSpace1stTo2ndEnd)
7670
{
7671
// Find next non-null allocation or move nextAllocIndex to the end.
7672
while (nextAlloc1stIndex < suballoc1stCount &&
7673
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7674
{
7675
++nextAlloc1stIndex;
7676
}
7677
7678
// Found non-null allocation.
7679
if (nextAlloc1stIndex < suballoc1stCount)
7680
{
7681
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7682
7683
// 1. Process free space before this allocation.
7684
if (lastOffset < suballoc.offset)
7685
{
7686
// There is free space from lastOffset to suballoc.offset.
7687
++unusedRangeCount;
7688
}
7689
7690
// 2. Process this allocation.
7691
// There is allocation with suballoc.offset, suballoc.size.
7692
++alloc1stCount;
7693
usedBytes += suballoc.size;
7694
7695
// 3. Prepare for next iteration.
7696
lastOffset = suballoc.offset + suballoc.size;
7697
++nextAlloc1stIndex;
7698
}
7699
// We are at the end.
7700
else
7701
{
7702
if (lastOffset < freeSpace1stTo2ndEnd)
7703
{
7704
// There is free space from lastOffset to freeSpace1stTo2ndEnd.
7705
++unusedRangeCount;
7706
}
7707
7708
// End of loop.
7709
lastOffset = freeSpace1stTo2ndEnd;
7710
}
7711
}
7712
7713
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7714
{
7715
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7716
while (lastOffset < size)
7717
{
7718
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7719
while (nextAlloc2ndIndex != SIZE_MAX &&
7720
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7721
{
7722
--nextAlloc2ndIndex;
7723
}
7724
7725
// Found non-null allocation.
7726
if (nextAlloc2ndIndex != SIZE_MAX)
7727
{
7728
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7729
7730
// 1. Process free space before this allocation.
7731
if (lastOffset < suballoc.offset)
7732
{
7733
// There is free space from lastOffset to suballoc.offset.
7734
++unusedRangeCount;
7735
}
7736
7737
// 2. Process this allocation.
7738
// There is allocation with suballoc.offset, suballoc.size.
7739
++alloc2ndCount;
7740
usedBytes += suballoc.size;
7741
7742
// 3. Prepare for next iteration.
7743
lastOffset = suballoc.offset + suballoc.size;
7744
--nextAlloc2ndIndex;
7745
}
7746
// We are at the end.
7747
else
7748
{
7749
if (lastOffset < size)
7750
{
7751
// There is free space from lastOffset to size.
7752
++unusedRangeCount;
7753
}
7754
7755
// End of loop.
7756
lastOffset = size;
7757
}
7758
}
7759
}
7760
7761
const VkDeviceSize unusedBytes = size - usedBytes;
7762
PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
7763
7764
// SECOND PASS
7765
lastOffset = 0;
7766
7767
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7768
{
7769
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7770
size_t nextAlloc2ndIndex = 0;
7771
while (lastOffset < freeSpace2ndTo1stEnd)
7772
{
7773
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7774
while (nextAlloc2ndIndex < suballoc2ndCount &&
7775
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7776
{
7777
++nextAlloc2ndIndex;
7778
}
7779
7780
// Found non-null allocation.
7781
if (nextAlloc2ndIndex < suballoc2ndCount)
7782
{
7783
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7784
7785
// 1. Process free space before this allocation.
7786
if (lastOffset < suballoc.offset)
7787
{
7788
// There is free space from lastOffset to suballoc.offset.
7789
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7790
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7791
}
7792
7793
// 2. Process this allocation.
7794
// There is allocation with suballoc.offset, suballoc.size.
7795
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
7796
7797
// 3. Prepare for next iteration.
7798
lastOffset = suballoc.offset + suballoc.size;
7799
++nextAlloc2ndIndex;
7800
}
7801
// We are at the end.
7802
else
7803
{
7804
if (lastOffset < freeSpace2ndTo1stEnd)
7805
{
7806
// There is free space from lastOffset to freeSpace2ndTo1stEnd.
7807
const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
7808
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7809
}
7810
7811
// End of loop.
7812
lastOffset = freeSpace2ndTo1stEnd;
7813
}
7814
}
7815
}
7816
7817
nextAlloc1stIndex = m_1stNullItemsBeginCount;
7818
while (lastOffset < freeSpace1stTo2ndEnd)
7819
{
7820
// Find next non-null allocation or move nextAllocIndex to the end.
7821
while (nextAlloc1stIndex < suballoc1stCount &&
7822
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7823
{
7824
++nextAlloc1stIndex;
7825
}
7826
7827
// Found non-null allocation.
7828
if (nextAlloc1stIndex < suballoc1stCount)
7829
{
7830
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7831
7832
// 1. Process free space before this allocation.
7833
if (lastOffset < suballoc.offset)
7834
{
7835
// There is free space from lastOffset to suballoc.offset.
7836
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7837
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7838
}
7839
7840
// 2. Process this allocation.
7841
// There is allocation with suballoc.offset, suballoc.size.
7842
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
7843
7844
// 3. Prepare for next iteration.
7845
lastOffset = suballoc.offset + suballoc.size;
7846
++nextAlloc1stIndex;
7847
}
7848
// We are at the end.
7849
else
7850
{
7851
if (lastOffset < freeSpace1stTo2ndEnd)
7852
{
7853
// There is free space from lastOffset to freeSpace1stTo2ndEnd.
7854
const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
7855
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7856
}
7857
7858
// End of loop.
7859
lastOffset = freeSpace1stTo2ndEnd;
7860
}
7861
}
7862
7863
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7864
{
7865
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7866
while (lastOffset < size)
7867
{
7868
// Find next non-null allocation or move nextAlloc2ndIndex to the end.
7869
while (nextAlloc2ndIndex != SIZE_MAX &&
7870
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7871
{
7872
--nextAlloc2ndIndex;
7873
}
7874
7875
// Found non-null allocation.
7876
if (nextAlloc2ndIndex != SIZE_MAX)
7877
{
7878
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7879
7880
// 1. Process free space before this allocation.
7881
if (lastOffset < suballoc.offset)
7882
{
7883
// There is free space from lastOffset to suballoc.offset.
7884
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7885
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7886
}
7887
7888
// 2. Process this allocation.
7889
// There is allocation with suballoc.offset, suballoc.size.
7890
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
7891
7892
// 3. Prepare for next iteration.
7893
lastOffset = suballoc.offset + suballoc.size;
7894
--nextAlloc2ndIndex;
7895
}
7896
// We are at the end.
7897
else
7898
{
7899
if (lastOffset < size)
7900
{
7901
// There is free space from lastOffset to size.
7902
const VkDeviceSize unusedRangeSize = size - lastOffset;
7903
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
7904
}
7905
7906
// End of loop.
7907
lastOffset = size;
7908
}
7909
}
7910
}
7911
7912
PrintDetailedMap_End(json);
7913
}
7914
#endif // VMA_STATS_STRING_ENABLED
7915
7916
bool VmaBlockMetadata_Linear::CreateAllocationRequest(
7917
VkDeviceSize allocSize,
7918
VkDeviceSize allocAlignment,
7919
bool upperAddress,
7920
VmaSuballocationType allocType,
7921
uint32_t strategy,
7922
VmaAllocationRequest* pAllocationRequest)
7923
{
7924
VMA_ASSERT(allocSize > 0);
7925
VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7926
VMA_ASSERT(pAllocationRequest != VMA_NULL);
7927
VMA_HEAVY_ASSERT(Validate());
7928
7929
if(allocSize > GetSize())
7930
return false;
7931
7932
pAllocationRequest->size = allocSize;
7933
return upperAddress ?
7934
CreateAllocationRequest_UpperAddress(
7935
allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :
7936
CreateAllocationRequest_LowerAddress(
7937
allocSize, allocAlignment, allocType, strategy, pAllocationRequest);
7938
}
7939
7940
VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
7941
{
7942
VMA_ASSERT(!IsVirtual());
7943
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7944
for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
7945
{
7946
const VmaSuballocation& suballoc = suballocations1st[i];
7947
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7948
{
7949
if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
7950
{
7951
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
7952
return VK_ERROR_UNKNOWN_COPY;
7953
}
7954
}
7955
}
7956
7957
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7958
for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
7959
{
7960
const VmaSuballocation& suballoc = suballocations2nd[i];
7961
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7962
{
7963
if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
7964
{
7965
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
7966
return VK_ERROR_UNKNOWN_COPY;
7967
}
7968
}
7969
}
7970
7971
return VK_SUCCESS;
7972
}
7973
7974
void VmaBlockMetadata_Linear::Alloc(
7975
const VmaAllocationRequest& request,
7976
VmaSuballocationType type,
7977
void* userData)
7978
{
7979
const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
7980
const VmaSuballocation newSuballoc = { offset, request.size, userData, type };
7981
7982
switch (request.type)
7983
{
7984
case VmaAllocationRequestType::UpperAddress:
7985
{
7986
VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
7987
"CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
7988
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7989
suballocations2nd.push_back(newSuballoc);
7990
m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
7991
}
7992
break;
7993
case VmaAllocationRequestType::EndOf1st:
7994
{
7995
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7996
7997
VMA_ASSERT(suballocations1st.empty() ||
7998
offset >= suballocations1st.back().offset + suballocations1st.back().size);
7999
// Check if it fits before the end of the block.
8000
VMA_ASSERT(offset + request.size <= GetSize());
8001
8002
suballocations1st.push_back(newSuballoc);
8003
}
8004
break;
8005
case VmaAllocationRequestType::EndOf2nd:
8006
{
8007
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8008
// New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
8009
VMA_ASSERT(!suballocations1st.empty() &&
8010
offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
8011
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8012
8013
switch (m_2ndVectorMode)
8014
{
8015
case SECOND_VECTOR_EMPTY:
8016
// First allocation from second part ring buffer.
8017
VMA_ASSERT(suballocations2nd.empty());
8018
m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
8019
break;
8020
case SECOND_VECTOR_RING_BUFFER:
8021
// 2-part ring buffer is already started.
8022
VMA_ASSERT(!suballocations2nd.empty());
8023
break;
8024
case SECOND_VECTOR_DOUBLE_STACK:
8025
VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
8026
break;
8027
default:
8028
VMA_ASSERT(0);
8029
}
8030
8031
suballocations2nd.push_back(newSuballoc);
8032
}
8033
break;
8034
default:
8035
VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
8036
}
8037
8038
m_SumFreeSize -= newSuballoc.size;
8039
}
8040
8041
void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)
8042
{
8043
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8044
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8045
VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;
8046
8047
if (!suballocations1st.empty())
8048
{
8049
// First allocation: Mark it as next empty at the beginning.
8050
VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8051
if (firstSuballoc.offset == offset)
8052
{
8053
firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8054
firstSuballoc.userData = VMA_NULL;
8055
m_SumFreeSize += firstSuballoc.size;
8056
++m_1stNullItemsBeginCount;
8057
CleanupAfterFree();
8058
return;
8059
}
8060
}
8061
8062
// Last allocation in 2-part ring buffer or top of upper stack (same logic).
8063
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
8064
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8065
{
8066
VmaSuballocation& lastSuballoc = suballocations2nd.back();
8067
if (lastSuballoc.offset == offset)
8068
{
8069
m_SumFreeSize += lastSuballoc.size;
8070
suballocations2nd.pop_back();
8071
CleanupAfterFree();
8072
return;
8073
}
8074
}
8075
// Last allocation in 1st vector.
8076
else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
8077
{
8078
VmaSuballocation& lastSuballoc = suballocations1st.back();
8079
if (lastSuballoc.offset == offset)
8080
{
8081
m_SumFreeSize += lastSuballoc.size;
8082
suballocations1st.pop_back();
8083
CleanupAfterFree();
8084
return;
8085
}
8086
}
8087
8088
VmaSuballocation refSuballoc;
8089
refSuballoc.offset = offset;
8090
// Rest of members stays uninitialized intentionally for better performance.
8091
8092
// Item from the middle of 1st vector.
8093
{
8094
const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
8095
suballocations1st.begin() + m_1stNullItemsBeginCount,
8096
suballocations1st.end(),
8097
refSuballoc,
8098
VmaSuballocationOffsetLess());
8099
if (it != suballocations1st.end())
8100
{
8101
it->type = VMA_SUBALLOCATION_TYPE_FREE;
8102
it->userData = VMA_NULL;
8103
++m_1stNullItemsMiddleCount;
8104
m_SumFreeSize += it->size;
8105
CleanupAfterFree();
8106
return;
8107
}
8108
}
8109
8110
if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8111
{
8112
// Item from the middle of 2nd vector.
8113
const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8114
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8115
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8116
if (it != suballocations2nd.end())
8117
{
8118
it->type = VMA_SUBALLOCATION_TYPE_FREE;
8119
it->userData = VMA_NULL;
8120
++m_2ndNullItemsCount;
8121
m_SumFreeSize += it->size;
8122
CleanupAfterFree();
8123
return;
8124
}
8125
}
8126
8127
VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
8128
}
8129
8130
void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
8131
{
8132
outInfo.offset = (VkDeviceSize)allocHandle - 1;
8133
VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);
8134
outInfo.size = suballoc.size;
8135
outInfo.pUserData = suballoc.userData;
8136
}
8137
8138
void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const
8139
{
8140
return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;
8141
}
8142
8143
VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const
8144
{
8145
// Function only used for defragmentation, which is disabled for this algorithm
8146
VMA_ASSERT(0);
8147
return VK_NULL_HANDLE;
8148
}
8149
8150
VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const
8151
{
8152
// Function only used for defragmentation, which is disabled for this algorithm
8153
VMA_ASSERT(0);
8154
return VK_NULL_HANDLE;
8155
}
8156
8157
VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const
8158
{
8159
// Function only used for defragmentation, which is disabled for this algorithm
8160
VMA_ASSERT(0);
8161
return 0;
8162
}
8163
8164
void VmaBlockMetadata_Linear::Clear()
8165
{
8166
m_SumFreeSize = GetSize();
8167
m_Suballocations0.clear();
8168
m_Suballocations1.clear();
8169
// Leaving m_1stVectorIndex unchanged - it doesn't matter.
8170
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8171
m_1stNullItemsBeginCount = 0;
8172
m_1stNullItemsMiddleCount = 0;
8173
m_2ndNullItemsCount = 0;
8174
}
8175
8176
void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
8177
{
8178
VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);
8179
suballoc.userData = userData;
8180
}
8181
8182
void VmaBlockMetadata_Linear::DebugLogAllAllocations() const
8183
{
8184
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8185
for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)
8186
if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8187
DebugLogAllocation(it->offset, it->size, it->userData);
8188
8189
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8190
for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)
8191
if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8192
DebugLogAllocation(it->offset, it->size, it->userData);
8193
}
8194
8195
VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const
8196
{
8197
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8198
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8199
8200
VmaSuballocation refSuballoc;
8201
refSuballoc.offset = offset;
8202
// Rest of members stays uninitialized intentionally for better performance.
8203
8204
// Item from the 1st vector.
8205
{
8206
SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(
8207
suballocations1st.begin() + m_1stNullItemsBeginCount,
8208
suballocations1st.end(),
8209
refSuballoc,
8210
VmaSuballocationOffsetLess());
8211
if (it != suballocations1st.end())
8212
{
8213
return const_cast<VmaSuballocation&>(*it);
8214
}
8215
}
8216
8217
if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8218
{
8219
// Rest of members stays uninitialized intentionally for better performance.
8220
SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8221
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8222
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8223
if (it != suballocations2nd.end())
8224
{
8225
return const_cast<VmaSuballocation&>(*it);
8226
}
8227
}
8228
8229
VMA_ASSERT(0 && "Allocation not found in linear allocator!");
8230
return const_cast<VmaSuballocation&>(suballocations1st.back()); // Should never occur.
8231
}
8232
8233
bool VmaBlockMetadata_Linear::ShouldCompact1st() const
8234
{
8235
const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8236
const size_t suballocCount = AccessSuballocations1st().size();
8237
return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
8238
}
8239
8240
void VmaBlockMetadata_Linear::CleanupAfterFree()
8241
{
8242
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8243
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8244
8245
if (IsEmpty())
8246
{
8247
suballocations1st.clear();
8248
suballocations2nd.clear();
8249
m_1stNullItemsBeginCount = 0;
8250
m_1stNullItemsMiddleCount = 0;
8251
m_2ndNullItemsCount = 0;
8252
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8253
}
8254
else
8255
{
8256
const size_t suballoc1stCount = suballocations1st.size();
8257
const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8258
VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
8259
8260
// Find more null items at the beginning of 1st vector.
8261
while (m_1stNullItemsBeginCount < suballoc1stCount &&
8262
suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8263
{
8264
++m_1stNullItemsBeginCount;
8265
--m_1stNullItemsMiddleCount;
8266
}
8267
8268
// Find more null items at the end of 1st vector.
8269
while (m_1stNullItemsMiddleCount > 0 &&
8270
suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8271
{
8272
--m_1stNullItemsMiddleCount;
8273
suballocations1st.pop_back();
8274
}
8275
8276
// Find more null items at the end of 2nd vector.
8277
while (m_2ndNullItemsCount > 0 &&
8278
suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8279
{
8280
--m_2ndNullItemsCount;
8281
suballocations2nd.pop_back();
8282
}
8283
8284
// Find more null items at the beginning of 2nd vector.
8285
while (m_2ndNullItemsCount > 0 &&
8286
suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
8287
{
8288
--m_2ndNullItemsCount;
8289
VmaVectorRemove(suballocations2nd, 0);
8290
}
8291
8292
if (ShouldCompact1st())
8293
{
8294
const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
8295
size_t srcIndex = m_1stNullItemsBeginCount;
8296
for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
8297
{
8298
while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
8299
{
8300
++srcIndex;
8301
}
8302
if (dstIndex != srcIndex)
8303
{
8304
suballocations1st[dstIndex] = suballocations1st[srcIndex];
8305
}
8306
++srcIndex;
8307
}
8308
suballocations1st.resize(nonNullItemCount);
8309
m_1stNullItemsBeginCount = 0;
8310
m_1stNullItemsMiddleCount = 0;
8311
}
8312
8313
// 2nd vector became empty.
8314
if (suballocations2nd.empty())
8315
{
8316
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8317
}
8318
8319
// 1st vector became empty.
8320
if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
8321
{
8322
suballocations1st.clear();
8323
m_1stNullItemsBeginCount = 0;
8324
8325
if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8326
{
8327
// Swap 1st with 2nd. Now 2nd is empty.
8328
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8329
m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
8330
while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
8331
suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8332
{
8333
++m_1stNullItemsBeginCount;
8334
--m_1stNullItemsMiddleCount;
8335
}
8336
m_2ndNullItemsCount = 0;
8337
m_1stVectorIndex ^= 1;
8338
}
8339
}
8340
}
8341
8342
VMA_HEAVY_ASSERT(Validate());
8343
}
8344
8345
bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
8346
VkDeviceSize allocSize,
8347
VkDeviceSize allocAlignment,
8348
VmaSuballocationType allocType,
8349
uint32_t strategy,
8350
VmaAllocationRequest* pAllocationRequest)
8351
{
8352
const VkDeviceSize blockSize = GetSize();
8353
const VkDeviceSize debugMargin = GetDebugMargin();
8354
const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
8355
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8356
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8357
8358
if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8359
{
8360
// Try to allocate at the end of 1st vector.
8361
8362
VkDeviceSize resultBaseOffset = 0;
8363
if (!suballocations1st.empty())
8364
{
8365
const VmaSuballocation& lastSuballoc = suballocations1st.back();
8366
resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
8367
}
8368
8369
// Start from offset equal to beginning of free space.
8370
VkDeviceSize resultOffset = resultBaseOffset;
8371
8372
// Apply alignment.
8373
resultOffset = VmaAlignUp(resultOffset, allocAlignment);
8374
8375
// Check previous suballocations for BufferImageGranularity conflicts.
8376
// Make bigger alignment if necessary.
8377
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
8378
{
8379
bool bufferImageGranularityConflict = false;
8380
for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
8381
{
8382
const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
8383
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
8384
{
8385
if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8386
{
8387
bufferImageGranularityConflict = true;
8388
break;
8389
}
8390
}
8391
else
8392
// Already on previous page.
8393
break;
8394
}
8395
if (bufferImageGranularityConflict)
8396
{
8397
resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
8398
}
8399
}
8400
8401
const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
8402
suballocations2nd.back().offset : blockSize;
8403
8404
// There is enough free space at the end after alignment.
8405
if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)
8406
{
8407
// Check next suballocations for BufferImageGranularity conflicts.
8408
// If conflict exists, allocation cannot be made here.
8409
if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8410
{
8411
for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
8412
{
8413
const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
8414
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8415
{
8416
if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8417
{
8418
return false;
8419
}
8420
}
8421
else
8422
{
8423
// Already on previous page.
8424
break;
8425
}
8426
}
8427
}
8428
8429
// All tests passed: Success.
8430
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
8431
// pAllocationRequest->item, customData unused.
8432
pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
8433
return true;
8434
}
8435
}
8436
8437
// Wrap-around to end of 2nd vector. Try to allocate there, watching for the
8438
// beginning of 1st vector as the end of free space.
8439
if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8440
{
8441
VMA_ASSERT(!suballocations1st.empty());
8442
8443
VkDeviceSize resultBaseOffset = 0;
8444
if (!suballocations2nd.empty())
8445
{
8446
const VmaSuballocation& lastSuballoc = suballocations2nd.back();
8447
resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
8448
}
8449
8450
// Start from offset equal to beginning of free space.
8451
VkDeviceSize resultOffset = resultBaseOffset;
8452
8453
// Apply alignment.
8454
resultOffset = VmaAlignUp(resultOffset, allocAlignment);
8455
8456
// Check previous suballocations for BufferImageGranularity conflicts.
8457
// Make bigger alignment if necessary.
8458
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
8459
{
8460
bool bufferImageGranularityConflict = false;
8461
for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
8462
{
8463
const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
8464
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
8465
{
8466
if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8467
{
8468
bufferImageGranularityConflict = true;
8469
break;
8470
}
8471
}
8472
else
8473
// Already on previous page.
8474
break;
8475
}
8476
if (bufferImageGranularityConflict)
8477
{
8478
resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
8479
}
8480
}
8481
8482
size_t index1st = m_1stNullItemsBeginCount;
8483
8484
// There is enough free space at the end after alignment.
8485
if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
8486
(index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
8487
{
8488
// Check next suballocations for BufferImageGranularity conflicts.
8489
// If conflict exists, allocation cannot be made here.
8490
if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
8491
{
8492
for (size_t nextSuballocIndex = index1st;
8493
nextSuballocIndex < suballocations1st.size();
8494
nextSuballocIndex++)
8495
{
8496
const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
8497
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8498
{
8499
if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8500
{
8501
return false;
8502
}
8503
}
8504
else
8505
{
8506
// Already on next page.
8507
break;
8508
}
8509
}
8510
}
8511
8512
// All tests passed: Success.
8513
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
8514
pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
8515
// pAllocationRequest->item, customData unused.
8516
return true;
8517
}
8518
}
8519
8520
return false;
8521
}
8522
8523
bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
8524
VkDeviceSize allocSize,
8525
VkDeviceSize allocAlignment,
8526
VmaSuballocationType allocType,
8527
uint32_t strategy,
8528
VmaAllocationRequest* pAllocationRequest)
8529
{
8530
const VkDeviceSize blockSize = GetSize();
8531
const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
8532
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8533
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8534
8535
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8536
{
8537
VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
8538
return false;
8539
}
8540
8541
// Try to allocate before 2nd.back(), or end of block if 2nd.empty().
8542
if (allocSize > blockSize)
8543
{
8544
return false;
8545
}
8546
VkDeviceSize resultBaseOffset = blockSize - allocSize;
8547
if (!suballocations2nd.empty())
8548
{
8549
const VmaSuballocation& lastSuballoc = suballocations2nd.back();
8550
resultBaseOffset = lastSuballoc.offset - allocSize;
8551
if (allocSize > lastSuballoc.offset)
8552
{
8553
return false;
8554
}
8555
}
8556
8557
// Start from offset equal to end of free space.
8558
VkDeviceSize resultOffset = resultBaseOffset;
8559
8560
const VkDeviceSize debugMargin = GetDebugMargin();
8561
8562
// Apply debugMargin at the end.
8563
if (debugMargin > 0)
8564
{
8565
if (resultOffset < debugMargin)
8566
{
8567
return false;
8568
}
8569
resultOffset -= debugMargin;
8570
}
8571
8572
// Apply alignment.
8573
resultOffset = VmaAlignDown(resultOffset, allocAlignment);
8574
8575
// Check next suballocations from 2nd for BufferImageGranularity conflicts.
8576
// Make bigger alignment if necessary.
8577
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
8578
{
8579
bool bufferImageGranularityConflict = false;
8580
for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
8581
{
8582
const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
8583
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8584
{
8585
if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
8586
{
8587
bufferImageGranularityConflict = true;
8588
break;
8589
}
8590
}
8591
else
8592
// Already on previous page.
8593
break;
8594
}
8595
if (bufferImageGranularityConflict)
8596
{
8597
resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
8598
}
8599
}
8600
8601
// There is enough free space.
8602
const VkDeviceSize endOf1st = !suballocations1st.empty() ?
8603
suballocations1st.back().offset + suballocations1st.back().size :
8604
0;
8605
if (endOf1st + debugMargin <= resultOffset)
8606
{
8607
// Check previous suballocations for BufferImageGranularity conflicts.
8608
// If conflict exists, allocation cannot be made here.
8609
if (bufferImageGranularity > 1)
8610
{
8611
for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
8612
{
8613
const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
8614
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
8615
{
8616
if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
8617
{
8618
return false;
8619
}
8620
}
8621
else
8622
{
8623
// Already on next page.
8624
break;
8625
}
8626
}
8627
}
8628
8629
// All tests passed: Success.
8630
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
8631
// pAllocationRequest->item unused.
8632
pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
8633
return true;
8634
}
8635
8636
return false;
8637
}
8638
#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
8639
#endif // _VMA_BLOCK_METADATA_LINEAR
8640
8641
#ifndef _VMA_BLOCK_METADATA_TLSF
8642
// To not search current larger region if first allocation won't succeed and skip to smaller range
8643
// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest().
8644
// When fragmentation and reusal of previous blocks doesn't matter then use with
8645
// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible.
8646
class VmaBlockMetadata_TLSF : public VmaBlockMetadata
8647
{
8648
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_TLSF)
8649
public:
8650
VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
8651
VkDeviceSize bufferImageGranularity, bool isVirtual);
8652
virtual ~VmaBlockMetadata_TLSF();
8653
8654
size_t GetAllocationCount() const override { return m_AllocCount; }
8655
size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }
8656
VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }
8657
bool IsEmpty() const override { return m_NullBlock->offset == 0; }
8658
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }
8659
8660
void Init(VkDeviceSize size) override;
8661
bool Validate() const override;
8662
8663
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
8664
void AddStatistics(VmaStatistics& inoutStats) const override;
8665
8666
#if VMA_STATS_STRING_ENABLED
8667
void PrintDetailedMap(class VmaJsonWriter& json) const override;
8668
#endif
8669
8670
bool CreateAllocationRequest(
8671
VkDeviceSize allocSize,
8672
VkDeviceSize allocAlignment,
8673
bool upperAddress,
8674
VmaSuballocationType allocType,
8675
uint32_t strategy,
8676
VmaAllocationRequest* pAllocationRequest) override;
8677
8678
VkResult CheckCorruption(const void* pBlockData) override;
8679
void Alloc(
8680
const VmaAllocationRequest& request,
8681
VmaSuballocationType type,
8682
void* userData) override;
8683
8684
void Free(VmaAllocHandle allocHandle) override;
8685
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
8686
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
8687
VmaAllocHandle GetAllocationListBegin() const override;
8688
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
8689
VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
8690
void Clear() override;
8691
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
8692
void DebugLogAllAllocations() const override;
8693
8694
private:
8695
// According to original paper it should be preferable 4 or 5:
8696
// M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"
8697
// http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf
8698
static const uint8_t SECOND_LEVEL_INDEX = 5;
8699
static const uint16_t SMALL_BUFFER_SIZE = 256;
8700
static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;
8701
static const uint8_t MEMORY_CLASS_SHIFT = 7;
8702
static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;
8703
8704
class Block
8705
{
8706
public:
8707
VkDeviceSize offset;
8708
VkDeviceSize size;
8709
Block* prevPhysical;
8710
Block* nextPhysical;
8711
8712
void MarkFree() { prevFree = VMA_NULL; }
8713
void MarkTaken() { prevFree = this; }
8714
bool IsFree() const { return prevFree != this; }
8715
void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }
8716
Block*& PrevFree() { return prevFree; }
8717
Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }
8718
8719
private:
8720
Block* prevFree; // Address of the same block here indicates that block is taken
8721
union
8722
{
8723
Block* nextFree;
8724
void* userData;
8725
};
8726
};
8727
8728
size_t m_AllocCount;
8729
// Total number of free blocks besides null block
8730
size_t m_BlocksFreeCount;
8731
// Total size of free blocks excluding null block
8732
VkDeviceSize m_BlocksFreeSize;
8733
uint32_t m_IsFreeBitmap;
8734
uint8_t m_MemoryClasses;
8735
uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];
8736
uint32_t m_ListsCount;
8737
/*
8738
* 0: 0-3 lists for small buffers
8739
* 1+: 0-(2^SLI-1) lists for normal buffers
8740
*/
8741
Block** m_FreeList;
8742
VmaPoolAllocator<Block> m_BlockAllocator;
8743
Block* m_NullBlock;
8744
VmaBlockBufferImageGranularity m_GranularityHandler;
8745
8746
uint8_t SizeToMemoryClass(VkDeviceSize size) const;
8747
uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;
8748
uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;
8749
uint32_t GetListIndex(VkDeviceSize size) const;
8750
8751
void RemoveFreeBlock(Block* block);
8752
void InsertFreeBlock(Block* block);
8753
void MergeBlock(Block* block, Block* prev);
8754
8755
Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;
8756
bool CheckBlock(
8757
Block& block,
8758
uint32_t listIndex,
8759
VkDeviceSize allocSize,
8760
VkDeviceSize allocAlignment,
8761
VmaSuballocationType allocType,
8762
VmaAllocationRequest* pAllocationRequest);
8763
};
8764
8765
#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
8766
VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
8767
VkDeviceSize bufferImageGranularity, bool isVirtual)
8768
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
8769
m_AllocCount(0),
8770
m_BlocksFreeCount(0),
8771
m_BlocksFreeSize(0),
8772
m_IsFreeBitmap(0),
8773
m_MemoryClasses(0),
8774
m_ListsCount(0),
8775
m_FreeList(VMA_NULL),
8776
m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),
8777
m_NullBlock(VMA_NULL),
8778
m_GranularityHandler(bufferImageGranularity) {}
8779
8780
VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()
8781
{
8782
if (m_FreeList)
8783
vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);
8784
m_GranularityHandler.Destroy(GetAllocationCallbacks());
8785
}
8786
8787
void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)
8788
{
8789
VmaBlockMetadata::Init(size);
8790
8791
if (!IsVirtual())
8792
m_GranularityHandler.Init(GetAllocationCallbacks(), size);
8793
8794
m_NullBlock = m_BlockAllocator.Alloc();
8795
m_NullBlock->size = size;
8796
m_NullBlock->offset = 0;
8797
m_NullBlock->prevPhysical = VMA_NULL;
8798
m_NullBlock->nextPhysical = VMA_NULL;
8799
m_NullBlock->MarkFree();
8800
m_NullBlock->NextFree() = VMA_NULL;
8801
m_NullBlock->PrevFree() = VMA_NULL;
8802
uint8_t memoryClass = SizeToMemoryClass(size);
8803
uint16_t sli = SizeToSecondIndex(size, memoryClass);
8804
m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;
8805
if (IsVirtual())
8806
m_ListsCount += 1UL << SECOND_LEVEL_INDEX;
8807
else
8808
m_ListsCount += 4;
8809
8810
m_MemoryClasses = memoryClass + uint8_t(2);
8811
memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));
8812
8813
m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);
8814
memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
8815
}
8816
8817
bool VmaBlockMetadata_TLSF::Validate() const
8818
{
8819
VMA_VALIDATE(GetSumFreeSize() <= GetSize());
8820
8821
VkDeviceSize calculatedSize = m_NullBlock->size;
8822
VkDeviceSize calculatedFreeSize = m_NullBlock->size;
8823
size_t allocCount = 0;
8824
size_t freeCount = 0;
8825
8826
// Check integrity of free lists
8827
for (uint32_t list = 0; list < m_ListsCount; ++list)
8828
{
8829
Block* block = m_FreeList[list];
8830
if (block != VMA_NULL)
8831
{
8832
VMA_VALIDATE(block->IsFree());
8833
VMA_VALIDATE(block->PrevFree() == VMA_NULL);
8834
while (block->NextFree())
8835
{
8836
VMA_VALIDATE(block->NextFree()->IsFree());
8837
VMA_VALIDATE(block->NextFree()->PrevFree() == block);
8838
block = block->NextFree();
8839
}
8840
}
8841
}
8842
8843
VkDeviceSize nextOffset = m_NullBlock->offset;
8844
auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());
8845
8846
VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);
8847
if (m_NullBlock->prevPhysical)
8848
{
8849
VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);
8850
}
8851
// Check all blocks
8852
for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)
8853
{
8854
VMA_VALIDATE(prev->offset + prev->size == nextOffset);
8855
nextOffset = prev->offset;
8856
calculatedSize += prev->size;
8857
8858
uint32_t listIndex = GetListIndex(prev->size);
8859
if (prev->IsFree())
8860
{
8861
++freeCount;
8862
// Check if free block belongs to free list
8863
Block* freeBlock = m_FreeList[listIndex];
8864
VMA_VALIDATE(freeBlock != VMA_NULL);
8865
8866
bool found = false;
8867
do
8868
{
8869
if (freeBlock == prev)
8870
found = true;
8871
8872
freeBlock = freeBlock->NextFree();
8873
} while (!found && freeBlock != VMA_NULL);
8874
8875
VMA_VALIDATE(found);
8876
calculatedFreeSize += prev->size;
8877
}
8878
else
8879
{
8880
++allocCount;
8881
// Check if taken block is not on a free list
8882
Block* freeBlock = m_FreeList[listIndex];
8883
while (freeBlock)
8884
{
8885
VMA_VALIDATE(freeBlock != prev);
8886
freeBlock = freeBlock->NextFree();
8887
}
8888
8889
if (!IsVirtual())
8890
{
8891
VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));
8892
}
8893
}
8894
8895
if (prev->prevPhysical)
8896
{
8897
VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);
8898
}
8899
}
8900
8901
if (!IsVirtual())
8902
{
8903
VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));
8904
}
8905
8906
VMA_VALIDATE(nextOffset == 0);
8907
VMA_VALIDATE(calculatedSize == GetSize());
8908
VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());
8909
VMA_VALIDATE(allocCount == m_AllocCount);
8910
VMA_VALIDATE(freeCount == m_BlocksFreeCount);
8911
8912
return true;
8913
}
8914
8915
void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
8916
{
8917
inoutStats.statistics.blockCount++;
8918
inoutStats.statistics.blockBytes += GetSize();
8919
if (m_NullBlock->size > 0)
8920
VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);
8921
8922
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
8923
{
8924
if (block->IsFree())
8925
VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);
8926
else
8927
VmaAddDetailedStatisticsAllocation(inoutStats, block->size);
8928
}
8929
}
8930
8931
void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const
8932
{
8933
inoutStats.blockCount++;
8934
inoutStats.allocationCount += (uint32_t)m_AllocCount;
8935
inoutStats.blockBytes += GetSize();
8936
inoutStats.allocationBytes += GetSize() - GetSumFreeSize();
8937
}
8938
8939
#if VMA_STATS_STRING_ENABLED
8940
void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const
8941
{
8942
size_t blockCount = m_AllocCount + m_BlocksFreeCount;
8943
VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
8944
VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);
8945
8946
size_t i = blockCount;
8947
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
8948
{
8949
blockList[--i] = block;
8950
}
8951
VMA_ASSERT(i == 0);
8952
8953
VmaDetailedStatistics stats;
8954
VmaClearDetailedStatistics(stats);
8955
AddDetailedStatistics(stats);
8956
8957
PrintDetailedMap_Begin(json,
8958
stats.statistics.blockBytes - stats.statistics.allocationBytes,
8959
stats.statistics.allocationCount,
8960
stats.unusedRangeCount);
8961
8962
for (; i < blockCount; ++i)
8963
{
8964
Block* block = blockList[i];
8965
if (block->IsFree())
8966
PrintDetailedMap_UnusedRange(json, block->offset, block->size);
8967
else
8968
PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());
8969
}
8970
if (m_NullBlock->size > 0)
8971
PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);
8972
8973
PrintDetailedMap_End(json);
8974
}
8975
#endif
8976
8977
bool VmaBlockMetadata_TLSF::CreateAllocationRequest(
8978
VkDeviceSize allocSize,
8979
VkDeviceSize allocAlignment,
8980
bool upperAddress,
8981
VmaSuballocationType allocType,
8982
uint32_t strategy,
8983
VmaAllocationRequest* pAllocationRequest)
8984
{
8985
VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
8986
VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
8987
8988
// For small granularity round up
8989
if (!IsVirtual())
8990
m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);
8991
8992
allocSize += GetDebugMargin();
8993
// Quick check for too small pool
8994
if (allocSize > GetSumFreeSize())
8995
return false;
8996
8997
// If no free blocks in pool then check only null block
8998
if (m_BlocksFreeCount == 0)
8999
return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);
9000
9001
// Round up to the next block
9002
VkDeviceSize sizeForNextList = allocSize;
9003
VkDeviceSize smallSizeStep = VkDeviceSize(SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4));
9004
if (allocSize > SMALL_BUFFER_SIZE)
9005
{
9006
sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));
9007
}
9008
else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)
9009
sizeForNextList = SMALL_BUFFER_SIZE + 1;
9010
else
9011
sizeForNextList += smallSizeStep;
9012
9013
uint32_t nextListIndex = m_ListsCount;
9014
uint32_t prevListIndex = m_ListsCount;
9015
Block* nextListBlock = VMA_NULL;
9016
Block* prevListBlock = VMA_NULL;
9017
9018
// Check blocks according to strategies
9019
if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)
9020
{
9021
// Quick check for larger block first
9022
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
9023
if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9024
return true;
9025
9026
// If not fitted then null block
9027
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
9028
return true;
9029
9030
// Null block failed, search larger bucket
9031
while (nextListBlock)
9032
{
9033
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9034
return true;
9035
nextListBlock = nextListBlock->NextFree();
9036
}
9037
9038
// Failed again, check best fit bucket
9039
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
9040
while (prevListBlock)
9041
{
9042
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9043
return true;
9044
prevListBlock = prevListBlock->NextFree();
9045
}
9046
}
9047
else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
9048
{
9049
// Check best fit bucket
9050
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
9051
while (prevListBlock)
9052
{
9053
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9054
return true;
9055
prevListBlock = prevListBlock->NextFree();
9056
}
9057
9058
// If failed check null block
9059
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
9060
return true;
9061
9062
// Check larger bucket
9063
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
9064
while (nextListBlock)
9065
{
9066
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9067
return true;
9068
nextListBlock = nextListBlock->NextFree();
9069
}
9070
}
9071
else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )
9072
{
9073
// Perform search from the start
9074
VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
9075
VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);
9076
9077
size_t i = m_BlocksFreeCount;
9078
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
9079
{
9080
if (block->IsFree() && block->size >= allocSize)
9081
blockList[--i] = block;
9082
}
9083
9084
for (; i < m_BlocksFreeCount; ++i)
9085
{
9086
Block& block = *blockList[i];
9087
if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))
9088
return true;
9089
}
9090
9091
// If failed check null block
9092
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
9093
return true;
9094
9095
// Whole range searched, no more memory
9096
return false;
9097
}
9098
else
9099
{
9100
// Check larger bucket
9101
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
9102
while (nextListBlock)
9103
{
9104
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9105
return true;
9106
nextListBlock = nextListBlock->NextFree();
9107
}
9108
9109
// If failed check null block
9110
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
9111
return true;
9112
9113
// Check best fit bucket
9114
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
9115
while (prevListBlock)
9116
{
9117
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9118
return true;
9119
prevListBlock = prevListBlock->NextFree();
9120
}
9121
}
9122
9123
// Worst case, full search has to be done
9124
while (++nextListIndex < m_ListsCount)
9125
{
9126
nextListBlock = m_FreeList[nextListIndex];
9127
while (nextListBlock)
9128
{
9129
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
9130
return true;
9131
nextListBlock = nextListBlock->NextFree();
9132
}
9133
}
9134
9135
// No more memory sadly
9136
return false;
9137
}
9138
9139
VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)
9140
{
9141
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
9142
{
9143
if (!block->IsFree())
9144
{
9145
if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))
9146
{
9147
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9148
return VK_ERROR_UNKNOWN_COPY;
9149
}
9150
}
9151
}
9152
9153
return VK_SUCCESS;
9154
}
9155
9156
void VmaBlockMetadata_TLSF::Alloc(
9157
const VmaAllocationRequest& request,
9158
VmaSuballocationType type,
9159
void* userData)
9160
{
9161
VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);
9162
9163
// Get block and pop it from the free list
9164
Block* currentBlock = (Block*)request.allocHandle;
9165
VkDeviceSize offset = request.algorithmData;
9166
VMA_ASSERT(currentBlock != VMA_NULL);
9167
VMA_ASSERT(currentBlock->offset <= offset);
9168
9169
if (currentBlock != m_NullBlock)
9170
RemoveFreeBlock(currentBlock);
9171
9172
VkDeviceSize debugMargin = GetDebugMargin();
9173
VkDeviceSize misssingAlignment = offset - currentBlock->offset;
9174
9175
// Append missing alignment to prev block or create new one
9176
if (misssingAlignment)
9177
{
9178
Block* prevBlock = currentBlock->prevPhysical;
9179
VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");
9180
9181
if (prevBlock->IsFree() && prevBlock->size != debugMargin)
9182
{
9183
uint32_t oldList = GetListIndex(prevBlock->size);
9184
prevBlock->size += misssingAlignment;
9185
// Check if new size crosses list bucket
9186
if (oldList != GetListIndex(prevBlock->size))
9187
{
9188
prevBlock->size -= misssingAlignment;
9189
RemoveFreeBlock(prevBlock);
9190
prevBlock->size += misssingAlignment;
9191
InsertFreeBlock(prevBlock);
9192
}
9193
else
9194
m_BlocksFreeSize += misssingAlignment;
9195
}
9196
else
9197
{
9198
Block* newBlock = m_BlockAllocator.Alloc();
9199
currentBlock->prevPhysical = newBlock;
9200
prevBlock->nextPhysical = newBlock;
9201
newBlock->prevPhysical = prevBlock;
9202
newBlock->nextPhysical = currentBlock;
9203
newBlock->size = misssingAlignment;
9204
newBlock->offset = currentBlock->offset;
9205
newBlock->MarkTaken();
9206
9207
InsertFreeBlock(newBlock);
9208
}
9209
9210
currentBlock->size -= misssingAlignment;
9211
currentBlock->offset += misssingAlignment;
9212
}
9213
9214
VkDeviceSize size = request.size + debugMargin;
9215
if (currentBlock->size == size)
9216
{
9217
if (currentBlock == m_NullBlock)
9218
{
9219
// Setup new null block
9220
m_NullBlock = m_BlockAllocator.Alloc();
9221
m_NullBlock->size = 0;
9222
m_NullBlock->offset = currentBlock->offset + size;
9223
m_NullBlock->prevPhysical = currentBlock;
9224
m_NullBlock->nextPhysical = VMA_NULL;
9225
m_NullBlock->MarkFree();
9226
m_NullBlock->PrevFree() = VMA_NULL;
9227
m_NullBlock->NextFree() = VMA_NULL;
9228
currentBlock->nextPhysical = m_NullBlock;
9229
currentBlock->MarkTaken();
9230
}
9231
}
9232
else
9233
{
9234
VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");
9235
9236
// Create new free block
9237
Block* newBlock = m_BlockAllocator.Alloc();
9238
newBlock->size = currentBlock->size - size;
9239
newBlock->offset = currentBlock->offset + size;
9240
newBlock->prevPhysical = currentBlock;
9241
newBlock->nextPhysical = currentBlock->nextPhysical;
9242
currentBlock->nextPhysical = newBlock;
9243
currentBlock->size = size;
9244
9245
if (currentBlock == m_NullBlock)
9246
{
9247
m_NullBlock = newBlock;
9248
m_NullBlock->MarkFree();
9249
m_NullBlock->NextFree() = VMA_NULL;
9250
m_NullBlock->PrevFree() = VMA_NULL;
9251
currentBlock->MarkTaken();
9252
}
9253
else
9254
{
9255
newBlock->nextPhysical->prevPhysical = newBlock;
9256
newBlock->MarkTaken();
9257
InsertFreeBlock(newBlock);
9258
}
9259
}
9260
currentBlock->UserData() = userData;
9261
9262
if (debugMargin > 0)
9263
{
9264
currentBlock->size -= debugMargin;
9265
Block* newBlock = m_BlockAllocator.Alloc();
9266
newBlock->size = debugMargin;
9267
newBlock->offset = currentBlock->offset + currentBlock->size;
9268
newBlock->prevPhysical = currentBlock;
9269
newBlock->nextPhysical = currentBlock->nextPhysical;
9270
newBlock->MarkTaken();
9271
currentBlock->nextPhysical->prevPhysical = newBlock;
9272
currentBlock->nextPhysical = newBlock;
9273
InsertFreeBlock(newBlock);
9274
}
9275
9276
if (!IsVirtual())
9277
m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,
9278
currentBlock->offset, currentBlock->size);
9279
++m_AllocCount;
9280
}
9281
9282
void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)
9283
{
9284
Block* block = (Block*)allocHandle;
9285
Block* next = block->nextPhysical;
9286
VMA_ASSERT(!block->IsFree() && "Block is already free!");
9287
9288
if (!IsVirtual())
9289
m_GranularityHandler.FreePages(block->offset, block->size);
9290
--m_AllocCount;
9291
9292
VkDeviceSize debugMargin = GetDebugMargin();
9293
if (debugMargin > 0)
9294
{
9295
RemoveFreeBlock(next);
9296
MergeBlock(next, block);
9297
block = next;
9298
next = next->nextPhysical;
9299
}
9300
9301
// Try merging
9302
Block* prev = block->prevPhysical;
9303
if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)
9304
{
9305
RemoveFreeBlock(prev);
9306
MergeBlock(block, prev);
9307
}
9308
9309
if (!next->IsFree())
9310
InsertFreeBlock(block);
9311
else if (next == m_NullBlock)
9312
MergeBlock(m_NullBlock, block);
9313
else
9314
{
9315
RemoveFreeBlock(next);
9316
MergeBlock(next, block);
9317
InsertFreeBlock(next);
9318
}
9319
}
9320
9321
void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
9322
{
9323
Block* block = (Block*)allocHandle;
9324
VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");
9325
outInfo.offset = block->offset;
9326
outInfo.size = block->size;
9327
outInfo.pUserData = block->UserData();
9328
}
9329
9330
void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const
9331
{
9332
Block* block = (Block*)allocHandle;
9333
VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");
9334
return block->UserData();
9335
}
9336
9337
VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const
9338
{
9339
if (m_AllocCount == 0)
9340
return VK_NULL_HANDLE;
9341
9342
for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)
9343
{
9344
if (!block->IsFree())
9345
return (VmaAllocHandle)block;
9346
}
9347
VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");
9348
return VK_NULL_HANDLE;
9349
}
9350
9351
VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const
9352
{
9353
Block* startBlock = (Block*)prevAlloc;
9354
VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");
9355
9356
for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)
9357
{
9358
if (!block->IsFree())
9359
return (VmaAllocHandle)block;
9360
}
9361
return VK_NULL_HANDLE;
9362
}
9363
9364
VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const
9365
{
9366
Block* block = (Block*)alloc;
9367
VMA_ASSERT(!block->IsFree() && "Incorrect block!");
9368
9369
if (block->prevPhysical)
9370
return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;
9371
return 0;
9372
}
9373
9374
void VmaBlockMetadata_TLSF::Clear()
9375
{
9376
m_AllocCount = 0;
9377
m_BlocksFreeCount = 0;
9378
m_BlocksFreeSize = 0;
9379
m_IsFreeBitmap = 0;
9380
m_NullBlock->offset = 0;
9381
m_NullBlock->size = GetSize();
9382
Block* block = m_NullBlock->prevPhysical;
9383
m_NullBlock->prevPhysical = VMA_NULL;
9384
while (block)
9385
{
9386
Block* prev = block->prevPhysical;
9387
m_BlockAllocator.Free(block);
9388
block = prev;
9389
}
9390
memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
9391
memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));
9392
m_GranularityHandler.Clear();
9393
}
9394
9395
void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
9396
{
9397
Block* block = (Block*)allocHandle;
9398
VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");
9399
block->UserData() = userData;
9400
}
9401
9402
void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const
9403
{
9404
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
9405
if (!block->IsFree())
9406
DebugLogAllocation(block->offset, block->size, block->UserData());
9407
}
9408
9409
uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const
9410
{
9411
if (size > SMALL_BUFFER_SIZE)
9412
return uint8_t(VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT);
9413
return 0;
9414
}
9415
9416
uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const
9417
{
9418
if (memoryClass == 0)
9419
{
9420
if (IsVirtual())
9421
return static_cast<uint16_t>((size - 1) / 8);
9422
else
9423
return static_cast<uint16_t>((size - 1) / 64);
9424
}
9425
return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));
9426
}
9427
9428
uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const
9429
{
9430
if (memoryClass == 0)
9431
return secondIndex;
9432
9433
const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;
9434
if (IsVirtual())
9435
return index + (1 << SECOND_LEVEL_INDEX);
9436
else
9437
return index + 4;
9438
}
9439
9440
uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const
9441
{
9442
uint8_t memoryClass = SizeToMemoryClass(size);
9443
return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));
9444
}
9445
9446
void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)
9447
{
9448
VMA_ASSERT(block != m_NullBlock);
9449
VMA_ASSERT(block->IsFree());
9450
9451
if (block->NextFree() != VMA_NULL)
9452
block->NextFree()->PrevFree() = block->PrevFree();
9453
if (block->PrevFree() != VMA_NULL)
9454
block->PrevFree()->NextFree() = block->NextFree();
9455
else
9456
{
9457
uint8_t memClass = SizeToMemoryClass(block->size);
9458
uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
9459
uint32_t index = GetListIndex(memClass, secondIndex);
9460
VMA_ASSERT(m_FreeList[index] == block);
9461
m_FreeList[index] = block->NextFree();
9462
if (block->NextFree() == VMA_NULL)
9463
{
9464
m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);
9465
if (m_InnerIsFreeBitmap[memClass] == 0)
9466
m_IsFreeBitmap &= ~(1UL << memClass);
9467
}
9468
}
9469
block->MarkTaken();
9470
block->UserData() = VMA_NULL;
9471
--m_BlocksFreeCount;
9472
m_BlocksFreeSize -= block->size;
9473
}
9474
9475
void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)
9476
{
9477
VMA_ASSERT(block != m_NullBlock);
9478
VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");
9479
9480
uint8_t memClass = SizeToMemoryClass(block->size);
9481
uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
9482
uint32_t index = GetListIndex(memClass, secondIndex);
9483
VMA_ASSERT(index < m_ListsCount);
9484
block->PrevFree() = VMA_NULL;
9485
block->NextFree() = m_FreeList[index];
9486
m_FreeList[index] = block;
9487
if (block->NextFree() != VMA_NULL)
9488
block->NextFree()->PrevFree() = block;
9489
else
9490
{
9491
m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;
9492
m_IsFreeBitmap |= 1UL << memClass;
9493
}
9494
++m_BlocksFreeCount;
9495
m_BlocksFreeSize += block->size;
9496
}
9497
9498
void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)
9499
{
9500
VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!");
9501
VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");
9502
9503
block->offset = prev->offset;
9504
block->size += prev->size;
9505
block->prevPhysical = prev->prevPhysical;
9506
if (block->prevPhysical)
9507
block->prevPhysical->nextPhysical = block;
9508
m_BlockAllocator.Free(prev);
9509
}
9510
9511
VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const
9512
{
9513
uint8_t memoryClass = SizeToMemoryClass(size);
9514
uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));
9515
if (!innerFreeMap)
9516
{
9517
// Check higher levels for available blocks
9518
uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));
9519
if (!freeMap)
9520
return VMA_NULL; // No more memory available
9521
9522
// Find lowest free region
9523
memoryClass = VMA_BITSCAN_LSB(freeMap);
9524
innerFreeMap = m_InnerIsFreeBitmap[memoryClass];
9525
VMA_ASSERT(innerFreeMap != 0);
9526
}
9527
// Find lowest free subregion
9528
listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));
9529
VMA_ASSERT(m_FreeList[listIndex]);
9530
return m_FreeList[listIndex];
9531
}
9532
9533
bool VmaBlockMetadata_TLSF::CheckBlock(
9534
Block& block,
9535
uint32_t listIndex,
9536
VkDeviceSize allocSize,
9537
VkDeviceSize allocAlignment,
9538
VmaSuballocationType allocType,
9539
VmaAllocationRequest* pAllocationRequest)
9540
{
9541
VMA_ASSERT(block.IsFree() && "Block is already taken!");
9542
9543
VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);
9544
if (block.size < allocSize + alignedOffset - block.offset)
9545
return false;
9546
9547
// Check for granularity conflicts
9548
if (!IsVirtual() &&
9549
m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))
9550
return false;
9551
9552
// Alloc successful
9553
pAllocationRequest->type = VmaAllocationRequestType::TLSF;
9554
pAllocationRequest->allocHandle = (VmaAllocHandle)&block;
9555
pAllocationRequest->size = allocSize - GetDebugMargin();
9556
pAllocationRequest->customData = (void*)allocType;
9557
pAllocationRequest->algorithmData = alignedOffset;
9558
9559
// Place block at the start of list if it's normal block
9560
if (listIndex != m_ListsCount && block.PrevFree())
9561
{
9562
block.PrevFree()->NextFree() = block.NextFree();
9563
if (block.NextFree())
9564
block.NextFree()->PrevFree() = block.PrevFree();
9565
block.PrevFree() = VMA_NULL;
9566
block.NextFree() = m_FreeList[listIndex];
9567
m_FreeList[listIndex] = &block;
9568
if (block.NextFree())
9569
block.NextFree()->PrevFree() = &block;
9570
}
9571
9572
return true;
9573
}
9574
#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
9575
#endif // _VMA_BLOCK_METADATA_TLSF
9576
9577
#ifndef _VMA_BLOCK_VECTOR
9578
/*
9579
Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
9580
Vulkan memory type.
9581
9582
Synchronized internally with a mutex.
9583
*/
9584
class VmaBlockVector
9585
{
9586
friend struct VmaDefragmentationContext_T;
9587
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockVector)
9588
public:
9589
VmaBlockVector(
9590
VmaAllocator hAllocator,
9591
VmaPool hParentPool,
9592
uint32_t memoryTypeIndex,
9593
VkDeviceSize preferredBlockSize,
9594
size_t minBlockCount,
9595
size_t maxBlockCount,
9596
VkDeviceSize bufferImageGranularity,
9597
bool explicitBlockSize,
9598
uint32_t algorithm,
9599
float priority,
9600
VkDeviceSize minAllocationAlignment,
9601
void* pMemoryAllocateNext);
9602
~VmaBlockVector();
9603
9604
VmaAllocator GetAllocator() const { return m_hAllocator; }
9605
VmaPool GetParentPool() const { return m_hParentPool; }
9606
bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
9607
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
9608
VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
9609
VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
9610
uint32_t GetAlgorithm() const { return m_Algorithm; }
9611
bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }
9612
float GetPriority() const { return m_Priority; }
9613
const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }
9614
// To be used only while the m_Mutex is locked. Used during defragmentation.
9615
size_t GetBlockCount() const { return m_Blocks.size(); }
9616
// To be used only while the m_Mutex is locked. Used during defragmentation.
9617
VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
9618
VMA_RW_MUTEX &GetMutex() { return m_Mutex; }
9619
9620
VkResult CreateMinBlocks();
9621
void AddStatistics(VmaStatistics& inoutStats);
9622
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
9623
bool IsEmpty();
9624
bool IsCorruptionDetectionEnabled() const;
9625
9626
VkResult Allocate(
9627
VkDeviceSize size,
9628
VkDeviceSize alignment,
9629
const VmaAllocationCreateInfo& createInfo,
9630
VmaSuballocationType suballocType,
9631
size_t allocationCount,
9632
VmaAllocation* pAllocations);
9633
9634
void Free(const VmaAllocation hAllocation);
9635
9636
#if VMA_STATS_STRING_ENABLED
9637
void PrintDetailedMap(class VmaJsonWriter& json);
9638
#endif
9639
9640
VkResult CheckCorruption();
9641
9642
private:
9643
const VmaAllocator m_hAllocator;
9644
const VmaPool m_hParentPool;
9645
const uint32_t m_MemoryTypeIndex;
9646
const VkDeviceSize m_PreferredBlockSize;
9647
const size_t m_MinBlockCount;
9648
const size_t m_MaxBlockCount;
9649
const VkDeviceSize m_BufferImageGranularity;
9650
const bool m_ExplicitBlockSize;
9651
const uint32_t m_Algorithm;
9652
const float m_Priority;
9653
const VkDeviceSize m_MinAllocationAlignment;
9654
9655
void* const m_pMemoryAllocateNext;
9656
VMA_RW_MUTEX m_Mutex;
9657
// Incrementally sorted by sumFreeSize, ascending.
9658
VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;
9659
uint32_t m_NextBlockId;
9660
bool m_IncrementalSort = true;
9661
9662
void SetIncrementalSort(bool val) { m_IncrementalSort = val; }
9663
9664
VkDeviceSize CalcMaxBlockSize() const;
9665
// Finds and removes given block from vector.
9666
void Remove(VmaDeviceMemoryBlock* pBlock);
9667
// Performs single step in sorting m_Blocks. They may not be fully sorted
9668
// after this call.
9669
void IncrementallySortBlocks();
9670
void SortByFreeSize();
9671
9672
VkResult AllocatePage(
9673
VkDeviceSize size,
9674
VkDeviceSize alignment,
9675
const VmaAllocationCreateInfo& createInfo,
9676
VmaSuballocationType suballocType,
9677
VmaAllocation* pAllocation);
9678
9679
VkResult AllocateFromBlock(
9680
VmaDeviceMemoryBlock* pBlock,
9681
VkDeviceSize size,
9682
VkDeviceSize alignment,
9683
VmaAllocationCreateFlags allocFlags,
9684
void* pUserData,
9685
VmaSuballocationType suballocType,
9686
uint32_t strategy,
9687
VmaAllocation* pAllocation);
9688
9689
VkResult CommitAllocationRequest(
9690
VmaAllocationRequest& allocRequest,
9691
VmaDeviceMemoryBlock* pBlock,
9692
VkDeviceSize alignment,
9693
VmaAllocationCreateFlags allocFlags,
9694
void* pUserData,
9695
VmaSuballocationType suballocType,
9696
VmaAllocation* pAllocation);
9697
9698
VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
9699
bool HasEmptyBlock();
9700
};
9701
#endif // _VMA_BLOCK_VECTOR
9702
9703
#ifndef _VMA_DEFRAGMENTATION_CONTEXT
9704
struct VmaDefragmentationContext_T
9705
{
9706
VMA_CLASS_NO_COPY_NO_MOVE(VmaDefragmentationContext_T)
9707
public:
9708
VmaDefragmentationContext_T(
9709
VmaAllocator hAllocator,
9710
const VmaDefragmentationInfo& info);
9711
~VmaDefragmentationContext_T();
9712
9713
void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }
9714
9715
VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);
9716
VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);
9717
9718
private:
9719
// Max number of allocations to ignore due to size constraints before ending single pass
9720
static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;
9721
enum class CounterStatus { Pass, Ignore, End };
9722
9723
struct FragmentedBlock
9724
{
9725
uint32_t data;
9726
VmaDeviceMemoryBlock* block;
9727
};
9728
struct StateBalanced
9729
{
9730
VkDeviceSize avgFreeSize = 0;
9731
VkDeviceSize avgAllocSize = UINT64_MAX;
9732
};
9733
struct StateExtensive
9734
{
9735
enum class Operation : uint8_t
9736
{
9737
FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,
9738
MoveBuffers, MoveTextures, MoveAll,
9739
Cleanup, Done
9740
};
9741
9742
Operation operation = Operation::FindFreeBlockTexture;
9743
size_t firstFreeBlock = SIZE_MAX;
9744
};
9745
struct MoveAllocationData
9746
{
9747
VkDeviceSize size;
9748
VkDeviceSize alignment;
9749
VmaSuballocationType type;
9750
VmaAllocationCreateFlags flags;
9751
VmaDefragmentationMove move = {};
9752
};
9753
9754
const VkDeviceSize m_MaxPassBytes;
9755
const uint32_t m_MaxPassAllocations;
9756
const PFN_vmaCheckDefragmentationBreakFunction m_BreakCallback;
9757
void* m_BreakCallbackUserData;
9758
9759
VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;
9760
VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;
9761
9762
uint8_t m_IgnoredAllocs = 0;
9763
uint32_t m_Algorithm;
9764
uint32_t m_BlockVectorCount;
9765
VmaBlockVector* m_PoolBlockVector;
9766
VmaBlockVector** m_pBlockVectors;
9767
size_t m_ImmovableBlockCount = 0;
9768
VmaDefragmentationStats m_GlobalStats = { 0 };
9769
VmaDefragmentationStats m_PassStats = { 0 };
9770
void* m_AlgorithmState = VMA_NULL;
9771
9772
static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);
9773
CounterStatus CheckCounters(VkDeviceSize bytes);
9774
bool IncrementCounters(VkDeviceSize bytes);
9775
bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);
9776
bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);
9777
9778
bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);
9779
bool ComputeDefragmentation_Fast(VmaBlockVector& vector);
9780
bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);
9781
bool ComputeDefragmentation_Full(VmaBlockVector& vector);
9782
bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);
9783
9784
void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);
9785
bool MoveDataToFreeBlocks(VmaSuballocationType currentType,
9786
VmaBlockVector& vector, size_t firstFreeBlock,
9787
bool& texturePresent, bool& bufferPresent, bool& otherPresent);
9788
};
9789
#endif // _VMA_DEFRAGMENTATION_CONTEXT
9790
9791
#ifndef _VMA_POOL_T
9792
struct VmaPool_T
9793
{
9794
friend struct VmaPoolListItemTraits;
9795
VMA_CLASS_NO_COPY_NO_MOVE(VmaPool_T)
9796
public:
9797
VmaBlockVector m_BlockVector;
9798
VmaDedicatedAllocationList m_DedicatedAllocations;
9799
9800
VmaPool_T(
9801
VmaAllocator hAllocator,
9802
const VmaPoolCreateInfo& createInfo,
9803
VkDeviceSize preferredBlockSize);
9804
~VmaPool_T();
9805
9806
uint32_t GetId() const { return m_Id; }
9807
void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
9808
9809
const char* GetName() const { return m_Name; }
9810
void SetName(const char* pName);
9811
9812
#if VMA_STATS_STRING_ENABLED
9813
//void PrintDetailedMap(class VmaStringBuilder& sb);
9814
#endif
9815
9816
private:
9817
uint32_t m_Id;
9818
char* m_Name;
9819
VmaPool_T* m_PrevPool = VMA_NULL;
9820
VmaPool_T* m_NextPool = VMA_NULL;
9821
};
9822
9823
struct VmaPoolListItemTraits
9824
{
9825
typedef VmaPool_T ItemType;
9826
9827
static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
9828
static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
9829
static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
9830
static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
9831
};
9832
#endif // _VMA_POOL_T
9833
9834
#ifndef _VMA_CURRENT_BUDGET_DATA
9835
struct VmaCurrentBudgetData
9836
{
9837
VMA_CLASS_NO_COPY_NO_MOVE(VmaCurrentBudgetData)
9838
public:
9839
9840
VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];
9841
VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];
9842
VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
9843
VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
9844
9845
#if VMA_MEMORY_BUDGET
9846
VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
9847
VMA_RW_MUTEX m_BudgetMutex;
9848
uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
9849
uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
9850
uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
9851
#endif // VMA_MEMORY_BUDGET
9852
9853
VmaCurrentBudgetData();
9854
9855
void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
9856
void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
9857
};
9858
9859
#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
9860
VmaCurrentBudgetData::VmaCurrentBudgetData()
9861
{
9862
for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
9863
{
9864
m_BlockCount[heapIndex] = 0;
9865
m_AllocationCount[heapIndex] = 0;
9866
m_BlockBytes[heapIndex] = 0;
9867
m_AllocationBytes[heapIndex] = 0;
9868
#if VMA_MEMORY_BUDGET
9869
m_VulkanUsage[heapIndex] = 0;
9870
m_VulkanBudget[heapIndex] = 0;
9871
m_BlockBytesAtBudgetFetch[heapIndex] = 0;
9872
#endif
9873
}
9874
9875
#if VMA_MEMORY_BUDGET
9876
m_OperationsSinceBudgetFetch = 0;
9877
#endif
9878
}
9879
9880
void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
9881
{
9882
m_AllocationBytes[heapIndex] += allocationSize;
9883
++m_AllocationCount[heapIndex];
9884
#if VMA_MEMORY_BUDGET
9885
++m_OperationsSinceBudgetFetch;
9886
#endif
9887
}
9888
9889
void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
9890
{
9891
VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
9892
m_AllocationBytes[heapIndex] -= allocationSize;
9893
VMA_ASSERT(m_AllocationCount[heapIndex] > 0);
9894
--m_AllocationCount[heapIndex];
9895
#if VMA_MEMORY_BUDGET
9896
++m_OperationsSinceBudgetFetch;
9897
#endif
9898
}
9899
#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
9900
#endif // _VMA_CURRENT_BUDGET_DATA
9901
9902
#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR
9903
/*
9904
Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
9905
*/
9906
class VmaAllocationObjectAllocator
9907
{
9908
VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocationObjectAllocator)
9909
public:
9910
VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)
9911
: m_Allocator(pAllocationCallbacks, 1024) {}
9912
9913
template<typename... Types> VmaAllocation Allocate(Types&&... args);
9914
void Free(VmaAllocation hAlloc);
9915
9916
private:
9917
VMA_MUTEX m_Mutex;
9918
VmaPoolAllocator<VmaAllocation_T> m_Allocator;
9919
};
9920
9921
template<typename... Types>
9922
VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
9923
{
9924
VmaMutexLock mutexLock(m_Mutex);
9925
return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
9926
}
9927
9928
void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
9929
{
9930
VmaMutexLock mutexLock(m_Mutex);
9931
m_Allocator.Free(hAlloc);
9932
}
9933
#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR
9934
9935
#ifndef _VMA_VIRTUAL_BLOCK_T
9936
struct VmaVirtualBlock_T
9937
{
9938
VMA_CLASS_NO_COPY_NO_MOVE(VmaVirtualBlock_T)
9939
public:
9940
const bool m_AllocationCallbacksSpecified;
9941
const VkAllocationCallbacks m_AllocationCallbacks;
9942
9943
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
9944
~VmaVirtualBlock_T();
9945
9946
VkResult Init() { return VK_SUCCESS; }
9947
bool IsEmpty() const { return m_Metadata->IsEmpty(); }
9948
void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }
9949
void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }
9950
void Clear() { m_Metadata->Clear(); }
9951
9952
const VkAllocationCallbacks* GetAllocationCallbacks() const;
9953
void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);
9954
VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
9955
VkDeviceSize* outOffset);
9956
void GetStatistics(VmaStatistics& outStats) const;
9957
void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;
9958
#if VMA_STATS_STRING_ENABLED
9959
void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
9960
#endif
9961
9962
private:
9963
VmaBlockMetadata* m_Metadata;
9964
};
9965
9966
#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
9967
VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)
9968
: m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
9969
m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
9970
{
9971
const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
9972
switch (algorithm)
9973
{
9974
case 0:
9975
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
9976
break;
9977
case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
9978
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);
9979
break;
9980
default:
9981
VMA_ASSERT(0);
9982
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
9983
}
9984
9985
m_Metadata->Init(createInfo.size);
9986
}
9987
9988
VmaVirtualBlock_T::~VmaVirtualBlock_T()
9989
{
9990
// Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT
9991
// to receive the list of the unfreed allocations.
9992
if (!m_Metadata->IsEmpty())
9993
m_Metadata->DebugLogAllAllocations();
9994
// This is the most important assert in the entire library.
9995
// Hitting it means you have some memory leak - unreleased virtual allocations.
9996
VMA_ASSERT_LEAK(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
9997
9998
vma_delete(GetAllocationCallbacks(), m_Metadata);
9999
}
10000
10001
const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const
10002
{
10003
return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
10004
}
10005
10006
void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)
10007
{
10008
m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);
10009
}
10010
10011
VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
10012
VkDeviceSize* outOffset)
10013
{
10014
VmaAllocationRequest request = {};
10015
if (m_Metadata->CreateAllocationRequest(
10016
createInfo.size, // allocSize
10017
VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment
10018
(createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
10019
VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
10020
createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
10021
&request))
10022
{
10023
m_Metadata->Alloc(request,
10024
VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
10025
createInfo.pUserData);
10026
outAllocation = (VmaVirtualAllocation)request.allocHandle;
10027
if(outOffset)
10028
*outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);
10029
return VK_SUCCESS;
10030
}
10031
outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;
10032
if (outOffset)
10033
*outOffset = UINT64_MAX;
10034
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
10035
}
10036
10037
void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const
10038
{
10039
VmaClearStatistics(outStats);
10040
m_Metadata->AddStatistics(outStats);
10041
}
10042
10043
void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const
10044
{
10045
VmaClearDetailedStatistics(outStats);
10046
m_Metadata->AddDetailedStatistics(outStats);
10047
}
10048
10049
#if VMA_STATS_STRING_ENABLED
10050
void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
10051
{
10052
VmaJsonWriter json(GetAllocationCallbacks(), sb);
10053
json.BeginObject();
10054
10055
VmaDetailedStatistics stats;
10056
CalculateDetailedStatistics(stats);
10057
10058
json.WriteString("Stats");
10059
VmaPrintDetailedStatistics(json, stats);
10060
10061
if (detailedMap)
10062
{
10063
json.WriteString("Details");
10064
json.BeginObject();
10065
m_Metadata->PrintDetailedMap(json);
10066
json.EndObject();
10067
}
10068
10069
json.EndObject();
10070
}
10071
#endif // VMA_STATS_STRING_ENABLED
10072
#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
10073
#endif // _VMA_VIRTUAL_BLOCK_T
10074
10075
10076
// Main allocator object.
10077
struct VmaAllocator_T
10078
{
10079
VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocator_T)
10080
public:
10081
const bool m_UseMutex;
10082
const uint32_t m_VulkanApiVersion;
10083
bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
10084
bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
10085
bool m_UseExtMemoryBudget;
10086
bool m_UseAmdDeviceCoherentMemory;
10087
bool m_UseKhrBufferDeviceAddress;
10088
bool m_UseExtMemoryPriority;
10089
bool m_UseKhrMaintenance4;
10090
bool m_UseKhrMaintenance5;
10091
const VkDevice m_hDevice;
10092
const VkInstance m_hInstance;
10093
const bool m_AllocationCallbacksSpecified;
10094
const VkAllocationCallbacks m_AllocationCallbacks;
10095
VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
10096
VmaAllocationObjectAllocator m_AllocationObjectAllocator;
10097
10098
// Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
10099
uint32_t m_HeapSizeLimitMask;
10100
10101
VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
10102
VkPhysicalDeviceMemoryProperties m_MemProps;
10103
10104
// Default pools.
10105
VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
10106
VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
10107
10108
VmaCurrentBudgetData m_Budget;
10109
VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
10110
10111
VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
10112
VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
10113
~VmaAllocator_T();
10114
10115
const VkAllocationCallbacks* GetAllocationCallbacks() const
10116
{
10117
return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
10118
}
10119
const VmaVulkanFunctions& GetVulkanFunctions() const
10120
{
10121
return m_VulkanFunctions;
10122
}
10123
10124
VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
10125
10126
VkDeviceSize GetBufferImageGranularity() const
10127
{
10128
return VMA_MAX(
10129
static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
10130
m_PhysicalDeviceProperties.limits.bufferImageGranularity);
10131
}
10132
10133
uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
10134
uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
10135
10136
uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
10137
{
10138
VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
10139
return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
10140
}
10141
// True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
10142
bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
10143
{
10144
return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
10145
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
10146
}
10147
// Minimum alignment for all allocations in specific memory type.
10148
VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
10149
{
10150
return IsMemoryTypeNonCoherent(memTypeIndex) ?
10151
VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
10152
(VkDeviceSize)VMA_MIN_ALIGNMENT;
10153
}
10154
10155
bool IsIntegratedGpu() const
10156
{
10157
return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
10158
}
10159
10160
uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
10161
10162
void GetBufferMemoryRequirements(
10163
VkBuffer hBuffer,
10164
VkMemoryRequirements& memReq,
10165
bool& requiresDedicatedAllocation,
10166
bool& prefersDedicatedAllocation) const;
10167
void GetImageMemoryRequirements(
10168
VkImage hImage,
10169
VkMemoryRequirements& memReq,
10170
bool& requiresDedicatedAllocation,
10171
bool& prefersDedicatedAllocation) const;
10172
VkResult FindMemoryTypeIndex(
10173
uint32_t memoryTypeBits,
10174
const VmaAllocationCreateInfo* pAllocationCreateInfo,
10175
VmaBufferImageUsage bufImgUsage,
10176
uint32_t* pMemoryTypeIndex) const;
10177
10178
// Main allocation function.
10179
VkResult AllocateMemory(
10180
const VkMemoryRequirements& vkMemReq,
10181
bool requiresDedicatedAllocation,
10182
bool prefersDedicatedAllocation,
10183
VkBuffer dedicatedBuffer,
10184
VkImage dedicatedImage,
10185
VmaBufferImageUsage dedicatedBufferImageUsage,
10186
const VmaAllocationCreateInfo& createInfo,
10187
VmaSuballocationType suballocType,
10188
size_t allocationCount,
10189
VmaAllocation* pAllocations);
10190
10191
// Main deallocation function.
10192
void FreeMemory(
10193
size_t allocationCount,
10194
const VmaAllocation* pAllocations);
10195
10196
void CalculateStatistics(VmaTotalStatistics* pStats);
10197
10198
void GetHeapBudgets(
10199
VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);
10200
10201
#if VMA_STATS_STRING_ENABLED
10202
void PrintDetailedMap(class VmaJsonWriter& json);
10203
#endif
10204
10205
void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
10206
void GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo);
10207
10208
VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
10209
void DestroyPool(VmaPool pool);
10210
void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);
10211
void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);
10212
10213
void SetCurrentFrameIndex(uint32_t frameIndex);
10214
uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
10215
10216
VkResult CheckPoolCorruption(VmaPool hPool);
10217
VkResult CheckCorruption(uint32_t memoryTypeBits);
10218
10219
// Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
10220
VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
10221
// Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
10222
void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
10223
// Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
10224
VkResult BindVulkanBuffer(
10225
VkDeviceMemory memory,
10226
VkDeviceSize memoryOffset,
10227
VkBuffer buffer,
10228
const void* pNext);
10229
// Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
10230
VkResult BindVulkanImage(
10231
VkDeviceMemory memory,
10232
VkDeviceSize memoryOffset,
10233
VkImage image,
10234
const void* pNext);
10235
10236
VkResult Map(VmaAllocation hAllocation, void** ppData);
10237
void Unmap(VmaAllocation hAllocation);
10238
10239
VkResult BindBufferMemory(
10240
VmaAllocation hAllocation,
10241
VkDeviceSize allocationLocalOffset,
10242
VkBuffer hBuffer,
10243
const void* pNext);
10244
VkResult BindImageMemory(
10245
VmaAllocation hAllocation,
10246
VkDeviceSize allocationLocalOffset,
10247
VkImage hImage,
10248
const void* pNext);
10249
10250
VkResult FlushOrInvalidateAllocation(
10251
VmaAllocation hAllocation,
10252
VkDeviceSize offset, VkDeviceSize size,
10253
VMA_CACHE_OPERATION op);
10254
VkResult FlushOrInvalidateAllocations(
10255
uint32_t allocationCount,
10256
const VmaAllocation* allocations,
10257
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
10258
VMA_CACHE_OPERATION op);
10259
10260
VkResult CopyMemoryToAllocation(
10261
const void* pSrcHostPointer,
10262
VmaAllocation dstAllocation,
10263
VkDeviceSize dstAllocationLocalOffset,
10264
VkDeviceSize size);
10265
VkResult CopyAllocationToMemory(
10266
VmaAllocation srcAllocation,
10267
VkDeviceSize srcAllocationLocalOffset,
10268
void* pDstHostPointer,
10269
VkDeviceSize size);
10270
10271
void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
10272
10273
/*
10274
Returns bit mask of memory types that can support defragmentation on GPU as
10275
they support creation of required buffer for copy operations.
10276
*/
10277
uint32_t GetGpuDefragmentationMemoryTypeBits();
10278
10279
#if VMA_EXTERNAL_MEMORY
10280
VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
10281
{
10282
return m_TypeExternalMemoryHandleTypes[memTypeIndex];
10283
}
10284
#endif // #if VMA_EXTERNAL_MEMORY
10285
10286
private:
10287
VkDeviceSize m_PreferredLargeHeapBlockSize;
10288
10289
VkPhysicalDevice m_PhysicalDevice;
10290
VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
10291
VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
10292
#if VMA_EXTERNAL_MEMORY
10293
VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
10294
#endif // #if VMA_EXTERNAL_MEMORY
10295
10296
VMA_RW_MUTEX m_PoolsMutex;
10297
typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
10298
// Protected by m_PoolsMutex.
10299
PoolList m_Pools;
10300
uint32_t m_NextPoolId;
10301
10302
VmaVulkanFunctions m_VulkanFunctions;
10303
10304
// Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
10305
uint32_t m_GlobalMemoryTypeBits;
10306
10307
void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
10308
10309
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
10310
void ImportVulkanFunctions_Static();
10311
#endif
10312
10313
void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
10314
10315
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
10316
void ImportVulkanFunctions_Dynamic();
10317
#endif
10318
10319
void ValidateVulkanFunctions();
10320
10321
VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
10322
10323
VkResult AllocateMemoryOfType(
10324
VmaPool pool,
10325
VkDeviceSize size,
10326
VkDeviceSize alignment,
10327
bool dedicatedPreferred,
10328
VkBuffer dedicatedBuffer,
10329
VkImage dedicatedImage,
10330
VmaBufferImageUsage dedicatedBufferImageUsage,
10331
const VmaAllocationCreateInfo& createInfo,
10332
uint32_t memTypeIndex,
10333
VmaSuballocationType suballocType,
10334
VmaDedicatedAllocationList& dedicatedAllocations,
10335
VmaBlockVector& blockVector,
10336
size_t allocationCount,
10337
VmaAllocation* pAllocations);
10338
10339
// Helper function only to be used inside AllocateDedicatedMemory.
10340
VkResult AllocateDedicatedMemoryPage(
10341
VmaPool pool,
10342
VkDeviceSize size,
10343
VmaSuballocationType suballocType,
10344
uint32_t memTypeIndex,
10345
const VkMemoryAllocateInfo& allocInfo,
10346
bool map,
10347
bool isUserDataString,
10348
bool isMappingAllowed,
10349
void* pUserData,
10350
VmaAllocation* pAllocation);
10351
10352
// Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
10353
VkResult AllocateDedicatedMemory(
10354
VmaPool pool,
10355
VkDeviceSize size,
10356
VmaSuballocationType suballocType,
10357
VmaDedicatedAllocationList& dedicatedAllocations,
10358
uint32_t memTypeIndex,
10359
bool map,
10360
bool isUserDataString,
10361
bool isMappingAllowed,
10362
bool canAliasMemory,
10363
void* pUserData,
10364
float priority,
10365
VkBuffer dedicatedBuffer,
10366
VkImage dedicatedImage,
10367
VmaBufferImageUsage dedicatedBufferImageUsage,
10368
size_t allocationCount,
10369
VmaAllocation* pAllocations,
10370
const void* pNextChain = VMA_NULL);
10371
10372
void FreeDedicatedMemory(const VmaAllocation allocation);
10373
10374
VkResult CalcMemTypeParams(
10375
VmaAllocationCreateInfo& outCreateInfo,
10376
uint32_t memTypeIndex,
10377
VkDeviceSize size,
10378
size_t allocationCount);
10379
VkResult CalcAllocationParams(
10380
VmaAllocationCreateInfo& outCreateInfo,
10381
bool dedicatedRequired,
10382
bool dedicatedPreferred);
10383
10384
/*
10385
Calculates and returns bit mask of memory types that can support defragmentation
10386
on GPU as they support creation of required buffer for copy operations.
10387
*/
10388
uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
10389
uint32_t CalculateGlobalMemoryTypeBits() const;
10390
10391
bool GetFlushOrInvalidateRange(
10392
VmaAllocation allocation,
10393
VkDeviceSize offset, VkDeviceSize size,
10394
VkMappedMemoryRange& outRange) const;
10395
10396
#if VMA_MEMORY_BUDGET
10397
void UpdateVulkanBudget();
10398
#endif // #if VMA_MEMORY_BUDGET
10399
};
10400
10401
10402
#ifndef _VMA_MEMORY_FUNCTIONS
10403
static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
10404
{
10405
return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
10406
}
10407
10408
static void VmaFree(VmaAllocator hAllocator, void* ptr)
10409
{
10410
VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
10411
}
10412
10413
template<typename T>
10414
static T* VmaAllocate(VmaAllocator hAllocator)
10415
{
10416
return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
10417
}
10418
10419
template<typename T>
10420
static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
10421
{
10422
return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
10423
}
10424
10425
template<typename T>
10426
static void vma_delete(VmaAllocator hAllocator, T* ptr)
10427
{
10428
if(ptr != VMA_NULL)
10429
{
10430
ptr->~T();
10431
VmaFree(hAllocator, ptr);
10432
}
10433
}
10434
10435
template<typename T>
10436
static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
10437
{
10438
if(ptr != VMA_NULL)
10439
{
10440
for(size_t i = count; i--; )
10441
ptr[i].~T();
10442
VmaFree(hAllocator, ptr);
10443
}
10444
}
10445
#endif // _VMA_MEMORY_FUNCTIONS
10446
10447
#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
10448
VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
10449
: m_pMetadata(VMA_NULL),
10450
m_MemoryTypeIndex(UINT32_MAX),
10451
m_Id(0),
10452
m_hMemory(VK_NULL_HANDLE),
10453
m_MapCount(0),
10454
m_pMappedData(VMA_NULL) {}
10455
10456
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
10457
{
10458
VMA_ASSERT_LEAK(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
10459
VMA_ASSERT_LEAK(m_hMemory == VK_NULL_HANDLE);
10460
}
10461
10462
void VmaDeviceMemoryBlock::Init(
10463
VmaAllocator hAllocator,
10464
VmaPool hParentPool,
10465
uint32_t newMemoryTypeIndex,
10466
VkDeviceMemory newMemory,
10467
VkDeviceSize newSize,
10468
uint32_t id,
10469
uint32_t algorithm,
10470
VkDeviceSize bufferImageGranularity)
10471
{
10472
VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10473
10474
m_hParentPool = hParentPool;
10475
m_MemoryTypeIndex = newMemoryTypeIndex;
10476
m_Id = id;
10477
m_hMemory = newMemory;
10478
10479
switch (algorithm)
10480
{
10481
case 0:
10482
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
10483
bufferImageGranularity, false); // isVirtual
10484
break;
10485
case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
10486
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
10487
bufferImageGranularity, false); // isVirtual
10488
break;
10489
default:
10490
VMA_ASSERT(0);
10491
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
10492
bufferImageGranularity, false); // isVirtual
10493
}
10494
m_pMetadata->Init(newSize);
10495
}
10496
10497
void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
10498
{
10499
// Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT
10500
// to receive the list of the unfreed allocations.
10501
if (!m_pMetadata->IsEmpty())
10502
m_pMetadata->DebugLogAllAllocations();
10503
// This is the most important assert in the entire library.
10504
// Hitting it means you have some memory leak - unreleased VmaAllocation objects.
10505
VMA_ASSERT_LEAK(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
10506
10507
VMA_ASSERT_LEAK(m_hMemory != VK_NULL_HANDLE);
10508
allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
10509
m_hMemory = VK_NULL_HANDLE;
10510
10511
vma_delete(allocator, m_pMetadata);
10512
m_pMetadata = VMA_NULL;
10513
}
10514
10515
void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator)
10516
{
10517
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10518
m_MappingHysteresis.PostAlloc();
10519
}
10520
10521
void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)
10522
{
10523
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10524
if(m_MappingHysteresis.PostFree())
10525
{
10526
VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);
10527
if (m_MapCount == 0)
10528
{
10529
m_pMappedData = VMA_NULL;
10530
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
10531
}
10532
}
10533
}
10534
10535
bool VmaDeviceMemoryBlock::Validate() const
10536
{
10537
VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
10538
(m_pMetadata->GetSize() != 0));
10539
10540
return m_pMetadata->Validate();
10541
}
10542
10543
VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
10544
{
10545
void* pData = VMA_NULL;
10546
VkResult res = Map(hAllocator, 1, &pData);
10547
if (res != VK_SUCCESS)
10548
{
10549
return res;
10550
}
10551
10552
res = m_pMetadata->CheckCorruption(pData);
10553
10554
Unmap(hAllocator, 1);
10555
10556
return res;
10557
}
10558
10559
VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
10560
{
10561
if (count == 0)
10562
{
10563
return VK_SUCCESS;
10564
}
10565
10566
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10567
const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
10568
if (oldTotalMapCount != 0)
10569
{
10570
VMA_ASSERT(m_pMappedData != VMA_NULL);
10571
m_MappingHysteresis.PostMap();
10572
m_MapCount += count;
10573
if (ppData != VMA_NULL)
10574
{
10575
*ppData = m_pMappedData;
10576
}
10577
return VK_SUCCESS;
10578
}
10579
else
10580
{
10581
VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
10582
hAllocator->m_hDevice,
10583
m_hMemory,
10584
0, // offset
10585
VK_WHOLE_SIZE,
10586
0, // flags
10587
&m_pMappedData);
10588
if (result == VK_SUCCESS)
10589
{
10590
VMA_ASSERT(m_pMappedData != VMA_NULL);
10591
m_MappingHysteresis.PostMap();
10592
m_MapCount = count;
10593
if (ppData != VMA_NULL)
10594
{
10595
*ppData = m_pMappedData;
10596
}
10597
}
10598
return result;
10599
}
10600
}
10601
10602
void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
10603
{
10604
if (count == 0)
10605
{
10606
return;
10607
}
10608
10609
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10610
if (m_MapCount >= count)
10611
{
10612
m_MapCount -= count;
10613
const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
10614
if (totalMapCount == 0)
10615
{
10616
m_pMappedData = VMA_NULL;
10617
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
10618
}
10619
m_MappingHysteresis.PostUnmap();
10620
}
10621
else
10622
{
10623
VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
10624
}
10625
}
10626
10627
VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
10628
{
10629
VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
10630
10631
void* pData;
10632
VkResult res = Map(hAllocator, 1, &pData);
10633
if (res != VK_SUCCESS)
10634
{
10635
return res;
10636
}
10637
10638
VmaWriteMagicValue(pData, allocOffset + allocSize);
10639
10640
Unmap(hAllocator, 1);
10641
return VK_SUCCESS;
10642
}
10643
10644
VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
10645
{
10646
VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
10647
10648
void* pData;
10649
VkResult res = Map(hAllocator, 1, &pData);
10650
if (res != VK_SUCCESS)
10651
{
10652
return res;
10653
}
10654
10655
if (!VmaValidateMagicValue(pData, allocOffset + allocSize))
10656
{
10657
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
10658
}
10659
10660
Unmap(hAllocator, 1);
10661
return VK_SUCCESS;
10662
}
10663
10664
VkResult VmaDeviceMemoryBlock::BindBufferMemory(
10665
const VmaAllocator hAllocator,
10666
const VmaAllocation hAllocation,
10667
VkDeviceSize allocationLocalOffset,
10668
VkBuffer hBuffer,
10669
const void* pNext)
10670
{
10671
VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
10672
hAllocation->GetBlock() == this);
10673
VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
10674
"Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
10675
const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
10676
// This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
10677
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10678
return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
10679
}
10680
10681
VkResult VmaDeviceMemoryBlock::BindImageMemory(
10682
const VmaAllocator hAllocator,
10683
const VmaAllocation hAllocation,
10684
VkDeviceSize allocationLocalOffset,
10685
VkImage hImage,
10686
const void* pNext)
10687
{
10688
VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
10689
hAllocation->GetBlock() == this);
10690
VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
10691
"Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
10692
const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
10693
// This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
10694
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
10695
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
10696
}
10697
#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
10698
10699
#ifndef _VMA_ALLOCATION_T_FUNCTIONS
10700
VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)
10701
: m_Alignment{ 1 },
10702
m_Size{ 0 },
10703
m_pUserData{ VMA_NULL },
10704
m_pName{ VMA_NULL },
10705
m_MemoryTypeIndex{ 0 },
10706
m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },
10707
m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },
10708
m_MapCount{ 0 },
10709
m_Flags{ 0 }
10710
{
10711
if(mappingAllowed)
10712
m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;
10713
}
10714
10715
VmaAllocation_T::~VmaAllocation_T()
10716
{
10717
VMA_ASSERT_LEAK(m_MapCount == 0 && "Allocation was not unmapped before destruction.");
10718
10719
// Check if owned string was freed.
10720
VMA_ASSERT(m_pName == VMA_NULL);
10721
}
10722
10723
void VmaAllocation_T::InitBlockAllocation(
10724
VmaDeviceMemoryBlock* block,
10725
VmaAllocHandle allocHandle,
10726
VkDeviceSize alignment,
10727
VkDeviceSize size,
10728
uint32_t memoryTypeIndex,
10729
VmaSuballocationType suballocationType,
10730
bool mapped)
10731
{
10732
VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
10733
VMA_ASSERT(block != VMA_NULL);
10734
m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
10735
m_Alignment = alignment;
10736
m_Size = size;
10737
m_MemoryTypeIndex = memoryTypeIndex;
10738
if(mapped)
10739
{
10740
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
10741
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
10742
}
10743
m_SuballocationType = (uint8_t)suballocationType;
10744
m_BlockAllocation.m_Block = block;
10745
m_BlockAllocation.m_AllocHandle = allocHandle;
10746
}
10747
10748
void VmaAllocation_T::InitDedicatedAllocation(
10749
VmaPool hParentPool,
10750
uint32_t memoryTypeIndex,
10751
VkDeviceMemory hMemory,
10752
VmaSuballocationType suballocationType,
10753
void* pMappedData,
10754
VkDeviceSize size)
10755
{
10756
VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
10757
VMA_ASSERT(hMemory != VK_NULL_HANDLE);
10758
m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
10759
m_Alignment = 0;
10760
m_Size = size;
10761
m_MemoryTypeIndex = memoryTypeIndex;
10762
m_SuballocationType = (uint8_t)suballocationType;
10763
if(pMappedData != VMA_NULL)
10764
{
10765
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
10766
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
10767
}
10768
m_DedicatedAllocation.m_hParentPool = hParentPool;
10769
m_DedicatedAllocation.m_hMemory = hMemory;
10770
m_DedicatedAllocation.m_pMappedData = pMappedData;
10771
m_DedicatedAllocation.m_Prev = VMA_NULL;
10772
m_DedicatedAllocation.m_Next = VMA_NULL;
10773
}
10774
10775
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
10776
{
10777
VMA_ASSERT(pName == VMA_NULL || pName != m_pName);
10778
10779
FreeName(hAllocator);
10780
10781
if (pName != VMA_NULL)
10782
m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);
10783
}
10784
10785
uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)
10786
{
10787
VMA_ASSERT(allocation != VMA_NULL);
10788
VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
10789
VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);
10790
10791
if (m_MapCount != 0)
10792
m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);
10793
10794
m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);
10795
std::swap(m_BlockAllocation, allocation->m_BlockAllocation);
10796
m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);
10797
10798
#if VMA_STATS_STRING_ENABLED
10799
std::swap(m_BufferImageUsage, allocation->m_BufferImageUsage);
10800
#endif
10801
return m_MapCount;
10802
}
10803
10804
VmaAllocHandle VmaAllocation_T::GetAllocHandle() const
10805
{
10806
switch (m_Type)
10807
{
10808
case ALLOCATION_TYPE_BLOCK:
10809
return m_BlockAllocation.m_AllocHandle;
10810
case ALLOCATION_TYPE_DEDICATED:
10811
return VK_NULL_HANDLE;
10812
default:
10813
VMA_ASSERT(0);
10814
return VK_NULL_HANDLE;
10815
}
10816
}
10817
10818
VkDeviceSize VmaAllocation_T::GetOffset() const
10819
{
10820
switch (m_Type)
10821
{
10822
case ALLOCATION_TYPE_BLOCK:
10823
return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);
10824
case ALLOCATION_TYPE_DEDICATED:
10825
return 0;
10826
default:
10827
VMA_ASSERT(0);
10828
return 0;
10829
}
10830
}
10831
10832
VmaPool VmaAllocation_T::GetParentPool() const
10833
{
10834
switch (m_Type)
10835
{
10836
case ALLOCATION_TYPE_BLOCK:
10837
return m_BlockAllocation.m_Block->GetParentPool();
10838
case ALLOCATION_TYPE_DEDICATED:
10839
return m_DedicatedAllocation.m_hParentPool;
10840
default:
10841
VMA_ASSERT(0);
10842
return VK_NULL_HANDLE;
10843
}
10844
}
10845
10846
VkDeviceMemory VmaAllocation_T::GetMemory() const
10847
{
10848
switch (m_Type)
10849
{
10850
case ALLOCATION_TYPE_BLOCK:
10851
return m_BlockAllocation.m_Block->GetDeviceMemory();
10852
case ALLOCATION_TYPE_DEDICATED:
10853
return m_DedicatedAllocation.m_hMemory;
10854
default:
10855
VMA_ASSERT(0);
10856
return VK_NULL_HANDLE;
10857
}
10858
}
10859
10860
void* VmaAllocation_T::GetMappedData() const
10861
{
10862
switch (m_Type)
10863
{
10864
case ALLOCATION_TYPE_BLOCK:
10865
if (m_MapCount != 0 || IsPersistentMap())
10866
{
10867
void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
10868
VMA_ASSERT(pBlockData != VMA_NULL);
10869
return (char*)pBlockData + GetOffset();
10870
}
10871
else
10872
{
10873
return VMA_NULL;
10874
}
10875
break;
10876
case ALLOCATION_TYPE_DEDICATED:
10877
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
10878
return m_DedicatedAllocation.m_pMappedData;
10879
default:
10880
VMA_ASSERT(0);
10881
return VMA_NULL;
10882
}
10883
}
10884
10885
void VmaAllocation_T::BlockAllocMap()
10886
{
10887
VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
10888
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
10889
10890
if (m_MapCount < 0xFF)
10891
{
10892
++m_MapCount;
10893
}
10894
else
10895
{
10896
VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
10897
}
10898
}
10899
10900
void VmaAllocation_T::BlockAllocUnmap()
10901
{
10902
VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
10903
10904
if (m_MapCount > 0)
10905
{
10906
--m_MapCount;
10907
}
10908
else
10909
{
10910
VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
10911
}
10912
}
10913
10914
VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
10915
{
10916
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
10917
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
10918
10919
if (m_MapCount != 0 || IsPersistentMap())
10920
{
10921
if (m_MapCount < 0xFF)
10922
{
10923
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
10924
*ppData = m_DedicatedAllocation.m_pMappedData;
10925
++m_MapCount;
10926
return VK_SUCCESS;
10927
}
10928
else
10929
{
10930
VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
10931
return VK_ERROR_MEMORY_MAP_FAILED;
10932
}
10933
}
10934
else
10935
{
10936
VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
10937
hAllocator->m_hDevice,
10938
m_DedicatedAllocation.m_hMemory,
10939
0, // offset
10940
VK_WHOLE_SIZE,
10941
0, // flags
10942
ppData);
10943
if (result == VK_SUCCESS)
10944
{
10945
m_DedicatedAllocation.m_pMappedData = *ppData;
10946
m_MapCount = 1;
10947
}
10948
return result;
10949
}
10950
}
10951
10952
void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
10953
{
10954
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
10955
10956
if (m_MapCount > 0)
10957
{
10958
--m_MapCount;
10959
if (m_MapCount == 0 && !IsPersistentMap())
10960
{
10961
m_DedicatedAllocation.m_pMappedData = VMA_NULL;
10962
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
10963
hAllocator->m_hDevice,
10964
m_DedicatedAllocation.m_hMemory);
10965
}
10966
}
10967
else
10968
{
10969
VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
10970
}
10971
}
10972
10973
#if VMA_STATS_STRING_ENABLED
10974
void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
10975
{
10976
json.WriteString("Type");
10977
json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
10978
10979
json.WriteString("Size");
10980
json.WriteNumber(m_Size);
10981
json.WriteString("Usage");
10982
json.WriteNumber(m_BufferImageUsage.Value); // It may be uint32_t or uint64_t.
10983
10984
if (m_pUserData != VMA_NULL)
10985
{
10986
json.WriteString("CustomData");
10987
json.BeginString();
10988
json.ContinueString_Pointer(m_pUserData);
10989
json.EndString();
10990
}
10991
if (m_pName != VMA_NULL)
10992
{
10993
json.WriteString("Name");
10994
json.WriteString(m_pName);
10995
}
10996
}
10997
#endif // VMA_STATS_STRING_ENABLED
10998
10999
void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
11000
{
11001
if(m_pName)
11002
{
11003
VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);
11004
m_pName = VMA_NULL;
11005
}
11006
}
11007
#endif // _VMA_ALLOCATION_T_FUNCTIONS
11008
11009
#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS
11010
VmaBlockVector::VmaBlockVector(
11011
VmaAllocator hAllocator,
11012
VmaPool hParentPool,
11013
uint32_t memoryTypeIndex,
11014
VkDeviceSize preferredBlockSize,
11015
size_t minBlockCount,
11016
size_t maxBlockCount,
11017
VkDeviceSize bufferImageGranularity,
11018
bool explicitBlockSize,
11019
uint32_t algorithm,
11020
float priority,
11021
VkDeviceSize minAllocationAlignment,
11022
void* pMemoryAllocateNext)
11023
: m_hAllocator(hAllocator),
11024
m_hParentPool(hParentPool),
11025
m_MemoryTypeIndex(memoryTypeIndex),
11026
m_PreferredBlockSize(preferredBlockSize),
11027
m_MinBlockCount(minBlockCount),
11028
m_MaxBlockCount(maxBlockCount),
11029
m_BufferImageGranularity(bufferImageGranularity),
11030
m_ExplicitBlockSize(explicitBlockSize),
11031
m_Algorithm(algorithm),
11032
m_Priority(priority),
11033
m_MinAllocationAlignment(minAllocationAlignment),
11034
m_pMemoryAllocateNext(pMemoryAllocateNext),
11035
m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11036
m_NextBlockId(0) {}
11037
11038
VmaBlockVector::~VmaBlockVector()
11039
{
11040
for (size_t i = m_Blocks.size(); i--; )
11041
{
11042
m_Blocks[i]->Destroy(m_hAllocator);
11043
vma_delete(m_hAllocator, m_Blocks[i]);
11044
}
11045
}
11046
11047
VkResult VmaBlockVector::CreateMinBlocks()
11048
{
11049
for (size_t i = 0; i < m_MinBlockCount; ++i)
11050
{
11051
VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11052
if (res != VK_SUCCESS)
11053
{
11054
return res;
11055
}
11056
}
11057
return VK_SUCCESS;
11058
}
11059
11060
void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)
11061
{
11062
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11063
11064
const size_t blockCount = m_Blocks.size();
11065
for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11066
{
11067
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11068
VMA_ASSERT(pBlock);
11069
VMA_HEAVY_ASSERT(pBlock->Validate());
11070
pBlock->m_pMetadata->AddStatistics(inoutStats);
11071
}
11072
}
11073
11074
void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
11075
{
11076
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11077
11078
const size_t blockCount = m_Blocks.size();
11079
for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11080
{
11081
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11082
VMA_ASSERT(pBlock);
11083
VMA_HEAVY_ASSERT(pBlock->Validate());
11084
pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);
11085
}
11086
}
11087
11088
bool VmaBlockVector::IsEmpty()
11089
{
11090
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11091
return m_Blocks.empty();
11092
}
11093
11094
bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11095
{
11096
const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11097
return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11098
(VMA_DEBUG_MARGIN > 0) &&
11099
(m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
11100
(m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11101
}
11102
11103
VkResult VmaBlockVector::Allocate(
11104
VkDeviceSize size,
11105
VkDeviceSize alignment,
11106
const VmaAllocationCreateInfo& createInfo,
11107
VmaSuballocationType suballocType,
11108
size_t allocationCount,
11109
VmaAllocation* pAllocations)
11110
{
11111
size_t allocIndex;
11112
VkResult res = VK_SUCCESS;
11113
11114
alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
11115
11116
if (IsCorruptionDetectionEnabled())
11117
{
11118
size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11119
alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11120
}
11121
11122
{
11123
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11124
for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11125
{
11126
res = AllocatePage(
11127
size,
11128
alignment,
11129
createInfo,
11130
suballocType,
11131
pAllocations + allocIndex);
11132
if (res != VK_SUCCESS)
11133
{
11134
break;
11135
}
11136
}
11137
}
11138
11139
if (res != VK_SUCCESS)
11140
{
11141
// Free all already created allocations.
11142
while (allocIndex--)
11143
Free(pAllocations[allocIndex]);
11144
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11145
}
11146
11147
return res;
11148
}
11149
11150
VkResult VmaBlockVector::AllocatePage(
11151
VkDeviceSize size,
11152
VkDeviceSize alignment,
11153
const VmaAllocationCreateInfo& createInfo,
11154
VmaSuballocationType suballocType,
11155
VmaAllocation* pAllocation)
11156
{
11157
const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11158
11159
VkDeviceSize freeMemory;
11160
{
11161
const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11162
VmaBudget heapBudget = {};
11163
m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
11164
freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
11165
}
11166
11167
const bool canFallbackToDedicated = !HasExplicitBlockSize() &&
11168
(createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;
11169
const bool canCreateNewBlock =
11170
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11171
(m_Blocks.size() < m_MaxBlockCount) &&
11172
(freeMemory >= size || !canFallbackToDedicated);
11173
uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11174
11175
// Upper address can only be used with linear allocator and within single memory block.
11176
if (isUpperAddress &&
11177
(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11178
{
11179
return VK_ERROR_FEATURE_NOT_PRESENT;
11180
}
11181
11182
// Early reject: requested allocation size is larger that maximum block size for this block vector.
11183
if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11184
{
11185
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11186
}
11187
11188
// 1. Search existing allocations. Try to allocate.
11189
if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11190
{
11191
// Use only last block.
11192
if (!m_Blocks.empty())
11193
{
11194
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11195
VMA_ASSERT(pCurrBlock);
11196
VkResult res = AllocateFromBlock(
11197
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
11198
if (res == VK_SUCCESS)
11199
{
11200
VMA_DEBUG_LOG_FORMAT(" Returned from last block #%" PRIu32, pCurrBlock->GetId());
11201
IncrementallySortBlocks();
11202
return VK_SUCCESS;
11203
}
11204
}
11205
}
11206
else
11207
{
11208
if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default
11209
{
11210
const bool isHostVisible =
11211
(m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
11212
if(isHostVisible)
11213
{
11214
const bool isMappingAllowed = (createInfo.flags &
11215
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
11216
/*
11217
For non-mappable allocations, check blocks that are not mapped first.
11218
For mappable allocations, check blocks that are already mapped first.
11219
This way, having many blocks, we will separate mappable and non-mappable allocations,
11220
hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc.
11221
*/
11222
for(size_t mappingI = 0; mappingI < 2; ++mappingI)
11223
{
11224
// Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11225
for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11226
{
11227
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11228
VMA_ASSERT(pCurrBlock);
11229
const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;
11230
if((mappingI == 0) == (isMappingAllowed == isBlockMapped))
11231
{
11232
VkResult res = AllocateFromBlock(
11233
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
11234
if (res == VK_SUCCESS)
11235
{
11236
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId());
11237
IncrementallySortBlocks();
11238
return VK_SUCCESS;
11239
}
11240
}
11241
}
11242
}
11243
}
11244
else
11245
{
11246
// Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11247
for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11248
{
11249
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11250
VMA_ASSERT(pCurrBlock);
11251
VkResult res = AllocateFromBlock(
11252
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
11253
if (res == VK_SUCCESS)
11254
{
11255
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId());
11256
IncrementallySortBlocks();
11257
return VK_SUCCESS;
11258
}
11259
}
11260
}
11261
}
11262
else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
11263
{
11264
// Backward order in m_Blocks - prefer blocks with largest amount of free space.
11265
for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
11266
{
11267
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11268
VMA_ASSERT(pCurrBlock);
11269
VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
11270
if (res == VK_SUCCESS)
11271
{
11272
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId());
11273
IncrementallySortBlocks();
11274
return VK_SUCCESS;
11275
}
11276
}
11277
}
11278
}
11279
11280
// 2. Try to create new block.
11281
if (canCreateNewBlock)
11282
{
11283
// Calculate optimal size for new block.
11284
VkDeviceSize newBlockSize = m_PreferredBlockSize;
11285
uint32_t newBlockSizeShift = 0;
11286
const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11287
11288
if (!m_ExplicitBlockSize)
11289
{
11290
// Allocate 1/8, 1/4, 1/2 as first blocks.
11291
const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11292
for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11293
{
11294
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11295
if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11296
{
11297
newBlockSize = smallerNewBlockSize;
11298
++newBlockSizeShift;
11299
}
11300
else
11301
{
11302
break;
11303
}
11304
}
11305
}
11306
11307
size_t newBlockIndex = 0;
11308
VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11309
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11310
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11311
if (!m_ExplicitBlockSize)
11312
{
11313
while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11314
{
11315
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11316
if (smallerNewBlockSize >= size)
11317
{
11318
newBlockSize = smallerNewBlockSize;
11319
++newBlockSizeShift;
11320
res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11321
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11322
}
11323
else
11324
{
11325
break;
11326
}
11327
}
11328
}
11329
11330
if (res == VK_SUCCESS)
11331
{
11332
VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11333
VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11334
11335
res = AllocateFromBlock(
11336
pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
11337
if (res == VK_SUCCESS)
11338
{
11339
VMA_DEBUG_LOG_FORMAT(" Created new block #%" PRIu32 " Size=%" PRIu64, pBlock->GetId(), newBlockSize);
11340
IncrementallySortBlocks();
11341
return VK_SUCCESS;
11342
}
11343
else
11344
{
11345
// Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11346
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11347
}
11348
}
11349
}
11350
11351
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11352
}
11353
11354
void VmaBlockVector::Free(const VmaAllocation hAllocation)
11355
{
11356
VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11357
11358
bool budgetExceeded = false;
11359
{
11360
const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11361
VmaBudget heapBudget = {};
11362
m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
11363
budgetExceeded = heapBudget.usage >= heapBudget.budget;
11364
}
11365
11366
// Scope for lock.
11367
{
11368
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11369
11370
VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11371
11372
if (IsCorruptionDetectionEnabled())
11373
{
11374
VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11375
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11376
}
11377
11378
if (hAllocation->IsPersistentMap())
11379
{
11380
pBlock->Unmap(m_hAllocator, 1);
11381
}
11382
11383
const bool hadEmptyBlockBeforeFree = HasEmptyBlock();
11384
pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());
11385
pBlock->PostFree(m_hAllocator);
11386
VMA_HEAVY_ASSERT(pBlock->Validate());
11387
11388
VMA_DEBUG_LOG_FORMAT(" Freed from MemoryTypeIndex=%" PRIu32, m_MemoryTypeIndex);
11389
11390
const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
11391
// pBlock became empty after this deallocation.
11392
if (pBlock->m_pMetadata->IsEmpty())
11393
{
11394
// Already had empty block. We don't want to have two, so delete this one.
11395
if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)
11396
{
11397
pBlockToDelete = pBlock;
11398
Remove(pBlock);
11399
}
11400
// else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth.
11401
}
11402
// pBlock didn't become empty, but we have another empty block - find and free that one.
11403
// (This is optional, heuristics.)
11404
else if (hadEmptyBlockBeforeFree && canDeleteBlock)
11405
{
11406
VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11407
if (pLastBlock->m_pMetadata->IsEmpty())
11408
{
11409
pBlockToDelete = pLastBlock;
11410
m_Blocks.pop_back();
11411
}
11412
}
11413
11414
IncrementallySortBlocks();
11415
}
11416
11417
// Destruction of a free block. Deferred until this point, outside of mutex
11418
// lock, for performance reason.
11419
if (pBlockToDelete != VMA_NULL)
11420
{
11421
VMA_DEBUG_LOG_FORMAT(" Deleted empty block #%" PRIu32, pBlockToDelete->GetId());
11422
pBlockToDelete->Destroy(m_hAllocator);
11423
vma_delete(m_hAllocator, pBlockToDelete);
11424
}
11425
11426
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
11427
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
11428
}
11429
11430
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11431
{
11432
VkDeviceSize result = 0;
11433
for (size_t i = m_Blocks.size(); i--; )
11434
{
11435
result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11436
if (result >= m_PreferredBlockSize)
11437
{
11438
break;
11439
}
11440
}
11441
return result;
11442
}
11443
11444
void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11445
{
11446
for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11447
{
11448
if (m_Blocks[blockIndex] == pBlock)
11449
{
11450
VmaVectorRemove(m_Blocks, blockIndex);
11451
return;
11452
}
11453
}
11454
VMA_ASSERT(0);
11455
}
11456
11457
void VmaBlockVector::IncrementallySortBlocks()
11458
{
11459
if (!m_IncrementalSort)
11460
return;
11461
if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11462
{
11463
// Bubble sort only until first swap.
11464
for (size_t i = 1; i < m_Blocks.size(); ++i)
11465
{
11466
if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11467
{
11468
std::swap(m_Blocks[i - 1], m_Blocks[i]);
11469
return;
11470
}
11471
}
11472
}
11473
}
11474
11475
void VmaBlockVector::SortByFreeSize()
11476
{
11477
VMA_SORT(m_Blocks.begin(), m_Blocks.end(),
11478
[](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool
11479
{
11480
return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();
11481
});
11482
}
11483
11484
VkResult VmaBlockVector::AllocateFromBlock(
11485
VmaDeviceMemoryBlock* pBlock,
11486
VkDeviceSize size,
11487
VkDeviceSize alignment,
11488
VmaAllocationCreateFlags allocFlags,
11489
void* pUserData,
11490
VmaSuballocationType suballocType,
11491
uint32_t strategy,
11492
VmaAllocation* pAllocation)
11493
{
11494
const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11495
11496
VmaAllocationRequest currRequest = {};
11497
if (pBlock->m_pMetadata->CreateAllocationRequest(
11498
size,
11499
alignment,
11500
isUpperAddress,
11501
suballocType,
11502
strategy,
11503
&currRequest))
11504
{
11505
return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);
11506
}
11507
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11508
}
11509
11510
VkResult VmaBlockVector::CommitAllocationRequest(
11511
VmaAllocationRequest& allocRequest,
11512
VmaDeviceMemoryBlock* pBlock,
11513
VkDeviceSize alignment,
11514
VmaAllocationCreateFlags allocFlags,
11515
void* pUserData,
11516
VmaSuballocationType suballocType,
11517
VmaAllocation* pAllocation)
11518
{
11519
const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11520
const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11521
const bool isMappingAllowed = (allocFlags &
11522
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
11523
11524
pBlock->PostAlloc(m_hAllocator);
11525
// Allocate from pCurrBlock.
11526
if (mapped)
11527
{
11528
VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11529
if (res != VK_SUCCESS)
11530
{
11531
return res;
11532
}
11533
}
11534
11535
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);
11536
pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);
11537
(*pAllocation)->InitBlockAllocation(
11538
pBlock,
11539
allocRequest.allocHandle,
11540
alignment,
11541
allocRequest.size, // Not size, as actual allocation size may be larger than requested!
11542
m_MemoryTypeIndex,
11543
suballocType,
11544
mapped);
11545
VMA_HEAVY_ASSERT(pBlock->Validate());
11546
if (isUserDataString)
11547
(*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);
11548
else
11549
(*pAllocation)->SetUserData(m_hAllocator, pUserData);
11550
m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);
11551
if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11552
{
11553
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11554
}
11555
if (IsCorruptionDetectionEnabled())
11556
{
11557
VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);
11558
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11559
}
11560
return VK_SUCCESS;
11561
}
11562
11563
VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11564
{
11565
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11566
allocInfo.pNext = m_pMemoryAllocateNext;
11567
allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11568
allocInfo.allocationSize = blockSize;
11569
11570
#if VMA_BUFFER_DEVICE_ADDRESS
11571
// Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
11572
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
11573
if (m_hAllocator->m_UseKhrBufferDeviceAddress)
11574
{
11575
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
11576
VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
11577
}
11578
#endif // VMA_BUFFER_DEVICE_ADDRESS
11579
11580
#if VMA_MEMORY_PRIORITY
11581
VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
11582
if (m_hAllocator->m_UseExtMemoryPriority)
11583
{
11584
VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f);
11585
priorityInfo.priority = m_Priority;
11586
VmaPnextChainPushFront(&allocInfo, &priorityInfo);
11587
}
11588
#endif // VMA_MEMORY_PRIORITY
11589
11590
#if VMA_EXTERNAL_MEMORY
11591
// Attach VkExportMemoryAllocateInfoKHR if necessary.
11592
VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
11593
exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
11594
if (exportMemoryAllocInfo.handleTypes != 0)
11595
{
11596
VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
11597
}
11598
#endif // VMA_EXTERNAL_MEMORY
11599
11600
VkDeviceMemory mem = VK_NULL_HANDLE;
11601
VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11602
if (res < 0)
11603
{
11604
return res;
11605
}
11606
11607
// New VkDeviceMemory successfully created.
11608
11609
// Create new Allocation for it.
11610
VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11611
pBlock->Init(
11612
m_hAllocator,
11613
m_hParentPool,
11614
m_MemoryTypeIndex,
11615
mem,
11616
allocInfo.allocationSize,
11617
m_NextBlockId++,
11618
m_Algorithm,
11619
m_BufferImageGranularity);
11620
11621
m_Blocks.push_back(pBlock);
11622
if (pNewBlockIndex != VMA_NULL)
11623
{
11624
*pNewBlockIndex = m_Blocks.size() - 1;
11625
}
11626
11627
return VK_SUCCESS;
11628
}
11629
11630
bool VmaBlockVector::HasEmptyBlock()
11631
{
11632
for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)
11633
{
11634
VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
11635
if (pBlock->m_pMetadata->IsEmpty())
11636
{
11637
return true;
11638
}
11639
}
11640
return false;
11641
}
11642
11643
#if VMA_STATS_STRING_ENABLED
11644
void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
11645
{
11646
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11647
11648
11649
json.BeginObject();
11650
for (size_t i = 0; i < m_Blocks.size(); ++i)
11651
{
11652
json.BeginString();
11653
json.ContinueString(m_Blocks[i]->GetId());
11654
json.EndString();
11655
11656
json.BeginObject();
11657
json.WriteString("MapRefCount");
11658
json.WriteNumber(m_Blocks[i]->GetMapRefCount());
11659
11660
m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
11661
json.EndObject();
11662
}
11663
json.EndObject();
11664
}
11665
#endif // VMA_STATS_STRING_ENABLED
11666
11667
VkResult VmaBlockVector::CheckCorruption()
11668
{
11669
if (!IsCorruptionDetectionEnabled())
11670
{
11671
return VK_ERROR_FEATURE_NOT_PRESENT;
11672
}
11673
11674
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11675
for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11676
{
11677
VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11678
VMA_ASSERT(pBlock);
11679
VkResult res = pBlock->CheckCorruption(m_hAllocator);
11680
if (res != VK_SUCCESS)
11681
{
11682
return res;
11683
}
11684
}
11685
return VK_SUCCESS;
11686
}
11687
11688
#endif // _VMA_BLOCK_VECTOR_FUNCTIONS
11689
11690
#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
11691
VmaDefragmentationContext_T::VmaDefragmentationContext_T(
11692
VmaAllocator hAllocator,
11693
const VmaDefragmentationInfo& info)
11694
: m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),
11695
m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),
11696
m_BreakCallback(info.pfnBreakCallback),
11697
m_BreakCallbackUserData(info.pBreakCallbackUserData),
11698
m_MoveAllocator(hAllocator->GetAllocationCallbacks()),
11699
m_Moves(m_MoveAllocator)
11700
{
11701
m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK;
11702
11703
if (info.pool != VMA_NULL)
11704
{
11705
m_BlockVectorCount = 1;
11706
m_PoolBlockVector = &info.pool->m_BlockVector;
11707
m_pBlockVectors = &m_PoolBlockVector;
11708
m_PoolBlockVector->SetIncrementalSort(false);
11709
m_PoolBlockVector->SortByFreeSize();
11710
}
11711
else
11712
{
11713
m_BlockVectorCount = hAllocator->GetMemoryTypeCount();
11714
m_PoolBlockVector = VMA_NULL;
11715
m_pBlockVectors = hAllocator->m_pBlockVectors;
11716
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
11717
{
11718
VmaBlockVector* vector = m_pBlockVectors[i];
11719
if (vector != VMA_NULL)
11720
{
11721
vector->SetIncrementalSort(false);
11722
vector->SortByFreeSize();
11723
}
11724
}
11725
}
11726
11727
switch (m_Algorithm)
11728
{
11729
case 0: // Default algorithm
11730
m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;
11731
m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
11732
break;
11733
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
11734
m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
11735
break;
11736
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
11737
if (hAllocator->GetBufferImageGranularity() > 1)
11738
{
11739
m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);
11740
}
11741
break;
11742
}
11743
}
11744
11745
VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
11746
{
11747
if (m_PoolBlockVector != VMA_NULL)
11748
{
11749
m_PoolBlockVector->SetIncrementalSort(true);
11750
}
11751
else
11752
{
11753
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
11754
{
11755
VmaBlockVector* vector = m_pBlockVectors[i];
11756
if (vector != VMA_NULL)
11757
vector->SetIncrementalSort(true);
11758
}
11759
}
11760
11761
if (m_AlgorithmState)
11762
{
11763
switch (m_Algorithm)
11764
{
11765
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
11766
vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);
11767
break;
11768
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
11769
vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);
11770
break;
11771
default:
11772
VMA_ASSERT(0);
11773
}
11774
}
11775
}
11776
11777
VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)
11778
{
11779
if (m_PoolBlockVector != VMA_NULL)
11780
{
11781
VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);
11782
11783
if (m_PoolBlockVector->GetBlockCount() > 1)
11784
ComputeDefragmentation(*m_PoolBlockVector, 0);
11785
else if (m_PoolBlockVector->GetBlockCount() == 1)
11786
ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));
11787
}
11788
else
11789
{
11790
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
11791
{
11792
if (m_pBlockVectors[i] != VMA_NULL)
11793
{
11794
VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);
11795
11796
if (m_pBlockVectors[i]->GetBlockCount() > 1)
11797
{
11798
if (ComputeDefragmentation(*m_pBlockVectors[i], i))
11799
break;
11800
}
11801
else if (m_pBlockVectors[i]->GetBlockCount() == 1)
11802
{
11803
if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))
11804
break;
11805
}
11806
}
11807
}
11808
}
11809
11810
moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());
11811
if (moveInfo.moveCount > 0)
11812
{
11813
moveInfo.pMoves = m_Moves.data();
11814
return VK_INCOMPLETE;
11815
}
11816
11817
moveInfo.pMoves = VMA_NULL;
11818
return VK_SUCCESS;
11819
}
11820
11821
VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)
11822
{
11823
VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);
11824
11825
VkResult result = VK_SUCCESS;
11826
VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);
11827
VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);
11828
VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);
11829
11830
VmaAllocator allocator = VMA_NULL;
11831
for (uint32_t i = 0; i < moveInfo.moveCount; ++i)
11832
{
11833
VmaDefragmentationMove& move = moveInfo.pMoves[i];
11834
size_t prevCount = 0, currentCount = 0;
11835
VkDeviceSize freedBlockSize = 0;
11836
11837
uint32_t vectorIndex;
11838
VmaBlockVector* vector;
11839
if (m_PoolBlockVector != VMA_NULL)
11840
{
11841
vectorIndex = 0;
11842
vector = m_PoolBlockVector;
11843
}
11844
else
11845
{
11846
vectorIndex = move.srcAllocation->GetMemoryTypeIndex();
11847
vector = m_pBlockVectors[vectorIndex];
11848
VMA_ASSERT(vector != VMA_NULL);
11849
}
11850
11851
switch (move.operation)
11852
{
11853
case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:
11854
{
11855
uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);
11856
if (mapCount > 0)
11857
{
11858
allocator = vector->m_hAllocator;
11859
VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();
11860
bool notPresent = true;
11861
for (FragmentedBlock& block : mappedBlocks)
11862
{
11863
if (block.block == newMapBlock)
11864
{
11865
notPresent = false;
11866
block.data += mapCount;
11867
break;
11868
}
11869
}
11870
if (notPresent)
11871
mappedBlocks.push_back({ mapCount, newMapBlock });
11872
}
11873
11874
// Scope for locks, Free have it's own lock
11875
{
11876
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11877
prevCount = vector->GetBlockCount();
11878
freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
11879
}
11880
vector->Free(move.dstTmpAllocation);
11881
{
11882
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11883
currentCount = vector->GetBlockCount();
11884
}
11885
11886
result = VK_INCOMPLETE;
11887
break;
11888
}
11889
case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:
11890
{
11891
m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
11892
--m_PassStats.allocationsMoved;
11893
vector->Free(move.dstTmpAllocation);
11894
11895
VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();
11896
bool notPresent = true;
11897
for (const FragmentedBlock& block : immovableBlocks)
11898
{
11899
if (block.block == newBlock)
11900
{
11901
notPresent = false;
11902
break;
11903
}
11904
}
11905
if (notPresent)
11906
immovableBlocks.push_back({ vectorIndex, newBlock });
11907
break;
11908
}
11909
case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:
11910
{
11911
m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
11912
--m_PassStats.allocationsMoved;
11913
// Scope for locks, Free have it's own lock
11914
{
11915
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11916
prevCount = vector->GetBlockCount();
11917
freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();
11918
}
11919
vector->Free(move.srcAllocation);
11920
{
11921
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11922
currentCount = vector->GetBlockCount();
11923
}
11924
freedBlockSize *= prevCount - currentCount;
11925
11926
VkDeviceSize dstBlockSize;
11927
{
11928
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11929
dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
11930
}
11931
vector->Free(move.dstTmpAllocation);
11932
{
11933
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
11934
freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());
11935
currentCount = vector->GetBlockCount();
11936
}
11937
11938
result = VK_INCOMPLETE;
11939
break;
11940
}
11941
default:
11942
VMA_ASSERT(0);
11943
}
11944
11945
if (prevCount > currentCount)
11946
{
11947
size_t freedBlocks = prevCount - currentCount;
11948
m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);
11949
m_PassStats.bytesFreed += freedBlockSize;
11950
}
11951
11952
if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT &&
11953
m_AlgorithmState != VMA_NULL)
11954
{
11955
// Avoid unnecessary tries to allocate when new free block is available
11956
StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];
11957
if (state.firstFreeBlock != SIZE_MAX)
11958
{
11959
const size_t diff = prevCount - currentCount;
11960
if (state.firstFreeBlock >= diff)
11961
{
11962
state.firstFreeBlock -= diff;
11963
if (state.firstFreeBlock != 0)
11964
state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();
11965
}
11966
else
11967
state.firstFreeBlock = 0;
11968
}
11969
}
11970
}
11971
moveInfo.moveCount = 0;
11972
moveInfo.pMoves = VMA_NULL;
11973
m_Moves.clear();
11974
11975
// Update stats
11976
m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;
11977
m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;
11978
m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;
11979
m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;
11980
m_PassStats = { 0 };
11981
11982
// Move blocks with immovable allocations according to algorithm
11983
if (immovableBlocks.size() > 0)
11984
{
11985
do
11986
{
11987
if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT)
11988
{
11989
if (m_AlgorithmState != VMA_NULL)
11990
{
11991
bool swapped = false;
11992
// Move to the start of free blocks range
11993
for (const FragmentedBlock& block : immovableBlocks)
11994
{
11995
StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];
11996
if (state.operation != StateExtensive::Operation::Cleanup)
11997
{
11998
VmaBlockVector* vector = m_pBlockVectors[block.data];
11999
VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
12000
12001
for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)
12002
{
12003
if (vector->GetBlock(i) == block.block)
12004
{
12005
std::swap(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);
12006
if (state.firstFreeBlock != SIZE_MAX)
12007
{
12008
if (i + 1 < state.firstFreeBlock)
12009
{
12010
if (state.firstFreeBlock > 1)
12011
std::swap(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
12012
else
12013
--state.firstFreeBlock;
12014
}
12015
}
12016
swapped = true;
12017
break;
12018
}
12019
}
12020
}
12021
}
12022
if (swapped)
12023
result = VK_INCOMPLETE;
12024
break;
12025
}
12026
}
12027
12028
// Move to the beginning
12029
for (const FragmentedBlock& block : immovableBlocks)
12030
{
12031
VmaBlockVector* vector = m_pBlockVectors[block.data];
12032
VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
12033
12034
for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)
12035
{
12036
if (vector->GetBlock(i) == block.block)
12037
{
12038
std::swap(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);
12039
break;
12040
}
12041
}
12042
}
12043
} while (false);
12044
}
12045
12046
// Bulk-map destination blocks
12047
for (const FragmentedBlock& block : mappedBlocks)
12048
{
12049
VkResult res = block.block->Map(allocator, block.data, VMA_NULL);
12050
VMA_ASSERT(res == VK_SUCCESS);
12051
}
12052
return result;
12053
}
12054
12055
bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)
12056
{
12057
switch (m_Algorithm)
12058
{
12059
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:
12060
return ComputeDefragmentation_Fast(vector);
12061
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
12062
return ComputeDefragmentation_Balanced(vector, index, true);
12063
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:
12064
return ComputeDefragmentation_Full(vector);
12065
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
12066
return ComputeDefragmentation_Extensive(vector, index);
12067
default:
12068
VMA_ASSERT(0);
12069
return ComputeDefragmentation_Balanced(vector, index, true);
12070
}
12071
}
12072
12073
VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(
12074
VmaAllocHandle handle, VmaBlockMetadata* metadata)
12075
{
12076
MoveAllocationData moveData;
12077
moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);
12078
moveData.size = moveData.move.srcAllocation->GetSize();
12079
moveData.alignment = moveData.move.srcAllocation->GetAlignment();
12080
moveData.type = moveData.move.srcAllocation->GetSuballocationType();
12081
moveData.flags = 0;
12082
12083
if (moveData.move.srcAllocation->IsPersistentMap())
12084
moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
12085
if (moveData.move.srcAllocation->IsMappingAllowed())
12086
moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
12087
12088
return moveData;
12089
}
12090
12091
VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)
12092
{
12093
// Check custom criteria if exists
12094
if (m_BreakCallback && m_BreakCallback(m_BreakCallbackUserData))
12095
return CounterStatus::End;
12096
12097
// Ignore allocation if will exceed max size for copy
12098
if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)
12099
{
12100
if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)
12101
return CounterStatus::Ignore;
12102
else
12103
return CounterStatus::End;
12104
}
12105
else
12106
m_IgnoredAllocs = 0;
12107
return CounterStatus::Pass;
12108
}
12109
12110
bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)
12111
{
12112
m_PassStats.bytesMoved += bytes;
12113
// Early return when max found
12114
if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)
12115
{
12116
VMA_ASSERT((m_PassStats.allocationsMoved == m_MaxPassAllocations ||
12117
m_PassStats.bytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");
12118
return true;
12119
}
12120
return false;
12121
}
12122
12123
bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)
12124
{
12125
VmaBlockMetadata* metadata = block->m_pMetadata;
12126
12127
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
12128
handle != VK_NULL_HANDLE;
12129
handle = metadata->GetNextAllocation(handle))
12130
{
12131
MoveAllocationData moveData = GetMoveData(handle, metadata);
12132
// Ignore newly created allocations by defragmentation algorithm
12133
if (moveData.move.srcAllocation->GetUserData() == this)
12134
continue;
12135
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12136
{
12137
case CounterStatus::Ignore:
12138
continue;
12139
case CounterStatus::End:
12140
return true;
12141
case CounterStatus::Pass:
12142
break;
12143
default:
12144
VMA_ASSERT(0);
12145
}
12146
12147
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
12148
if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
12149
{
12150
VmaAllocationRequest request = {};
12151
if (metadata->CreateAllocationRequest(
12152
moveData.size,
12153
moveData.alignment,
12154
false,
12155
moveData.type,
12156
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
12157
&request))
12158
{
12159
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
12160
{
12161
if (vector.CommitAllocationRequest(
12162
request,
12163
block,
12164
moveData.alignment,
12165
moveData.flags,
12166
this,
12167
moveData.type,
12168
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
12169
{
12170
m_Moves.push_back(moveData.move);
12171
if (IncrementCounters(moveData.size))
12172
return true;
12173
}
12174
}
12175
}
12176
}
12177
}
12178
return false;
12179
}
12180
12181
bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)
12182
{
12183
for (; start < end; ++start)
12184
{
12185
VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);
12186
if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)
12187
{
12188
if (vector.AllocateFromBlock(dstBlock,
12189
data.size,
12190
data.alignment,
12191
data.flags,
12192
this,
12193
data.type,
12194
0,
12195
&data.move.dstTmpAllocation) == VK_SUCCESS)
12196
{
12197
m_Moves.push_back(data.move);
12198
if (IncrementCounters(data.size))
12199
return true;
12200
break;
12201
}
12202
}
12203
}
12204
return false;
12205
}
12206
12207
bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)
12208
{
12209
// Move only between blocks
12210
12211
// Go through allocations in last blocks and try to fit them inside first ones
12212
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
12213
{
12214
VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
12215
12216
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
12217
handle != VK_NULL_HANDLE;
12218
handle = metadata->GetNextAllocation(handle))
12219
{
12220
MoveAllocationData moveData = GetMoveData(handle, metadata);
12221
// Ignore newly created allocations by defragmentation algorithm
12222
if (moveData.move.srcAllocation->GetUserData() == this)
12223
continue;
12224
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12225
{
12226
case CounterStatus::Ignore:
12227
continue;
12228
case CounterStatus::End:
12229
return true;
12230
case CounterStatus::Pass:
12231
break;
12232
default:
12233
VMA_ASSERT(0);
12234
}
12235
12236
// Check all previous blocks for free space
12237
if (AllocInOtherBlock(0, i, moveData, vector))
12238
return true;
12239
}
12240
}
12241
return false;
12242
}
12243
12244
bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)
12245
{
12246
// Go over every allocation and try to fit it in previous blocks at lowest offsets,
12247
// if not possible: realloc within single block to minimize offset (exclude offset == 0),
12248
// but only if there are noticeable gaps between them (some heuristic, ex. average size of allocation in block)
12249
VMA_ASSERT(m_AlgorithmState != VMA_NULL);
12250
12251
StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];
12252
if (update && vectorState.avgAllocSize == UINT64_MAX)
12253
UpdateVectorStatistics(vector, vectorState);
12254
12255
const size_t startMoveCount = m_Moves.size();
12256
VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;
12257
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
12258
{
12259
VmaDeviceMemoryBlock* block = vector.GetBlock(i);
12260
VmaBlockMetadata* metadata = block->m_pMetadata;
12261
VkDeviceSize prevFreeRegionSize = 0;
12262
12263
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
12264
handle != VK_NULL_HANDLE;
12265
handle = metadata->GetNextAllocation(handle))
12266
{
12267
MoveAllocationData moveData = GetMoveData(handle, metadata);
12268
// Ignore newly created allocations by defragmentation algorithm
12269
if (moveData.move.srcAllocation->GetUserData() == this)
12270
continue;
12271
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12272
{
12273
case CounterStatus::Ignore:
12274
continue;
12275
case CounterStatus::End:
12276
return true;
12277
case CounterStatus::Pass:
12278
break;
12279
default:
12280
VMA_ASSERT(0);
12281
}
12282
12283
// Check all previous blocks for free space
12284
const size_t prevMoveCount = m_Moves.size();
12285
if (AllocInOtherBlock(0, i, moveData, vector))
12286
return true;
12287
12288
VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);
12289
// If no room found then realloc within block for lower offset
12290
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
12291
if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
12292
{
12293
// Check if realloc will make sense
12294
if (prevFreeRegionSize >= minimalFreeRegion ||
12295
nextFreeRegionSize >= minimalFreeRegion ||
12296
moveData.size <= vectorState.avgFreeSize ||
12297
moveData.size <= vectorState.avgAllocSize)
12298
{
12299
VmaAllocationRequest request = {};
12300
if (metadata->CreateAllocationRequest(
12301
moveData.size,
12302
moveData.alignment,
12303
false,
12304
moveData.type,
12305
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
12306
&request))
12307
{
12308
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
12309
{
12310
if (vector.CommitAllocationRequest(
12311
request,
12312
block,
12313
moveData.alignment,
12314
moveData.flags,
12315
this,
12316
moveData.type,
12317
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
12318
{
12319
m_Moves.push_back(moveData.move);
12320
if (IncrementCounters(moveData.size))
12321
return true;
12322
}
12323
}
12324
}
12325
}
12326
}
12327
prevFreeRegionSize = nextFreeRegionSize;
12328
}
12329
}
12330
12331
// No moves performed, update statistics to current vector state
12332
if (startMoveCount == m_Moves.size() && !update)
12333
{
12334
vectorState.avgAllocSize = UINT64_MAX;
12335
return ComputeDefragmentation_Balanced(vector, index, false);
12336
}
12337
return false;
12338
}
12339
12340
bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)
12341
{
12342
// Go over every allocation and try to fit it in previous blocks at lowest offsets,
12343
// if not possible: realloc within single block to minimize offset (exclude offset == 0)
12344
12345
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
12346
{
12347
VmaDeviceMemoryBlock* block = vector.GetBlock(i);
12348
VmaBlockMetadata* metadata = block->m_pMetadata;
12349
12350
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
12351
handle != VK_NULL_HANDLE;
12352
handle = metadata->GetNextAllocation(handle))
12353
{
12354
MoveAllocationData moveData = GetMoveData(handle, metadata);
12355
// Ignore newly created allocations by defragmentation algorithm
12356
if (moveData.move.srcAllocation->GetUserData() == this)
12357
continue;
12358
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12359
{
12360
case CounterStatus::Ignore:
12361
continue;
12362
case CounterStatus::End:
12363
return true;
12364
case CounterStatus::Pass:
12365
break;
12366
default:
12367
VMA_ASSERT(0);
12368
}
12369
12370
// Check all previous blocks for free space
12371
const size_t prevMoveCount = m_Moves.size();
12372
if (AllocInOtherBlock(0, i, moveData, vector))
12373
return true;
12374
12375
// If no room found then realloc within block for lower offset
12376
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
12377
if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
12378
{
12379
VmaAllocationRequest request = {};
12380
if (metadata->CreateAllocationRequest(
12381
moveData.size,
12382
moveData.alignment,
12383
false,
12384
moveData.type,
12385
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
12386
&request))
12387
{
12388
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
12389
{
12390
if (vector.CommitAllocationRequest(
12391
request,
12392
block,
12393
moveData.alignment,
12394
moveData.flags,
12395
this,
12396
moveData.type,
12397
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
12398
{
12399
m_Moves.push_back(moveData.move);
12400
if (IncrementCounters(moveData.size))
12401
return true;
12402
}
12403
}
12404
}
12405
}
12406
}
12407
}
12408
return false;
12409
}
12410
12411
bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)
12412
{
12413
// First free single block, then populate it to the brim, then free another block, and so on
12414
12415
// Fallback to previous algorithm since without granularity conflicts it can achieve max packing
12416
if (vector.m_BufferImageGranularity == 1)
12417
return ComputeDefragmentation_Full(vector);
12418
12419
VMA_ASSERT(m_AlgorithmState != VMA_NULL);
12420
12421
StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];
12422
12423
bool texturePresent = false, bufferPresent = false, otherPresent = false;
12424
switch (vectorState.operation)
12425
{
12426
case StateExtensive::Operation::Done: // Vector defragmented
12427
return false;
12428
case StateExtensive::Operation::FindFreeBlockBuffer:
12429
case StateExtensive::Operation::FindFreeBlockTexture:
12430
case StateExtensive::Operation::FindFreeBlockAll:
12431
{
12432
// No more blocks to free, just perform fast realloc and move to cleanup
12433
if (vectorState.firstFreeBlock == 0)
12434
{
12435
vectorState.operation = StateExtensive::Operation::Cleanup;
12436
return ComputeDefragmentation_Fast(vector);
12437
}
12438
12439
// No free blocks, have to clear last one
12440
size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;
12441
VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;
12442
12443
const size_t prevMoveCount = m_Moves.size();
12444
for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();
12445
handle != VK_NULL_HANDLE;
12446
handle = freeMetadata->GetNextAllocation(handle))
12447
{
12448
MoveAllocationData moveData = GetMoveData(handle, freeMetadata);
12449
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12450
{
12451
case CounterStatus::Ignore:
12452
continue;
12453
case CounterStatus::End:
12454
return true;
12455
case CounterStatus::Pass:
12456
break;
12457
default:
12458
VMA_ASSERT(0);
12459
}
12460
12461
// Check all previous blocks for free space
12462
if (AllocInOtherBlock(0, last, moveData, vector))
12463
{
12464
// Full clear performed already
12465
if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)
12466
vectorState.firstFreeBlock = last;
12467
return true;
12468
}
12469
}
12470
12471
if (prevMoveCount == m_Moves.size())
12472
{
12473
// Cannot perform full clear, have to move data in other blocks around
12474
if (last != 0)
12475
{
12476
for (size_t i = last - 1; i; --i)
12477
{
12478
if (ReallocWithinBlock(vector, vector.GetBlock(i)))
12479
return true;
12480
}
12481
}
12482
12483
if (prevMoveCount == m_Moves.size())
12484
{
12485
// No possible reallocs within blocks, try to move them around fast
12486
return ComputeDefragmentation_Fast(vector);
12487
}
12488
}
12489
else
12490
{
12491
switch (vectorState.operation)
12492
{
12493
case StateExtensive::Operation::FindFreeBlockBuffer:
12494
vectorState.operation = StateExtensive::Operation::MoveBuffers;
12495
break;
12496
case StateExtensive::Operation::FindFreeBlockTexture:
12497
vectorState.operation = StateExtensive::Operation::MoveTextures;
12498
break;
12499
case StateExtensive::Operation::FindFreeBlockAll:
12500
vectorState.operation = StateExtensive::Operation::MoveAll;
12501
break;
12502
default:
12503
VMA_ASSERT(0);
12504
vectorState.operation = StateExtensive::Operation::MoveTextures;
12505
}
12506
vectorState.firstFreeBlock = last;
12507
// Nothing done, block found without reallocations, can perform another reallocs in same pass
12508
return ComputeDefragmentation_Extensive(vector, index);
12509
}
12510
break;
12511
}
12512
case StateExtensive::Operation::MoveTextures:
12513
{
12514
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,
12515
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
12516
{
12517
if (texturePresent)
12518
{
12519
vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;
12520
return ComputeDefragmentation_Extensive(vector, index);
12521
}
12522
12523
if (!bufferPresent && !otherPresent)
12524
{
12525
vectorState.operation = StateExtensive::Operation::Cleanup;
12526
break;
12527
}
12528
12529
// No more textures to move, check buffers
12530
vectorState.operation = StateExtensive::Operation::MoveBuffers;
12531
bufferPresent = false;
12532
otherPresent = false;
12533
}
12534
else
12535
break;
12536
VMA_FALLTHROUGH; // Fallthrough
12537
}
12538
case StateExtensive::Operation::MoveBuffers:
12539
{
12540
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,
12541
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
12542
{
12543
if (bufferPresent)
12544
{
12545
vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
12546
return ComputeDefragmentation_Extensive(vector, index);
12547
}
12548
12549
if (!otherPresent)
12550
{
12551
vectorState.operation = StateExtensive::Operation::Cleanup;
12552
break;
12553
}
12554
12555
// No more buffers to move, check all others
12556
vectorState.operation = StateExtensive::Operation::MoveAll;
12557
otherPresent = false;
12558
}
12559
else
12560
break;
12561
VMA_FALLTHROUGH; // Fallthrough
12562
}
12563
case StateExtensive::Operation::MoveAll:
12564
{
12565
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,
12566
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
12567
{
12568
if (otherPresent)
12569
{
12570
vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
12571
return ComputeDefragmentation_Extensive(vector, index);
12572
}
12573
// Everything moved
12574
vectorState.operation = StateExtensive::Operation::Cleanup;
12575
}
12576
break;
12577
}
12578
case StateExtensive::Operation::Cleanup:
12579
// Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062).
12580
break;
12581
}
12582
12583
if (vectorState.operation == StateExtensive::Operation::Cleanup)
12584
{
12585
// All other work done, pack data in blocks even tighter if possible
12586
const size_t prevMoveCount = m_Moves.size();
12587
for (size_t i = 0; i < vector.GetBlockCount(); ++i)
12588
{
12589
if (ReallocWithinBlock(vector, vector.GetBlock(i)))
12590
return true;
12591
}
12592
12593
if (prevMoveCount == m_Moves.size())
12594
vectorState.operation = StateExtensive::Operation::Done;
12595
}
12596
return false;
12597
}
12598
12599
void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)
12600
{
12601
size_t allocCount = 0;
12602
size_t freeCount = 0;
12603
state.avgFreeSize = 0;
12604
state.avgAllocSize = 0;
12605
12606
for (size_t i = 0; i < vector.GetBlockCount(); ++i)
12607
{
12608
VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
12609
12610
allocCount += metadata->GetAllocationCount();
12611
freeCount += metadata->GetFreeRegionsCount();
12612
state.avgFreeSize += metadata->GetSumFreeSize();
12613
state.avgAllocSize += metadata->GetSize();
12614
}
12615
12616
state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;
12617
state.avgFreeSize /= freeCount;
12618
}
12619
12620
bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,
12621
VmaBlockVector& vector, size_t firstFreeBlock,
12622
bool& texturePresent, bool& bufferPresent, bool& otherPresent)
12623
{
12624
const size_t prevMoveCount = m_Moves.size();
12625
for (size_t i = firstFreeBlock ; i;)
12626
{
12627
VmaDeviceMemoryBlock* block = vector.GetBlock(--i);
12628
VmaBlockMetadata* metadata = block->m_pMetadata;
12629
12630
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
12631
handle != VK_NULL_HANDLE;
12632
handle = metadata->GetNextAllocation(handle))
12633
{
12634
MoveAllocationData moveData = GetMoveData(handle, metadata);
12635
// Ignore newly created allocations by defragmentation algorithm
12636
if (moveData.move.srcAllocation->GetUserData() == this)
12637
continue;
12638
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
12639
{
12640
case CounterStatus::Ignore:
12641
continue;
12642
case CounterStatus::End:
12643
return true;
12644
case CounterStatus::Pass:
12645
break;
12646
default:
12647
VMA_ASSERT(0);
12648
}
12649
12650
// Move only single type of resources at once
12651
if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))
12652
{
12653
// Try to fit allocation into free blocks
12654
if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))
12655
return false;
12656
}
12657
12658
if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))
12659
texturePresent = true;
12660
else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))
12661
bufferPresent = true;
12662
else
12663
otherPresent = true;
12664
}
12665
}
12666
return prevMoveCount == m_Moves.size();
12667
}
12668
#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
12669
12670
#ifndef _VMA_POOL_T_FUNCTIONS
12671
VmaPool_T::VmaPool_T(
12672
VmaAllocator hAllocator,
12673
const VmaPoolCreateInfo& createInfo,
12674
VkDeviceSize preferredBlockSize)
12675
: m_BlockVector(
12676
hAllocator,
12677
this, // hParentPool
12678
createInfo.memoryTypeIndex,
12679
createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12680
createInfo.minBlockCount,
12681
createInfo.maxBlockCount,
12682
(createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12683
createInfo.blockSize != 0, // explicitBlockSize
12684
createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
12685
createInfo.priority,
12686
VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
12687
createInfo.pMemoryAllocateNext),
12688
m_Id(0),
12689
m_Name(VMA_NULL) {}
12690
12691
VmaPool_T::~VmaPool_T()
12692
{
12693
VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
12694
12695
const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12696
VmaFreeString(allocs, m_Name);
12697
}
12698
12699
void VmaPool_T::SetName(const char* pName)
12700
{
12701
const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12702
VmaFreeString(allocs, m_Name);
12703
12704
if (pName != VMA_NULL)
12705
{
12706
m_Name = VmaCreateStringCopy(allocs, pName);
12707
}
12708
else
12709
{
12710
m_Name = VMA_NULL;
12711
}
12712
}
12713
#endif // _VMA_POOL_T_FUNCTIONS
12714
12715
#ifndef _VMA_ALLOCATOR_T_FUNCTIONS
12716
VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
12717
m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
12718
m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
12719
m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
12720
m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
12721
m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
12722
m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
12723
m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
12724
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
12725
m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),
12726
m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),
12727
m_hDevice(pCreateInfo->device),
12728
m_hInstance(pCreateInfo->instance),
12729
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
12730
m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
12731
*pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
12732
m_AllocationObjectAllocator(&m_AllocationCallbacks),
12733
m_HeapSizeLimitMask(0),
12734
m_DeviceMemoryCount(0),
12735
m_PreferredLargeHeapBlockSize(0),
12736
m_PhysicalDevice(pCreateInfo->physicalDevice),
12737
m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
12738
m_NextPoolId(0),
12739
m_GlobalMemoryTypeBits(UINT32_MAX)
12740
{
12741
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
12742
{
12743
m_UseKhrDedicatedAllocation = false;
12744
m_UseKhrBindMemory2 = false;
12745
}
12746
12747
if(VMA_DEBUG_DETECT_CORRUPTION)
12748
{
12749
// Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
12750
VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
12751
}
12752
12753
VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
12754
12755
if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
12756
{
12757
#if !(VMA_DEDICATED_ALLOCATION)
12758
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
12759
{
12760
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
12761
}
12762
#endif
12763
#if !(VMA_BIND_MEMORY2)
12764
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
12765
{
12766
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
12767
}
12768
#endif
12769
}
12770
#if !(VMA_MEMORY_BUDGET)
12771
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
12772
{
12773
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
12774
}
12775
#endif
12776
#if !(VMA_BUFFER_DEVICE_ADDRESS)
12777
if(m_UseKhrBufferDeviceAddress)
12778
{
12779
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.");
12780
}
12781
#endif
12782
#if VMA_VULKAN_VERSION < 1003000
12783
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
12784
{
12785
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros.");
12786
}
12787
#endif
12788
#if VMA_VULKAN_VERSION < 1002000
12789
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
12790
{
12791
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
12792
}
12793
#endif
12794
#if VMA_VULKAN_VERSION < 1001000
12795
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
12796
{
12797
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
12798
}
12799
#endif
12800
#if !(VMA_MEMORY_PRIORITY)
12801
if(m_UseExtMemoryPriority)
12802
{
12803
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.");
12804
}
12805
#endif
12806
#if !(VMA_KHR_MAINTENANCE4)
12807
if(m_UseKhrMaintenance4)
12808
{
12809
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_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.");
12810
}
12811
#endif
12812
#if !(VMA_KHR_MAINTENANCE5)
12813
if(m_UseKhrMaintenance5)
12814
{
12815
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
12816
}
12817
#endif
12818
12819
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
12820
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
12821
memset(&m_MemProps, 0, sizeof(m_MemProps));
12822
12823
memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
12824
memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
12825
12826
#if VMA_EXTERNAL_MEMORY
12827
memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
12828
#endif // #if VMA_EXTERNAL_MEMORY
12829
12830
if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
12831
{
12832
m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
12833
m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
12834
m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
12835
}
12836
12837
ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
12838
12839
(*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
12840
(*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
12841
12842
VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
12843
VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
12844
VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
12845
VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
12846
12847
m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
12848
pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
12849
12850
m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
12851
12852
#if VMA_EXTERNAL_MEMORY
12853
if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
12854
{
12855
memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
12856
sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
12857
}
12858
#endif // #if VMA_EXTERNAL_MEMORY
12859
12860
if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
12861
{
12862
for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
12863
{
12864
const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
12865
if(limit != VK_WHOLE_SIZE)
12866
{
12867
m_HeapSizeLimitMask |= 1u << heapIndex;
12868
if(limit < m_MemProps.memoryHeaps[heapIndex].size)
12869
{
12870
m_MemProps.memoryHeaps[heapIndex].size = limit;
12871
}
12872
}
12873
}
12874
}
12875
12876
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
12877
{
12878
// Create only supported types
12879
if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
12880
{
12881
const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
12882
m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
12883
this,
12884
VK_NULL_HANDLE, // hParentPool
12885
memTypeIndex,
12886
preferredBlockSize,
12887
0,
12888
SIZE_MAX,
12889
GetBufferImageGranularity(),
12890
false, // explicitBlockSize
12891
0, // algorithm
12892
0.5f, // priority (0.5 is the default per Vulkan spec)
12893
GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment
12894
VMA_NULL); // // pMemoryAllocateNext
12895
// No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
12896
// because minBlockCount is 0.
12897
}
12898
}
12899
}
12900
12901
VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
12902
{
12903
VkResult res = VK_SUCCESS;
12904
12905
#if VMA_MEMORY_BUDGET
12906
if(m_UseExtMemoryBudget)
12907
{
12908
UpdateVulkanBudget();
12909
}
12910
#endif // #if VMA_MEMORY_BUDGET
12911
12912
return res;
12913
}
12914
12915
VmaAllocator_T::~VmaAllocator_T()
12916
{
12917
VMA_ASSERT(m_Pools.IsEmpty());
12918
12919
for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
12920
{
12921
vma_delete(this, m_pBlockVectors[memTypeIndex]);
12922
}
12923
}
12924
12925
void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
12926
{
12927
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
12928
ImportVulkanFunctions_Static();
12929
#endif
12930
12931
if(pVulkanFunctions != VMA_NULL)
12932
{
12933
ImportVulkanFunctions_Custom(pVulkanFunctions);
12934
}
12935
12936
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
12937
ImportVulkanFunctions_Dynamic();
12938
#endif
12939
12940
ValidateVulkanFunctions();
12941
}
12942
12943
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
12944
12945
void VmaAllocator_T::ImportVulkanFunctions_Static()
12946
{
12947
// Vulkan 1.0
12948
m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;
12949
m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;
12950
m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
12951
m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
12952
m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
12953
m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
12954
m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
12955
m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
12956
m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
12957
m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
12958
m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
12959
m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
12960
m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
12961
m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
12962
m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
12963
m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
12964
m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
12965
m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
12966
m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
12967
12968
// Vulkan 1.1
12969
#if VMA_VULKAN_VERSION >= 1001000
12970
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
12971
{
12972
m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
12973
m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
12974
m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
12975
m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
12976
}
12977
#endif
12978
12979
#if VMA_VULKAN_VERSION >= 1001000
12980
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
12981
{
12982
m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
12983
}
12984
#endif
12985
12986
#if VMA_VULKAN_VERSION >= 1003000
12987
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
12988
{
12989
m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;
12990
m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;
12991
}
12992
#endif
12993
}
12994
12995
#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1
12996
12997
void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
12998
{
12999
VMA_ASSERT(pVulkanFunctions != VMA_NULL);
13000
13001
#define VMA_COPY_IF_NOT_NULL(funcName) \
13002
if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
13003
13004
VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);
13005
VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);
13006
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
13007
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
13008
VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
13009
VMA_COPY_IF_NOT_NULL(vkFreeMemory);
13010
VMA_COPY_IF_NOT_NULL(vkMapMemory);
13011
VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
13012
VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
13013
VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
13014
VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
13015
VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
13016
VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
13017
VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
13018
VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
13019
VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
13020
VMA_COPY_IF_NOT_NULL(vkCreateImage);
13021
VMA_COPY_IF_NOT_NULL(vkDestroyImage);
13022
VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
13023
13024
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13025
VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
13026
VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
13027
#endif
13028
13029
#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
13030
VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
13031
VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
13032
#endif
13033
13034
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
13035
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
13036
#endif
13037
13038
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
13039
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
13040
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
13041
#endif
13042
13043
#undef VMA_COPY_IF_NOT_NULL
13044
}
13045
13046
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
13047
13048
void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
13049
{
13050
VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&
13051
"To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "
13052
"VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "
13053
"Other members can be null.");
13054
13055
#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
13056
if(m_VulkanFunctions.memberName == VMA_NULL) \
13057
m_VulkanFunctions.memberName = \
13058
(functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString);
13059
#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
13060
if(m_VulkanFunctions.memberName == VMA_NULL) \
13061
m_VulkanFunctions.memberName = \
13062
(functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString);
13063
13064
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
13065
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
13066
VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
13067
VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
13068
VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
13069
VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
13070
VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
13071
VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
13072
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
13073
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
13074
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
13075
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
13076
VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
13077
VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
13078
VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
13079
VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
13080
VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
13081
13082
#if VMA_VULKAN_VERSION >= 1001000
13083
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13084
{
13085
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
13086
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
13087
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
13088
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
13089
}
13090
#endif
13091
13092
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
13093
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13094
{
13095
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");
13096
}
13097
else if(m_UseExtMemoryBudget)
13098
{
13099
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
13100
}
13101
#endif
13102
13103
#if VMA_DEDICATED_ALLOCATION
13104
if(m_UseKhrDedicatedAllocation)
13105
{
13106
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
13107
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
13108
}
13109
#endif
13110
13111
#if VMA_BIND_MEMORY2
13112
if(m_UseKhrBindMemory2)
13113
{
13114
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
13115
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
13116
}
13117
#endif // #if VMA_BIND_MEMORY2
13118
13119
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
13120
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13121
{
13122
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");
13123
}
13124
else if(m_UseExtMemoryBudget)
13125
{
13126
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
13127
}
13128
#endif // #if VMA_MEMORY_BUDGET
13129
13130
#if VMA_VULKAN_VERSION >= 1003000
13131
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
13132
{
13133
VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");
13134
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");
13135
}
13136
#endif
13137
#if VMA_KHR_MAINTENANCE4
13138
if(m_UseKhrMaintenance4)
13139
{
13140
VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirementsKHR, "vkGetDeviceBufferMemoryRequirementsKHR");
13141
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");
13142
}
13143
#endif
13144
13145
#undef VMA_FETCH_DEVICE_FUNC
13146
#undef VMA_FETCH_INSTANCE_FUNC
13147
}
13148
13149
#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
13150
13151
void VmaAllocator_T::ValidateVulkanFunctions()
13152
{
13153
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
13154
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
13155
VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
13156
VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
13157
VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
13158
VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
13159
VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
13160
VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
13161
VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
13162
VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
13163
VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
13164
VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
13165
VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
13166
VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
13167
VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
13168
VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
13169
VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
13170
13171
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13172
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
13173
{
13174
VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
13175
VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
13176
}
13177
#endif
13178
13179
#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
13180
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
13181
{
13182
VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
13183
VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
13184
}
13185
#endif
13186
13187
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
13188
if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13189
{
13190
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
13191
}
13192
#endif
13193
13194
// Not validating these due to suspected driver bugs with these function
13195
// pointers being null despite correct extension or Vulkan version is enabled.
13196
// See issue #397. Their usage in VMA is optional anyway.
13197
//
13198
// VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);
13199
// VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);
13200
}
13201
13202
VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
13203
{
13204
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
13205
const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
13206
const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
13207
return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
13208
}
13209
13210
VkResult VmaAllocator_T::AllocateMemoryOfType(
13211
VmaPool pool,
13212
VkDeviceSize size,
13213
VkDeviceSize alignment,
13214
bool dedicatedPreferred,
13215
VkBuffer dedicatedBuffer,
13216
VkImage dedicatedImage,
13217
VmaBufferImageUsage dedicatedBufferImageUsage,
13218
const VmaAllocationCreateInfo& createInfo,
13219
uint32_t memTypeIndex,
13220
VmaSuballocationType suballocType,
13221
VmaDedicatedAllocationList& dedicatedAllocations,
13222
VmaBlockVector& blockVector,
13223
size_t allocationCount,
13224
VmaAllocation* pAllocations)
13225
{
13226
VMA_ASSERT(pAllocations != VMA_NULL);
13227
VMA_DEBUG_LOG_FORMAT(" AllocateMemory: MemoryTypeIndex=%" PRIu32 ", AllocationCount=%zu, Size=%" PRIu64, memTypeIndex, allocationCount, size);
13228
13229
VmaAllocationCreateInfo finalCreateInfo = createInfo;
13230
VkResult res = CalcMemTypeParams(
13231
finalCreateInfo,
13232
memTypeIndex,
13233
size,
13234
allocationCount);
13235
if(res != VK_SUCCESS)
13236
return res;
13237
13238
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
13239
{
13240
return AllocateDedicatedMemory(
13241
pool,
13242
size,
13243
suballocType,
13244
dedicatedAllocations,
13245
memTypeIndex,
13246
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
13247
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
13248
(finalCreateInfo.flags &
13249
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
13250
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
13251
finalCreateInfo.pUserData,
13252
finalCreateInfo.priority,
13253
dedicatedBuffer,
13254
dedicatedImage,
13255
dedicatedBufferImageUsage,
13256
allocationCount,
13257
pAllocations,
13258
blockVector.GetAllocationNextPtr());
13259
}
13260
else
13261
{
13262
const bool canAllocateDedicated =
13263
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
13264
(pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());
13265
13266
if(canAllocateDedicated)
13267
{
13268
// Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
13269
if(size > blockVector.GetPreferredBlockSize() / 2)
13270
{
13271
dedicatedPreferred = true;
13272
}
13273
// Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
13274
// which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above
13275
// 3/4 of the maximum allocation count.
13276
if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 &&
13277
m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
13278
{
13279
dedicatedPreferred = false;
13280
}
13281
13282
if(dedicatedPreferred)
13283
{
13284
res = AllocateDedicatedMemory(
13285
pool,
13286
size,
13287
suballocType,
13288
dedicatedAllocations,
13289
memTypeIndex,
13290
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
13291
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
13292
(finalCreateInfo.flags &
13293
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
13294
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
13295
finalCreateInfo.pUserData,
13296
finalCreateInfo.priority,
13297
dedicatedBuffer,
13298
dedicatedImage,
13299
dedicatedBufferImageUsage,
13300
allocationCount,
13301
pAllocations,
13302
blockVector.GetAllocationNextPtr());
13303
if(res == VK_SUCCESS)
13304
{
13305
// Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.
13306
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
13307
return VK_SUCCESS;
13308
}
13309
}
13310
}
13311
13312
res = blockVector.Allocate(
13313
size,
13314
alignment,
13315
finalCreateInfo,
13316
suballocType,
13317
allocationCount,
13318
pAllocations);
13319
if(res == VK_SUCCESS)
13320
return VK_SUCCESS;
13321
13322
// Try dedicated memory.
13323
if(canAllocateDedicated && !dedicatedPreferred)
13324
{
13325
res = AllocateDedicatedMemory(
13326
pool,
13327
size,
13328
suballocType,
13329
dedicatedAllocations,
13330
memTypeIndex,
13331
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
13332
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
13333
(finalCreateInfo.flags &
13334
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
13335
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
13336
finalCreateInfo.pUserData,
13337
finalCreateInfo.priority,
13338
dedicatedBuffer,
13339
dedicatedImage,
13340
dedicatedBufferImageUsage,
13341
allocationCount,
13342
pAllocations,
13343
blockVector.GetAllocationNextPtr());
13344
if(res == VK_SUCCESS)
13345
{
13346
// Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.
13347
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
13348
return VK_SUCCESS;
13349
}
13350
}
13351
// Everything failed: Return error code.
13352
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
13353
return res;
13354
}
13355
}
13356
13357
VkResult VmaAllocator_T::AllocateDedicatedMemory(
13358
VmaPool pool,
13359
VkDeviceSize size,
13360
VmaSuballocationType suballocType,
13361
VmaDedicatedAllocationList& dedicatedAllocations,
13362
uint32_t memTypeIndex,
13363
bool map,
13364
bool isUserDataString,
13365
bool isMappingAllowed,
13366
bool canAliasMemory,
13367
void* pUserData,
13368
float priority,
13369
VkBuffer dedicatedBuffer,
13370
VkImage dedicatedImage,
13371
VmaBufferImageUsage dedicatedBufferImageUsage,
13372
size_t allocationCount,
13373
VmaAllocation* pAllocations,
13374
const void* pNextChain)
13375
{
13376
VMA_ASSERT(allocationCount > 0 && pAllocations);
13377
13378
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13379
allocInfo.memoryTypeIndex = memTypeIndex;
13380
allocInfo.allocationSize = size;
13381
allocInfo.pNext = pNextChain;
13382
13383
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13384
VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
13385
if(!canAliasMemory)
13386
{
13387
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13388
{
13389
if(dedicatedBuffer != VK_NULL_HANDLE)
13390
{
13391
VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
13392
dedicatedAllocInfo.buffer = dedicatedBuffer;
13393
VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
13394
}
13395
else if(dedicatedImage != VK_NULL_HANDLE)
13396
{
13397
dedicatedAllocInfo.image = dedicatedImage;
13398
VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
13399
}
13400
}
13401
}
13402
#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13403
13404
#if VMA_BUFFER_DEVICE_ADDRESS
13405
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13406
if(m_UseKhrBufferDeviceAddress)
13407
{
13408
bool canContainBufferWithDeviceAddress = true;
13409
if(dedicatedBuffer != VK_NULL_HANDLE)
13410
{
13411
canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == VmaBufferImageUsage::UNKNOWN ||
13412
dedicatedBufferImageUsage.Contains(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT);
13413
}
13414
else if(dedicatedImage != VK_NULL_HANDLE)
13415
{
13416
canContainBufferWithDeviceAddress = false;
13417
}
13418
if(canContainBufferWithDeviceAddress)
13419
{
13420
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13421
VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13422
}
13423
}
13424
#endif // #if VMA_BUFFER_DEVICE_ADDRESS
13425
13426
#if VMA_MEMORY_PRIORITY
13427
VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
13428
if(m_UseExtMemoryPriority)
13429
{
13430
VMA_ASSERT(priority >= 0.f && priority <= 1.f);
13431
priorityInfo.priority = priority;
13432
VmaPnextChainPushFront(&allocInfo, &priorityInfo);
13433
}
13434
#endif // #if VMA_MEMORY_PRIORITY
13435
13436
#if VMA_EXTERNAL_MEMORY
13437
// Attach VkExportMemoryAllocateInfoKHR if necessary.
13438
VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
13439
exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
13440
if(exportMemoryAllocInfo.handleTypes != 0)
13441
{
13442
VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
13443
}
13444
#endif // #if VMA_EXTERNAL_MEMORY
13445
13446
size_t allocIndex;
13447
VkResult res = VK_SUCCESS;
13448
for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13449
{
13450
res = AllocateDedicatedMemoryPage(
13451
pool,
13452
size,
13453
suballocType,
13454
memTypeIndex,
13455
allocInfo,
13456
map,
13457
isUserDataString,
13458
isMappingAllowed,
13459
pUserData,
13460
pAllocations + allocIndex);
13461
if(res != VK_SUCCESS)
13462
{
13463
break;
13464
}
13465
}
13466
13467
if(res == VK_SUCCESS)
13468
{
13469
for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13470
{
13471
dedicatedAllocations.Register(pAllocations[allocIndex]);
13472
}
13473
VMA_DEBUG_LOG_FORMAT(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%" PRIu32, allocationCount, memTypeIndex);
13474
}
13475
else
13476
{
13477
// Free all already created allocations.
13478
while(allocIndex--)
13479
{
13480
VmaAllocation currAlloc = pAllocations[allocIndex];
13481
VkDeviceMemory hMemory = currAlloc->GetMemory();
13482
13483
/*
13484
There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
13485
before vkFreeMemory.
13486
13487
if(currAlloc->GetMappedData() != VMA_NULL)
13488
{
13489
(*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
13490
}
13491
*/
13492
13493
FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
13494
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
13495
m_AllocationObjectAllocator.Free(currAlloc);
13496
}
13497
13498
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
13499
}
13500
13501
return res;
13502
}
13503
13504
VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
13505
VmaPool pool,
13506
VkDeviceSize size,
13507
VmaSuballocationType suballocType,
13508
uint32_t memTypeIndex,
13509
const VkMemoryAllocateInfo& allocInfo,
13510
bool map,
13511
bool isUserDataString,
13512
bool isMappingAllowed,
13513
void* pUserData,
13514
VmaAllocation* pAllocation)
13515
{
13516
VkDeviceMemory hMemory = VK_NULL_HANDLE;
13517
VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
13518
if(res < 0)
13519
{
13520
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
13521
return res;
13522
}
13523
13524
void* pMappedData = VMA_NULL;
13525
if(map)
13526
{
13527
res = (*m_VulkanFunctions.vkMapMemory)(
13528
m_hDevice,
13529
hMemory,
13530
0,
13531
VK_WHOLE_SIZE,
13532
0,
13533
&pMappedData);
13534
if(res < 0)
13535
{
13536
VMA_DEBUG_LOG(" vkMapMemory FAILED");
13537
FreeVulkanMemory(memTypeIndex, size, hMemory);
13538
return res;
13539
}
13540
}
13541
13542
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
13543
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
13544
if (isUserDataString)
13545
(*pAllocation)->SetName(this, (const char*)pUserData);
13546
else
13547
(*pAllocation)->SetUserData(this, pUserData);
13548
m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
13549
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13550
{
13551
FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13552
}
13553
13554
return VK_SUCCESS;
13555
}
13556
13557
void VmaAllocator_T::GetBufferMemoryRequirements(
13558
VkBuffer hBuffer,
13559
VkMemoryRequirements& memReq,
13560
bool& requiresDedicatedAllocation,
13561
bool& prefersDedicatedAllocation) const
13562
{
13563
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13564
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13565
{
13566
VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
13567
memReqInfo.buffer = hBuffer;
13568
13569
VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
13570
13571
VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
13572
VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
13573
13574
(*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
13575
13576
memReq = memReq2.memoryRequirements;
13577
requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
13578
prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
13579
}
13580
else
13581
#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13582
{
13583
(*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
13584
requiresDedicatedAllocation = false;
13585
prefersDedicatedAllocation = false;
13586
}
13587
}
13588
13589
void VmaAllocator_T::GetImageMemoryRequirements(
13590
VkImage hImage,
13591
VkMemoryRequirements& memReq,
13592
bool& requiresDedicatedAllocation,
13593
bool& prefersDedicatedAllocation) const
13594
{
13595
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13596
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
13597
{
13598
VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
13599
memReqInfo.image = hImage;
13600
13601
VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
13602
13603
VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
13604
VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
13605
13606
(*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
13607
13608
memReq = memReq2.memoryRequirements;
13609
requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
13610
prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
13611
}
13612
else
13613
#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
13614
{
13615
(*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
13616
requiresDedicatedAllocation = false;
13617
prefersDedicatedAllocation = false;
13618
}
13619
}
13620
13621
VkResult VmaAllocator_T::FindMemoryTypeIndex(
13622
uint32_t memoryTypeBits,
13623
const VmaAllocationCreateInfo* pAllocationCreateInfo,
13624
VmaBufferImageUsage bufImgUsage,
13625
uint32_t* pMemoryTypeIndex) const
13626
{
13627
memoryTypeBits &= GetGlobalMemoryTypeBits();
13628
13629
if(pAllocationCreateInfo->memoryTypeBits != 0)
13630
{
13631
memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
13632
}
13633
13634
VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0;
13635
if(!FindMemoryPreferences(
13636
IsIntegratedGpu(),
13637
*pAllocationCreateInfo,
13638
bufImgUsage,
13639
requiredFlags, preferredFlags, notPreferredFlags))
13640
{
13641
return VK_ERROR_FEATURE_NOT_PRESENT;
13642
}
13643
13644
*pMemoryTypeIndex = UINT32_MAX;
13645
uint32_t minCost = UINT32_MAX;
13646
for(uint32_t memTypeIndex = 0, memTypeBit = 1;
13647
memTypeIndex < GetMemoryTypeCount();
13648
++memTypeIndex, memTypeBit <<= 1)
13649
{
13650
// This memory type is acceptable according to memoryTypeBits bitmask.
13651
if((memTypeBit & memoryTypeBits) != 0)
13652
{
13653
const VkMemoryPropertyFlags currFlags =
13654
m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
13655
// This memory type contains requiredFlags.
13656
if((requiredFlags & ~currFlags) == 0)
13657
{
13658
// Calculate cost as number of bits from preferredFlags not present in this memory type.
13659
uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) +
13660
VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);
13661
// Remember memory type with lowest cost.
13662
if(currCost < minCost)
13663
{
13664
*pMemoryTypeIndex = memTypeIndex;
13665
if(currCost == 0)
13666
{
13667
return VK_SUCCESS;
13668
}
13669
minCost = currCost;
13670
}
13671
}
13672
}
13673
}
13674
return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
13675
}
13676
13677
VkResult VmaAllocator_T::CalcMemTypeParams(
13678
VmaAllocationCreateInfo& inoutCreateInfo,
13679
uint32_t memTypeIndex,
13680
VkDeviceSize size,
13681
size_t allocationCount)
13682
{
13683
// If memory type is not HOST_VISIBLE, disable MAPPED.
13684
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
13685
(m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
13686
{
13687
inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
13688
}
13689
13690
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
13691
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)
13692
{
13693
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
13694
VmaBudget heapBudget = {};
13695
GetHeapBudgets(&heapBudget, heapIndex, 1);
13696
if(heapBudget.usage + size * allocationCount > heapBudget.budget)
13697
{
13698
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13699
}
13700
}
13701
return VK_SUCCESS;
13702
}
13703
13704
VkResult VmaAllocator_T::CalcAllocationParams(
13705
VmaAllocationCreateInfo& inoutCreateInfo,
13706
bool dedicatedRequired,
13707
bool dedicatedPreferred)
13708
{
13709
VMA_ASSERT((inoutCreateInfo.flags &
13710
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=
13711
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&
13712
"Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");
13713
VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||
13714
(inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&
13715
"Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
13716
if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
13717
{
13718
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)
13719
{
13720
VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&
13721
"When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
13722
}
13723
}
13724
13725
// If memory is lazily allocated, it should be always dedicated.
13726
if(dedicatedRequired ||
13727
inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
13728
{
13729
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
13730
}
13731
13732
if(inoutCreateInfo.pool != VK_NULL_HANDLE)
13733
{
13734
if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
13735
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
13736
{
13737
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
13738
return VK_ERROR_FEATURE_NOT_PRESENT;
13739
}
13740
inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
13741
}
13742
13743
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
13744
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
13745
{
13746
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
13747
return VK_ERROR_FEATURE_NOT_PRESENT;
13748
}
13749
13750
if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&
13751
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
13752
{
13753
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
13754
}
13755
13756
// Non-auto USAGE values imply HOST_ACCESS flags.
13757
// And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools.
13758
// Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*.
13759
// Otherwise they just protect from assert on mapping.
13760
if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&
13761
inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&
13762
inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
13763
{
13764
if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)
13765
{
13766
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
13767
}
13768
}
13769
13770
return VK_SUCCESS;
13771
}
13772
13773
VkResult VmaAllocator_T::AllocateMemory(
13774
const VkMemoryRequirements& vkMemReq,
13775
bool requiresDedicatedAllocation,
13776
bool prefersDedicatedAllocation,
13777
VkBuffer dedicatedBuffer,
13778
VkImage dedicatedImage,
13779
VmaBufferImageUsage dedicatedBufferImageUsage,
13780
const VmaAllocationCreateInfo& createInfo,
13781
VmaSuballocationType suballocType,
13782
size_t allocationCount,
13783
VmaAllocation* pAllocations)
13784
{
13785
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
13786
13787
VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
13788
13789
if(vkMemReq.size == 0)
13790
{
13791
return VK_ERROR_INITIALIZATION_FAILED;
13792
}
13793
13794
VmaAllocationCreateInfo createInfoFinal = createInfo;
13795
VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);
13796
if(res != VK_SUCCESS)
13797
return res;
13798
13799
if(createInfoFinal.pool != VK_NULL_HANDLE)
13800
{
13801
VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
13802
return AllocateMemoryOfType(
13803
createInfoFinal.pool,
13804
vkMemReq.size,
13805
vkMemReq.alignment,
13806
prefersDedicatedAllocation,
13807
dedicatedBuffer,
13808
dedicatedImage,
13809
dedicatedBufferImageUsage,
13810
createInfoFinal,
13811
blockVector.GetMemoryTypeIndex(),
13812
suballocType,
13813
createInfoFinal.pool->m_DedicatedAllocations,
13814
blockVector,
13815
allocationCount,
13816
pAllocations);
13817
}
13818
else
13819
{
13820
// Bit mask of memory Vulkan types acceptable for this allocation.
13821
uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
13822
uint32_t memTypeIndex = UINT32_MAX;
13823
res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
13824
// Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
13825
if(res != VK_SUCCESS)
13826
return res;
13827
do
13828
{
13829
VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
13830
VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
13831
res = AllocateMemoryOfType(
13832
VK_NULL_HANDLE,
13833
vkMemReq.size,
13834
vkMemReq.alignment,
13835
requiresDedicatedAllocation || prefersDedicatedAllocation,
13836
dedicatedBuffer,
13837
dedicatedImage,
13838
dedicatedBufferImageUsage,
13839
createInfoFinal,
13840
memTypeIndex,
13841
suballocType,
13842
m_DedicatedAllocations[memTypeIndex],
13843
*blockVector,
13844
allocationCount,
13845
pAllocations);
13846
// Allocation succeeded
13847
if(res == VK_SUCCESS)
13848
return VK_SUCCESS;
13849
13850
// Remove old memTypeIndex from list of possibilities.
13851
memoryTypeBits &= ~(1u << memTypeIndex);
13852
// Find alternative memTypeIndex.
13853
res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
13854
} while(res == VK_SUCCESS);
13855
13856
// No other matching memory type index could be found.
13857
// Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
13858
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13859
}
13860
}
13861
13862
void VmaAllocator_T::FreeMemory(
13863
size_t allocationCount,
13864
const VmaAllocation* pAllocations)
13865
{
13866
VMA_ASSERT(pAllocations);
13867
13868
for(size_t allocIndex = allocationCount; allocIndex--; )
13869
{
13870
VmaAllocation allocation = pAllocations[allocIndex];
13871
13872
if(allocation != VK_NULL_HANDLE)
13873
{
13874
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13875
{
13876
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
13877
}
13878
13879
allocation->FreeName(this);
13880
13881
switch(allocation->GetType())
13882
{
13883
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
13884
{
13885
VmaBlockVector* pBlockVector = VMA_NULL;
13886
VmaPool hPool = allocation->GetParentPool();
13887
if(hPool != VK_NULL_HANDLE)
13888
{
13889
pBlockVector = &hPool->m_BlockVector;
13890
}
13891
else
13892
{
13893
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
13894
pBlockVector = m_pBlockVectors[memTypeIndex];
13895
VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
13896
}
13897
pBlockVector->Free(allocation);
13898
}
13899
break;
13900
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
13901
FreeDedicatedMemory(allocation);
13902
break;
13903
default:
13904
VMA_ASSERT(0);
13905
}
13906
}
13907
}
13908
}
13909
13910
void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)
13911
{
13912
// Initialize.
13913
VmaClearDetailedStatistics(pStats->total);
13914
for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
13915
VmaClearDetailedStatistics(pStats->memoryType[i]);
13916
for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
13917
VmaClearDetailedStatistics(pStats->memoryHeap[i]);
13918
13919
// Process default pools.
13920
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
13921
{
13922
VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
13923
if (pBlockVector != VMA_NULL)
13924
pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
13925
}
13926
13927
// Process custom pools.
13928
{
13929
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
13930
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
13931
{
13932
VmaBlockVector& blockVector = pool->m_BlockVector;
13933
const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
13934
blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
13935
pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
13936
}
13937
}
13938
13939
// Process dedicated allocations.
13940
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
13941
{
13942
m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
13943
}
13944
13945
// Sum from memory types to memory heaps.
13946
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
13947
{
13948
const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
13949
VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);
13950
}
13951
13952
// Sum from memory heaps to total.
13953
for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)
13954
VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);
13955
13956
VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||
13957
pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);
13958
VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||
13959
pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);
13960
}
13961
13962
void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)
13963
{
13964
#if VMA_MEMORY_BUDGET
13965
if(m_UseExtMemoryBudget)
13966
{
13967
if(m_Budget.m_OperationsSinceBudgetFetch < 30)
13968
{
13969
VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
13970
for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
13971
{
13972
const uint32_t heapIndex = firstHeap + i;
13973
13974
outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
13975
outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
13976
outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
13977
outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
13978
13979
if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
13980
{
13981
outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] +
13982
outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
13983
}
13984
else
13985
{
13986
outBudgets->usage = 0;
13987
}
13988
13989
// Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
13990
outBudgets->budget = VMA_MIN(
13991
m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
13992
}
13993
}
13994
else
13995
{
13996
UpdateVulkanBudget(); // Outside of mutex lock
13997
GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion
13998
}
13999
}
14000
else
14001
#endif
14002
{
14003
for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
14004
{
14005
const uint32_t heapIndex = firstHeap + i;
14006
14007
outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
14008
outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
14009
outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
14010
outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
14011
14012
outBudgets->usage = outBudgets->statistics.blockBytes;
14013
outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
14014
}
14015
}
14016
}
14017
14018
void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14019
{
14020
pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14021
pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14022
pAllocationInfo->offset = hAllocation->GetOffset();
14023
pAllocationInfo->size = hAllocation->GetSize();
14024
pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14025
pAllocationInfo->pUserData = hAllocation->GetUserData();
14026
pAllocationInfo->pName = hAllocation->GetName();
14027
}
14028
14029
void VmaAllocator_T::GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo)
14030
{
14031
GetAllocationInfo(hAllocation, &pAllocationInfo->allocationInfo);
14032
14033
switch (hAllocation->GetType())
14034
{
14035
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14036
pAllocationInfo->blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
14037
pAllocationInfo->dedicatedMemory = VK_FALSE;
14038
break;
14039
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14040
pAllocationInfo->blockSize = pAllocationInfo->allocationInfo.size;
14041
pAllocationInfo->dedicatedMemory = VK_TRUE;
14042
break;
14043
default:
14044
VMA_ASSERT(0);
14045
}
14046
}
14047
14048
VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14049
{
14050
VMA_DEBUG_LOG_FORMAT(" CreatePool: MemoryTypeIndex=%" PRIu32 ", flags=%" PRIu32, pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
14051
14052
VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
14053
14054
// Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.
14055
if(pCreateInfo->pMemoryAllocateNext)
14056
{
14057
VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
14058
}
14059
14060
if(newCreateInfo.maxBlockCount == 0)
14061
{
14062
newCreateInfo.maxBlockCount = SIZE_MAX;
14063
}
14064
if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
14065
{
14066
return VK_ERROR_INITIALIZATION_FAILED;
14067
}
14068
// Memory type index out of range or forbidden.
14069
if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
14070
((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
14071
{
14072
return VK_ERROR_FEATURE_NOT_PRESENT;
14073
}
14074
if(newCreateInfo.minAllocationAlignment > 0)
14075
{
14076
VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
14077
}
14078
14079
const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
14080
14081
*pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
14082
14083
VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
14084
if(res != VK_SUCCESS)
14085
{
14086
vma_delete(this, *pPool);
14087
*pPool = VMA_NULL;
14088
return res;
14089
}
14090
14091
// Add to m_Pools.
14092
{
14093
VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
14094
(*pPool)->SetId(m_NextPoolId++);
14095
m_Pools.PushBack(*pPool);
14096
}
14097
14098
return VK_SUCCESS;
14099
}
14100
14101
void VmaAllocator_T::DestroyPool(VmaPool pool)
14102
{
14103
// Remove from m_Pools.
14104
{
14105
VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
14106
m_Pools.Remove(pool);
14107
}
14108
14109
vma_delete(this, pool);
14110
}
14111
14112
void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)
14113
{
14114
VmaClearStatistics(*pPoolStats);
14115
pool->m_BlockVector.AddStatistics(*pPoolStats);
14116
pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);
14117
}
14118
14119
void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)
14120
{
14121
VmaClearDetailedStatistics(*pPoolStats);
14122
pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);
14123
pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);
14124
}
14125
14126
void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
14127
{
14128
m_CurrentFrameIndex.store(frameIndex);
14129
14130
#if VMA_MEMORY_BUDGET
14131
if(m_UseExtMemoryBudget)
14132
{
14133
UpdateVulkanBudget();
14134
}
14135
#endif // #if VMA_MEMORY_BUDGET
14136
}
14137
14138
VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
14139
{
14140
return hPool->m_BlockVector.CheckCorruption();
14141
}
14142
14143
VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
14144
{
14145
VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
14146
14147
// Process default pools.
14148
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14149
{
14150
VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14151
if(pBlockVector != VMA_NULL)
14152
{
14153
VkResult localRes = pBlockVector->CheckCorruption();
14154
switch(localRes)
14155
{
14156
case VK_ERROR_FEATURE_NOT_PRESENT:
14157
break;
14158
case VK_SUCCESS:
14159
finalRes = VK_SUCCESS;
14160
break;
14161
default:
14162
return localRes;
14163
}
14164
}
14165
}
14166
14167
// Process custom pools.
14168
{
14169
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14170
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
14171
{
14172
if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
14173
{
14174
VkResult localRes = pool->m_BlockVector.CheckCorruption();
14175
switch(localRes)
14176
{
14177
case VK_ERROR_FEATURE_NOT_PRESENT:
14178
break;
14179
case VK_SUCCESS:
14180
finalRes = VK_SUCCESS;
14181
break;
14182
default:
14183
return localRes;
14184
}
14185
}
14186
}
14187
}
14188
14189
return finalRes;
14190
}
14191
14192
VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
14193
{
14194
AtomicTransactionalIncrement<VMA_ATOMIC_UINT32> deviceMemoryCountIncrement;
14195
const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
14196
#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
14197
if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
14198
{
14199
return VK_ERROR_TOO_MANY_OBJECTS;
14200
}
14201
#endif
14202
14203
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
14204
14205
// HeapSizeLimit is in effect for this heap.
14206
if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
14207
{
14208
const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14209
VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
14210
for(;;)
14211
{
14212
const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
14213
if(blockBytesAfterAllocation > heapSize)
14214
{
14215
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14216
}
14217
if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
14218
{
14219
break;
14220
}
14221
}
14222
}
14223
else
14224
{
14225
m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
14226
}
14227
++m_Budget.m_BlockCount[heapIndex];
14228
14229
// VULKAN CALL vkAllocateMemory.
14230
VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
14231
14232
if(res == VK_SUCCESS)
14233
{
14234
#if VMA_MEMORY_BUDGET
14235
++m_Budget.m_OperationsSinceBudgetFetch;
14236
#endif
14237
14238
// Informative callback.
14239
if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
14240
{
14241
(*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
14242
}
14243
14244
deviceMemoryCountIncrement.Commit();
14245
}
14246
else
14247
{
14248
--m_Budget.m_BlockCount[heapIndex];
14249
m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
14250
}
14251
14252
return res;
14253
}
14254
14255
void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
14256
{
14257
// Informative callback.
14258
if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
14259
{
14260
(*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
14261
}
14262
14263
// VULKAN CALL vkFreeMemory.
14264
(*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
14265
14266
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
14267
--m_Budget.m_BlockCount[heapIndex];
14268
m_Budget.m_BlockBytes[heapIndex] -= size;
14269
14270
--m_DeviceMemoryCount;
14271
}
14272
14273
VkResult VmaAllocator_T::BindVulkanBuffer(
14274
VkDeviceMemory memory,
14275
VkDeviceSize memoryOffset,
14276
VkBuffer buffer,
14277
const void* pNext)
14278
{
14279
if(pNext != VMA_NULL)
14280
{
14281
#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
14282
if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
14283
m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
14284
{
14285
VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
14286
bindBufferMemoryInfo.pNext = pNext;
14287
bindBufferMemoryInfo.buffer = buffer;
14288
bindBufferMemoryInfo.memory = memory;
14289
bindBufferMemoryInfo.memoryOffset = memoryOffset;
14290
return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
14291
}
14292
else
14293
#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
14294
{
14295
return VK_ERROR_EXTENSION_NOT_PRESENT;
14296
}
14297
}
14298
else
14299
{
14300
return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
14301
}
14302
}
14303
14304
VkResult VmaAllocator_T::BindVulkanImage(
14305
VkDeviceMemory memory,
14306
VkDeviceSize memoryOffset,
14307
VkImage image,
14308
const void* pNext)
14309
{
14310
if(pNext != VMA_NULL)
14311
{
14312
#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
14313
if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
14314
m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
14315
{
14316
VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
14317
bindBufferMemoryInfo.pNext = pNext;
14318
bindBufferMemoryInfo.image = image;
14319
bindBufferMemoryInfo.memory = memory;
14320
bindBufferMemoryInfo.memoryOffset = memoryOffset;
14321
return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
14322
}
14323
else
14324
#endif // #if VMA_BIND_MEMORY2
14325
{
14326
return VK_ERROR_EXTENSION_NOT_PRESENT;
14327
}
14328
}
14329
else
14330
{
14331
return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
14332
}
14333
}
14334
14335
VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
14336
{
14337
switch(hAllocation->GetType())
14338
{
14339
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14340
{
14341
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
14342
char *pBytes = VMA_NULL;
14343
VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
14344
if(res == VK_SUCCESS)
14345
{
14346
*ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
14347
hAllocation->BlockAllocMap();
14348
}
14349
return res;
14350
}
14351
VMA_FALLTHROUGH; // Fallthrough
14352
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14353
return hAllocation->DedicatedAllocMap(this, ppData);
14354
default:
14355
VMA_ASSERT(0);
14356
return VK_ERROR_MEMORY_MAP_FAILED;
14357
}
14358
}
14359
14360
void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
14361
{
14362
switch(hAllocation->GetType())
14363
{
14364
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14365
{
14366
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
14367
hAllocation->BlockAllocUnmap();
14368
pBlock->Unmap(this, 1);
14369
}
14370
break;
14371
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14372
hAllocation->DedicatedAllocUnmap(this);
14373
break;
14374
default:
14375
VMA_ASSERT(0);
14376
}
14377
}
14378
14379
VkResult VmaAllocator_T::BindBufferMemory(
14380
VmaAllocation hAllocation,
14381
VkDeviceSize allocationLocalOffset,
14382
VkBuffer hBuffer,
14383
const void* pNext)
14384
{
14385
VkResult res = VK_ERROR_UNKNOWN_COPY;
14386
switch(hAllocation->GetType())
14387
{
14388
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14389
res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
14390
break;
14391
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14392
{
14393
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
14394
VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");
14395
res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
14396
break;
14397
}
14398
default:
14399
VMA_ASSERT(0);
14400
}
14401
return res;
14402
}
14403
14404
VkResult VmaAllocator_T::BindImageMemory(
14405
VmaAllocation hAllocation,
14406
VkDeviceSize allocationLocalOffset,
14407
VkImage hImage,
14408
const void* pNext)
14409
{
14410
VkResult res = VK_ERROR_UNKNOWN_COPY;
14411
switch(hAllocation->GetType())
14412
{
14413
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14414
res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
14415
break;
14416
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14417
{
14418
VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
14419
VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");
14420
res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
14421
break;
14422
}
14423
default:
14424
VMA_ASSERT(0);
14425
}
14426
return res;
14427
}
14428
14429
VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
14430
VmaAllocation hAllocation,
14431
VkDeviceSize offset, VkDeviceSize size,
14432
VMA_CACHE_OPERATION op)
14433
{
14434
VkResult res = VK_SUCCESS;
14435
14436
VkMappedMemoryRange memRange = {};
14437
if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
14438
{
14439
switch(op)
14440
{
14441
case VMA_CACHE_FLUSH:
14442
res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
14443
break;
14444
case VMA_CACHE_INVALIDATE:
14445
res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
14446
break;
14447
default:
14448
VMA_ASSERT(0);
14449
}
14450
}
14451
// else: Just ignore this call.
14452
return res;
14453
}
14454
14455
VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
14456
uint32_t allocationCount,
14457
const VmaAllocation* allocations,
14458
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
14459
VMA_CACHE_OPERATION op)
14460
{
14461
typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
14462
typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
14463
RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
14464
14465
for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14466
{
14467
const VmaAllocation alloc = allocations[allocIndex];
14468
const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
14469
const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
14470
VkMappedMemoryRange newRange;
14471
if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
14472
{
14473
ranges.push_back(newRange);
14474
}
14475
}
14476
14477
VkResult res = VK_SUCCESS;
14478
if(!ranges.empty())
14479
{
14480
switch(op)
14481
{
14482
case VMA_CACHE_FLUSH:
14483
res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
14484
break;
14485
case VMA_CACHE_INVALIDATE:
14486
res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
14487
break;
14488
default:
14489
VMA_ASSERT(0);
14490
}
14491
}
14492
// else: Just ignore this call.
14493
return res;
14494
}
14495
14496
VkResult VmaAllocator_T::CopyMemoryToAllocation(
14497
const void* pSrcHostPointer,
14498
VmaAllocation dstAllocation,
14499
VkDeviceSize dstAllocationLocalOffset,
14500
VkDeviceSize size)
14501
{
14502
void* dstMappedData = VMA_NULL;
14503
VkResult res = Map(dstAllocation, &dstMappedData);
14504
if(res == VK_SUCCESS)
14505
{
14506
memcpy((char*)dstMappedData + dstAllocationLocalOffset, pSrcHostPointer, (size_t)size);
14507
Unmap(dstAllocation);
14508
res = FlushOrInvalidateAllocation(dstAllocation, dstAllocationLocalOffset, size, VMA_CACHE_FLUSH);
14509
}
14510
return res;
14511
}
14512
14513
VkResult VmaAllocator_T::CopyAllocationToMemory(
14514
VmaAllocation srcAllocation,
14515
VkDeviceSize srcAllocationLocalOffset,
14516
void* pDstHostPointer,
14517
VkDeviceSize size)
14518
{
14519
void* srcMappedData = VMA_NULL;
14520
VkResult res = Map(srcAllocation, &srcMappedData);
14521
if(res == VK_SUCCESS)
14522
{
14523
res = FlushOrInvalidateAllocation(srcAllocation, srcAllocationLocalOffset, size, VMA_CACHE_INVALIDATE);
14524
if(res == VK_SUCCESS)
14525
{
14526
memcpy(pDstHostPointer, (const char*)srcMappedData + srcAllocationLocalOffset, (size_t)size);
14527
Unmap(srcAllocation);
14528
}
14529
}
14530
return res;
14531
}
14532
14533
void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
14534
{
14535
VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
14536
14537
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14538
VmaPool parentPool = allocation->GetParentPool();
14539
if(parentPool == VK_NULL_HANDLE)
14540
{
14541
// Default pool
14542
m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
14543
}
14544
else
14545
{
14546
// Custom pool
14547
parentPool->m_DedicatedAllocations.Unregister(allocation);
14548
}
14549
14550
VkDeviceMemory hMemory = allocation->GetMemory();
14551
14552
/*
14553
There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14554
before vkFreeMemory.
14555
14556
if(allocation->GetMappedData() != VMA_NULL)
14557
{
14558
(*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14559
}
14560
*/
14561
14562
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
14563
14564
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
14565
m_AllocationObjectAllocator.Free(allocation);
14566
14567
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);
14568
}
14569
14570
uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
14571
{
14572
VkBufferCreateInfo dummyBufCreateInfo;
14573
VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
14574
14575
uint32_t memoryTypeBits = 0;
14576
14577
// Create buffer.
14578
VkBuffer buf = VK_NULL_HANDLE;
14579
VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
14580
m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
14581
if(res == VK_SUCCESS)
14582
{
14583
// Query for supported memory types.
14584
VkMemoryRequirements memReq;
14585
(*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
14586
memoryTypeBits = memReq.memoryTypeBits;
14587
14588
// Destroy buffer.
14589
(*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
14590
}
14591
14592
return memoryTypeBits;
14593
}
14594
14595
uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
14596
{
14597
// Make sure memory information is already fetched.
14598
VMA_ASSERT(GetMemoryTypeCount() > 0);
14599
14600
uint32_t memoryTypeBits = UINT32_MAX;
14601
14602
if(!m_UseAmdDeviceCoherentMemory)
14603
{
14604
// Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
14605
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14606
{
14607
if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
14608
{
14609
memoryTypeBits &= ~(1u << memTypeIndex);
14610
}
14611
}
14612
}
14613
14614
return memoryTypeBits;
14615
}
14616
14617
bool VmaAllocator_T::GetFlushOrInvalidateRange(
14618
VmaAllocation allocation,
14619
VkDeviceSize offset, VkDeviceSize size,
14620
VkMappedMemoryRange& outRange) const
14621
{
14622
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14623
if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
14624
{
14625
const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
14626
const VkDeviceSize allocationSize = allocation->GetSize();
14627
VMA_ASSERT(offset <= allocationSize);
14628
14629
outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
14630
outRange.pNext = VMA_NULL;
14631
outRange.memory = allocation->GetMemory();
14632
14633
switch(allocation->GetType())
14634
{
14635
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14636
outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
14637
if(size == VK_WHOLE_SIZE)
14638
{
14639
outRange.size = allocationSize - outRange.offset;
14640
}
14641
else
14642
{
14643
VMA_ASSERT(offset + size <= allocationSize);
14644
outRange.size = VMA_MIN(
14645
VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
14646
allocationSize - outRange.offset);
14647
}
14648
break;
14649
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14650
{
14651
// 1. Still within this allocation.
14652
outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
14653
if(size == VK_WHOLE_SIZE)
14654
{
14655
size = allocationSize - offset;
14656
}
14657
else
14658
{
14659
VMA_ASSERT(offset + size <= allocationSize);
14660
}
14661
outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
14662
14663
// 2. Adjust to whole block.
14664
const VkDeviceSize allocationOffset = allocation->GetOffset();
14665
VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
14666
const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
14667
outRange.offset += allocationOffset;
14668
outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
14669
14670
break;
14671
}
14672
default:
14673
VMA_ASSERT(0);
14674
}
14675
return true;
14676
}
14677
return false;
14678
}
14679
14680
#if VMA_MEMORY_BUDGET
14681
void VmaAllocator_T::UpdateVulkanBudget()
14682
{
14683
VMA_ASSERT(m_UseExtMemoryBudget);
14684
14685
VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
14686
14687
VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
14688
VmaPnextChainPushFront(&memProps, &budgetProps);
14689
14690
GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
14691
14692
{
14693
VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
14694
14695
for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14696
{
14697
m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
14698
m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
14699
m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
14700
14701
// Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
14702
if(m_Budget.m_VulkanBudget[heapIndex] == 0)
14703
{
14704
m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
14705
}
14706
else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
14707
{
14708
m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
14709
}
14710
if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
14711
{
14712
m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
14713
}
14714
}
14715
m_Budget.m_OperationsSinceBudgetFetch = 0;
14716
}
14717
}
14718
#endif // VMA_MEMORY_BUDGET
14719
14720
void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
14721
{
14722
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
14723
hAllocation->IsMappingAllowed() &&
14724
(m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
14725
{
14726
void* pData = VMA_NULL;
14727
VkResult res = Map(hAllocation, &pData);
14728
if(res == VK_SUCCESS)
14729
{
14730
memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
14731
FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
14732
Unmap(hAllocation);
14733
}
14734
else
14735
{
14736
VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
14737
}
14738
}
14739
}
14740
14741
uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
14742
{
14743
uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
14744
if(memoryTypeBits == UINT32_MAX)
14745
{
14746
memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
14747
m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
14748
}
14749
return memoryTypeBits;
14750
}
14751
14752
#if VMA_STATS_STRING_ENABLED
14753
void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
14754
{
14755
json.WriteString("DefaultPools");
14756
json.BeginObject();
14757
{
14758
for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14759
{
14760
VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];
14761
VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
14762
if (pBlockVector != VMA_NULL)
14763
{
14764
json.BeginString("Type ");
14765
json.ContinueString(memTypeIndex);
14766
json.EndString();
14767
json.BeginObject();
14768
{
14769
json.WriteString("PreferredBlockSize");
14770
json.WriteNumber(pBlockVector->GetPreferredBlockSize());
14771
14772
json.WriteString("Blocks");
14773
pBlockVector->PrintDetailedMap(json);
14774
14775
json.WriteString("DedicatedAllocations");
14776
dedicatedAllocList.BuildStatsString(json);
14777
}
14778
json.EndObject();
14779
}
14780
}
14781
}
14782
json.EndObject();
14783
14784
json.WriteString("CustomPools");
14785
json.BeginObject();
14786
{
14787
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14788
if (!m_Pools.IsEmpty())
14789
{
14790
for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14791
{
14792
bool displayType = true;
14793
size_t index = 0;
14794
for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
14795
{
14796
VmaBlockVector& blockVector = pool->m_BlockVector;
14797
if (blockVector.GetMemoryTypeIndex() == memTypeIndex)
14798
{
14799
if (displayType)
14800
{
14801
json.BeginString("Type ");
14802
json.ContinueString(memTypeIndex);
14803
json.EndString();
14804
json.BeginArray();
14805
displayType = false;
14806
}
14807
14808
json.BeginObject();
14809
{
14810
json.WriteString("Name");
14811
json.BeginString();
14812
json.ContinueString((uint64_t)index++);
14813
if (pool->GetName())
14814
{
14815
json.ContinueString(" - ");
14816
json.ContinueString(pool->GetName());
14817
}
14818
json.EndString();
14819
14820
json.WriteString("PreferredBlockSize");
14821
json.WriteNumber(blockVector.GetPreferredBlockSize());
14822
14823
json.WriteString("Blocks");
14824
blockVector.PrintDetailedMap(json);
14825
14826
json.WriteString("DedicatedAllocations");
14827
pool->m_DedicatedAllocations.BuildStatsString(json);
14828
}
14829
json.EndObject();
14830
}
14831
}
14832
14833
if (!displayType)
14834
json.EndArray();
14835
}
14836
}
14837
}
14838
json.EndObject();
14839
}
14840
#endif // VMA_STATS_STRING_ENABLED
14841
#endif // _VMA_ALLOCATOR_T_FUNCTIONS
14842
14843
14844
#ifndef _VMA_PUBLIC_INTERFACE
14845
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
14846
const VmaAllocatorCreateInfo* pCreateInfo,
14847
VmaAllocator* pAllocator)
14848
{
14849
VMA_ASSERT(pCreateInfo && pAllocator);
14850
VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
14851
(VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
14852
VMA_DEBUG_LOG("vmaCreateAllocator");
14853
*pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
14854
VkResult result = (*pAllocator)->Init(pCreateInfo);
14855
if(result < 0)
14856
{
14857
vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
14858
*pAllocator = VK_NULL_HANDLE;
14859
}
14860
return result;
14861
}
14862
14863
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
14864
VmaAllocator allocator)
14865
{
14866
if(allocator != VK_NULL_HANDLE)
14867
{
14868
VMA_DEBUG_LOG("vmaDestroyAllocator");
14869
VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
14870
vma_delete(&allocationCallbacks, allocator);
14871
}
14872
}
14873
14874
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
14875
{
14876
VMA_ASSERT(allocator && pAllocatorInfo);
14877
pAllocatorInfo->instance = allocator->m_hInstance;
14878
pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
14879
pAllocatorInfo->device = allocator->m_hDevice;
14880
}
14881
14882
VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
14883
VmaAllocator allocator,
14884
const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
14885
{
14886
VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
14887
*ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
14888
}
14889
14890
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
14891
VmaAllocator allocator,
14892
const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
14893
{
14894
VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
14895
*ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
14896
}
14897
14898
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
14899
VmaAllocator allocator,
14900
uint32_t memoryTypeIndex,
14901
VkMemoryPropertyFlags* pFlags)
14902
{
14903
VMA_ASSERT(allocator && pFlags);
14904
VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
14905
*pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
14906
}
14907
14908
VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
14909
VmaAllocator allocator,
14910
uint32_t frameIndex)
14911
{
14912
VMA_ASSERT(allocator);
14913
14914
VMA_DEBUG_GLOBAL_MUTEX_LOCK
14915
14916
allocator->SetCurrentFrameIndex(frameIndex);
14917
}
14918
14919
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
14920
VmaAllocator allocator,
14921
VmaTotalStatistics* pStats)
14922
{
14923
VMA_ASSERT(allocator && pStats);
14924
VMA_DEBUG_GLOBAL_MUTEX_LOCK
14925
allocator->CalculateStatistics(pStats);
14926
}
14927
14928
VMA_CALL_PRE uint64_t VMA_CALL_POST vmaCalculateLazilyAllocatedBytes(
14929
VmaAllocator allocator)
14930
{
14931
VMA_ASSERT(allocator);
14932
VMA_DEBUG_GLOBAL_MUTEX_LOCK
14933
VmaTotalStatistics stats;
14934
allocator->CalculateStatistics(&stats);
14935
uint64_t total_lazilily_allocated_bytes = 0;
14936
for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) {
14937
for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) {
14938
if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex) {
14939
VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
14940
if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
14941
total_lazilily_allocated_bytes += stats.memoryType[typeIndex].statistics.allocationBytes;
14942
}
14943
}
14944
}
14945
return total_lazilily_allocated_bytes;
14946
}
14947
14948
VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
14949
VmaAllocator allocator,
14950
VmaBudget* pBudgets)
14951
{
14952
VMA_ASSERT(allocator && pBudgets);
14953
VMA_DEBUG_GLOBAL_MUTEX_LOCK
14954
allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());
14955
}
14956
14957
#if VMA_STATS_STRING_ENABLED
14958
14959
VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
14960
VmaAllocator allocator,
14961
char** ppStatsString,
14962
VkBool32 detailedMap)
14963
{
14964
VMA_ASSERT(allocator && ppStatsString);
14965
VMA_DEBUG_GLOBAL_MUTEX_LOCK
14966
14967
VmaStringBuilder sb(allocator->GetAllocationCallbacks());
14968
{
14969
VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
14970
allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());
14971
14972
VmaTotalStatistics stats;
14973
allocator->CalculateStatistics(&stats);
14974
14975
VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
14976
json.BeginObject();
14977
{
14978
json.WriteString("General");
14979
json.BeginObject();
14980
{
14981
const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;
14982
const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;
14983
14984
json.WriteString("API");
14985
json.WriteString("Vulkan");
14986
14987
json.WriteString("apiVersion");
14988
json.BeginString();
14989
json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion));
14990
json.ContinueString(".");
14991
json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion));
14992
json.ContinueString(".");
14993
json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion));
14994
json.EndString();
14995
14996
json.WriteString("GPU");
14997
json.WriteString(deviceProperties.deviceName);
14998
json.WriteString("deviceType");
14999
json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));
15000
15001
json.WriteString("maxMemoryAllocationCount");
15002
json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);
15003
json.WriteString("bufferImageGranularity");
15004
json.WriteNumber(deviceProperties.limits.bufferImageGranularity);
15005
json.WriteString("nonCoherentAtomSize");
15006
json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);
15007
15008
json.WriteString("memoryHeapCount");
15009
json.WriteNumber(memoryProperties.memoryHeapCount);
15010
json.WriteString("memoryTypeCount");
15011
json.WriteNumber(memoryProperties.memoryTypeCount);
15012
}
15013
json.EndObject();
15014
}
15015
{
15016
json.WriteString("Total");
15017
VmaPrintDetailedStatistics(json, stats.total);
15018
}
15019
{
15020
json.WriteString("MemoryInfo");
15021
json.BeginObject();
15022
{
15023
for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15024
{
15025
json.BeginString("Heap ");
15026
json.ContinueString(heapIndex);
15027
json.EndString();
15028
json.BeginObject();
15029
{
15030
const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];
15031
json.WriteString("Flags");
15032
json.BeginArray(true);
15033
{
15034
if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
15035
json.WriteString("DEVICE_LOCAL");
15036
#if VMA_VULKAN_VERSION >= 1001000
15037
if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)
15038
json.WriteString("MULTI_INSTANCE");
15039
#endif
15040
15041
VkMemoryHeapFlags flags = heapInfo.flags &
15042
~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT
15043
#if VMA_VULKAN_VERSION >= 1001000
15044
| VK_MEMORY_HEAP_MULTI_INSTANCE_BIT
15045
#endif
15046
);
15047
if (flags != 0)
15048
json.WriteNumber(flags);
15049
}
15050
json.EndArray();
15051
15052
json.WriteString("Size");
15053
json.WriteNumber(heapInfo.size);
15054
15055
json.WriteString("Budget");
15056
json.BeginObject();
15057
{
15058
json.WriteString("BudgetBytes");
15059
json.WriteNumber(budgets[heapIndex].budget);
15060
json.WriteString("UsageBytes");
15061
json.WriteNumber(budgets[heapIndex].usage);
15062
}
15063
json.EndObject();
15064
15065
json.WriteString("Stats");
15066
VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);
15067
15068
json.WriteString("MemoryPools");
15069
json.BeginObject();
15070
{
15071
for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15072
{
15073
if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15074
{
15075
json.BeginString("Type ");
15076
json.ContinueString(typeIndex);
15077
json.EndString();
15078
json.BeginObject();
15079
{
15080
json.WriteString("Flags");
15081
json.BeginArray(true);
15082
{
15083
VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15084
if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
15085
json.WriteString("DEVICE_LOCAL");
15086
if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
15087
json.WriteString("HOST_VISIBLE");
15088
if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
15089
json.WriteString("HOST_COHERENT");
15090
if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
15091
json.WriteString("HOST_CACHED");
15092
if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
15093
json.WriteString("LAZILY_ALLOCATED");
15094
#if VMA_VULKAN_VERSION >= 1001000
15095
if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)
15096
json.WriteString("PROTECTED");
15097
#endif
15098
#if VK_AMD_device_coherent_memory
15099
if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)
15100
json.WriteString("DEVICE_COHERENT_AMD");
15101
if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)
15102
json.WriteString("DEVICE_UNCACHED_AMD");
15103
#endif
15104
15105
flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
15106
#if VMA_VULKAN_VERSION >= 1001000
15107
| VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
15108
#endif
15109
#if VK_AMD_device_coherent_memory
15110
| VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY
15111
| VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY
15112
#endif
15113
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
15114
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
15115
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
15116
if (flags != 0)
15117
json.WriteNumber(flags);
15118
}
15119
json.EndArray();
15120
15121
json.WriteString("Stats");
15122
VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);
15123
}
15124
json.EndObject();
15125
}
15126
}
15127
15128
}
15129
json.EndObject();
15130
}
15131
json.EndObject();
15132
}
15133
}
15134
json.EndObject();
15135
}
15136
15137
if (detailedMap == VK_TRUE)
15138
allocator->PrintDetailedMap(json);
15139
15140
json.EndObject();
15141
}
15142
15143
*ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
15144
}
15145
15146
VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
15147
VmaAllocator allocator,
15148
char* pStatsString)
15149
{
15150
if(pStatsString != VMA_NULL)
15151
{
15152
VMA_ASSERT(allocator);
15153
VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
15154
}
15155
}
15156
15157
#endif // VMA_STATS_STRING_ENABLED
15158
15159
/*
15160
This function is not protected by any mutex because it just reads immutable data.
15161
*/
15162
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
15163
VmaAllocator allocator,
15164
uint32_t memoryTypeBits,
15165
const VmaAllocationCreateInfo* pAllocationCreateInfo,
15166
uint32_t* pMemoryTypeIndex)
15167
{
15168
VMA_ASSERT(allocator != VK_NULL_HANDLE);
15169
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15170
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15171
15172
return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, VmaBufferImageUsage::UNKNOWN, pMemoryTypeIndex);
15173
}
15174
15175
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
15176
VmaAllocator allocator,
15177
const VkBufferCreateInfo* pBufferCreateInfo,
15178
const VmaAllocationCreateInfo* pAllocationCreateInfo,
15179
uint32_t* pMemoryTypeIndex)
15180
{
15181
VMA_ASSERT(allocator != VK_NULL_HANDLE);
15182
VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15183
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15184
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15185
15186
const VkDevice hDev = allocator->m_hDevice;
15187
const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
15188
VkResult res;
15189
15190
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
15191
if(funcs->vkGetDeviceBufferMemoryRequirements)
15192
{
15193
// Can query straight from VkBufferCreateInfo :)
15194
VkDeviceBufferMemoryRequirementsKHR devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR};
15195
devBufMemReq.pCreateInfo = pBufferCreateInfo;
15196
15197
VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
15198
(*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);
15199
15200
res = allocator->FindMemoryTypeIndex(
15201
memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo,
15202
VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex);
15203
}
15204
else
15205
#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
15206
{
15207
// Must create a dummy buffer to query :(
15208
VkBuffer hBuffer = VK_NULL_HANDLE;
15209
res = funcs->vkCreateBuffer(
15210
hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15211
if(res == VK_SUCCESS)
15212
{
15213
VkMemoryRequirements memReq = {};
15214
funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);
15215
15216
res = allocator->FindMemoryTypeIndex(
15217
memReq.memoryTypeBits, pAllocationCreateInfo,
15218
VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex);
15219
15220
funcs->vkDestroyBuffer(
15221
hDev, hBuffer, allocator->GetAllocationCallbacks());
15222
}
15223
}
15224
return res;
15225
}
15226
15227
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
15228
VmaAllocator allocator,
15229
const VkImageCreateInfo* pImageCreateInfo,
15230
const VmaAllocationCreateInfo* pAllocationCreateInfo,
15231
uint32_t* pMemoryTypeIndex)
15232
{
15233
VMA_ASSERT(allocator != VK_NULL_HANDLE);
15234
VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15235
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15236
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15237
15238
const VkDevice hDev = allocator->m_hDevice;
15239
const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
15240
VkResult res;
15241
15242
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
15243
if(funcs->vkGetDeviceImageMemoryRequirements)
15244
{
15245
// Can query straight from VkImageCreateInfo :)
15246
VkDeviceImageMemoryRequirementsKHR devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR};
15247
devImgMemReq.pCreateInfo = pImageCreateInfo;
15248
VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&
15249
"Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");
15250
15251
VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
15252
(*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);
15253
15254
res = allocator->FindMemoryTypeIndex(
15255
memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo,
15256
VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex);
15257
}
15258
else
15259
#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
15260
{
15261
// Must create a dummy image to query :(
15262
VkImage hImage = VK_NULL_HANDLE;
15263
res = funcs->vkCreateImage(
15264
hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15265
if(res == VK_SUCCESS)
15266
{
15267
VkMemoryRequirements memReq = {};
15268
funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);
15269
15270
res = allocator->FindMemoryTypeIndex(
15271
memReq.memoryTypeBits, pAllocationCreateInfo,
15272
VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex);
15273
15274
funcs->vkDestroyImage(
15275
hDev, hImage, allocator->GetAllocationCallbacks());
15276
}
15277
}
15278
return res;
15279
}
15280
15281
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
15282
VmaAllocator allocator,
15283
const VmaPoolCreateInfo* pCreateInfo,
15284
VmaPool* pPool)
15285
{
15286
VMA_ASSERT(allocator && pCreateInfo && pPool);
15287
15288
VMA_DEBUG_LOG("vmaCreatePool");
15289
15290
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15291
15292
return allocator->CreatePool(pCreateInfo, pPool);
15293
}
15294
15295
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
15296
VmaAllocator allocator,
15297
VmaPool pool)
15298
{
15299
VMA_ASSERT(allocator);
15300
15301
if(pool == VK_NULL_HANDLE)
15302
{
15303
return;
15304
}
15305
15306
VMA_DEBUG_LOG("vmaDestroyPool");
15307
15308
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15309
15310
allocator->DestroyPool(pool);
15311
}
15312
15313
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
15314
VmaAllocator allocator,
15315
VmaPool pool,
15316
VmaStatistics* pPoolStats)
15317
{
15318
VMA_ASSERT(allocator && pool && pPoolStats);
15319
15320
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15321
15322
allocator->GetPoolStatistics(pool, pPoolStats);
15323
}
15324
15325
VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
15326
VmaAllocator allocator,
15327
VmaPool pool,
15328
VmaDetailedStatistics* pPoolStats)
15329
{
15330
VMA_ASSERT(allocator && pool && pPoolStats);
15331
15332
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15333
15334
allocator->CalculatePoolStatistics(pool, pPoolStats);
15335
}
15336
15337
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15338
{
15339
VMA_ASSERT(allocator && pool);
15340
15341
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15342
15343
VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15344
15345
return allocator->CheckPoolCorruption(pool);
15346
}
15347
15348
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
15349
VmaAllocator allocator,
15350
VmaPool pool,
15351
const char** ppName)
15352
{
15353
VMA_ASSERT(allocator && pool && ppName);
15354
15355
VMA_DEBUG_LOG("vmaGetPoolName");
15356
15357
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15358
15359
*ppName = pool->GetName();
15360
}
15361
15362
VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
15363
VmaAllocator allocator,
15364
VmaPool pool,
15365
const char* pName)
15366
{
15367
VMA_ASSERT(allocator && pool);
15368
15369
VMA_DEBUG_LOG("vmaSetPoolName");
15370
15371
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15372
15373
pool->SetName(pName);
15374
}
15375
15376
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
15377
VmaAllocator allocator,
15378
const VkMemoryRequirements* pVkMemoryRequirements,
15379
const VmaAllocationCreateInfo* pCreateInfo,
15380
VmaAllocation* pAllocation,
15381
VmaAllocationInfo* pAllocationInfo)
15382
{
15383
VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15384
15385
VMA_DEBUG_LOG("vmaAllocateMemory");
15386
15387
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15388
15389
VkResult result = allocator->AllocateMemory(
15390
*pVkMemoryRequirements,
15391
false, // requiresDedicatedAllocation
15392
false, // prefersDedicatedAllocation
15393
VK_NULL_HANDLE, // dedicatedBuffer
15394
VK_NULL_HANDLE, // dedicatedImage
15395
VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage
15396
*pCreateInfo,
15397
VMA_SUBALLOCATION_TYPE_UNKNOWN,
15398
1, // allocationCount
15399
pAllocation);
15400
15401
if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15402
{
15403
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15404
}
15405
15406
return result;
15407
}
15408
15409
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
15410
VmaAllocator allocator,
15411
const VkMemoryRequirements* pVkMemoryRequirements,
15412
const VmaAllocationCreateInfo* pCreateInfo,
15413
size_t allocationCount,
15414
VmaAllocation* pAllocations,
15415
VmaAllocationInfo* pAllocationInfo)
15416
{
15417
if(allocationCount == 0)
15418
{
15419
return VK_SUCCESS;
15420
}
15421
15422
VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15423
15424
VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15425
15426
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15427
15428
VkResult result = allocator->AllocateMemory(
15429
*pVkMemoryRequirements,
15430
false, // requiresDedicatedAllocation
15431
false, // prefersDedicatedAllocation
15432
VK_NULL_HANDLE, // dedicatedBuffer
15433
VK_NULL_HANDLE, // dedicatedImage
15434
VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage
15435
*pCreateInfo,
15436
VMA_SUBALLOCATION_TYPE_UNKNOWN,
15437
allocationCount,
15438
pAllocations);
15439
15440
if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15441
{
15442
for(size_t i = 0; i < allocationCount; ++i)
15443
{
15444
allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
15445
}
15446
}
15447
15448
return result;
15449
}
15450
15451
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
15452
VmaAllocator allocator,
15453
VkBuffer buffer,
15454
const VmaAllocationCreateInfo* pCreateInfo,
15455
VmaAllocation* pAllocation,
15456
VmaAllocationInfo* pAllocationInfo)
15457
{
15458
VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
15459
15460
VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
15461
15462
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15463
15464
VkMemoryRequirements vkMemReq = {};
15465
bool requiresDedicatedAllocation = false;
15466
bool prefersDedicatedAllocation = false;
15467
allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
15468
requiresDedicatedAllocation,
15469
prefersDedicatedAllocation);
15470
15471
VkResult result = allocator->AllocateMemory(
15472
vkMemReq,
15473
requiresDedicatedAllocation,
15474
prefersDedicatedAllocation,
15475
buffer, // dedicatedBuffer
15476
VK_NULL_HANDLE, // dedicatedImage
15477
VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage
15478
*pCreateInfo,
15479
VMA_SUBALLOCATION_TYPE_BUFFER,
15480
1, // allocationCount
15481
pAllocation);
15482
15483
if(pAllocationInfo && result == VK_SUCCESS)
15484
{
15485
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15486
}
15487
15488
return result;
15489
}
15490
15491
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
15492
VmaAllocator allocator,
15493
VkImage image,
15494
const VmaAllocationCreateInfo* pCreateInfo,
15495
VmaAllocation* pAllocation,
15496
VmaAllocationInfo* pAllocationInfo)
15497
{
15498
VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
15499
15500
VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
15501
15502
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15503
15504
VkMemoryRequirements vkMemReq = {};
15505
bool requiresDedicatedAllocation = false;
15506
bool prefersDedicatedAllocation = false;
15507
allocator->GetImageMemoryRequirements(image, vkMemReq,
15508
requiresDedicatedAllocation, prefersDedicatedAllocation);
15509
15510
VkResult result = allocator->AllocateMemory(
15511
vkMemReq,
15512
requiresDedicatedAllocation,
15513
prefersDedicatedAllocation,
15514
VK_NULL_HANDLE, // dedicatedBuffer
15515
image, // dedicatedImage
15516
VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage
15517
*pCreateInfo,
15518
VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
15519
1, // allocationCount
15520
pAllocation);
15521
15522
if(pAllocationInfo && result == VK_SUCCESS)
15523
{
15524
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15525
}
15526
15527
return result;
15528
}
15529
15530
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
15531
VmaAllocator allocator,
15532
VmaAllocation allocation)
15533
{
15534
VMA_ASSERT(allocator);
15535
15536
if(allocation == VK_NULL_HANDLE)
15537
{
15538
return;
15539
}
15540
15541
VMA_DEBUG_LOG("vmaFreeMemory");
15542
15543
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15544
15545
allocator->FreeMemory(
15546
1, // allocationCount
15547
&allocation);
15548
}
15549
15550
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
15551
VmaAllocator allocator,
15552
size_t allocationCount,
15553
const VmaAllocation* pAllocations)
15554
{
15555
if(allocationCount == 0)
15556
{
15557
return;
15558
}
15559
15560
VMA_ASSERT(allocator);
15561
15562
VMA_DEBUG_LOG("vmaFreeMemoryPages");
15563
15564
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15565
15566
allocator->FreeMemory(allocationCount, pAllocations);
15567
}
15568
15569
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
15570
VmaAllocator allocator,
15571
VmaAllocation allocation,
15572
VmaAllocationInfo* pAllocationInfo)
15573
{
15574
VMA_ASSERT(allocator && allocation && pAllocationInfo);
15575
15576
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15577
15578
allocator->GetAllocationInfo(allocation, pAllocationInfo);
15579
}
15580
15581
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2(
15582
VmaAllocator allocator,
15583
VmaAllocation allocation,
15584
VmaAllocationInfo2* pAllocationInfo)
15585
{
15586
VMA_ASSERT(allocator && allocation && pAllocationInfo);
15587
15588
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15589
15590
allocator->GetAllocationInfo2(allocation, pAllocationInfo);
15591
}
15592
15593
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
15594
VmaAllocator allocator,
15595
VmaAllocation allocation,
15596
void* pUserData)
15597
{
15598
VMA_ASSERT(allocator && allocation);
15599
15600
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15601
15602
allocation->SetUserData(allocator, pUserData);
15603
}
15604
15605
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
15606
VmaAllocator VMA_NOT_NULL allocator,
15607
VmaAllocation VMA_NOT_NULL allocation,
15608
const char* VMA_NULLABLE pName)
15609
{
15610
allocation->SetName(allocator, pName);
15611
}
15612
15613
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
15614
VmaAllocator VMA_NOT_NULL allocator,
15615
VmaAllocation VMA_NOT_NULL allocation,
15616
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)
15617
{
15618
VMA_ASSERT(allocator && allocation && pFlags);
15619
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15620
*pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15621
}
15622
15623
VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
15624
VmaAllocator allocator,
15625
VmaAllocation allocation,
15626
void** ppData)
15627
{
15628
VMA_ASSERT(allocator && allocation && ppData);
15629
15630
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15631
15632
return allocator->Map(allocation, ppData);
15633
}
15634
15635
VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
15636
VmaAllocator allocator,
15637
VmaAllocation allocation)
15638
{
15639
VMA_ASSERT(allocator && allocation);
15640
15641
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15642
15643
allocator->Unmap(allocation);
15644
}
15645
15646
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
15647
VmaAllocator allocator,
15648
VmaAllocation allocation,
15649
VkDeviceSize offset,
15650
VkDeviceSize size)
15651
{
15652
VMA_ASSERT(allocator && allocation);
15653
15654
VMA_DEBUG_LOG("vmaFlushAllocation");
15655
15656
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15657
15658
return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
15659
}
15660
15661
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
15662
VmaAllocator allocator,
15663
VmaAllocation allocation,
15664
VkDeviceSize offset,
15665
VkDeviceSize size)
15666
{
15667
VMA_ASSERT(allocator && allocation);
15668
15669
VMA_DEBUG_LOG("vmaInvalidateAllocation");
15670
15671
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15672
15673
return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
15674
}
15675
15676
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
15677
VmaAllocator allocator,
15678
uint32_t allocationCount,
15679
const VmaAllocation* allocations,
15680
const VkDeviceSize* offsets,
15681
const VkDeviceSize* sizes)
15682
{
15683
VMA_ASSERT(allocator);
15684
15685
if(allocationCount == 0)
15686
{
15687
return VK_SUCCESS;
15688
}
15689
15690
VMA_ASSERT(allocations);
15691
15692
VMA_DEBUG_LOG("vmaFlushAllocations");
15693
15694
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15695
15696
return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
15697
}
15698
15699
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
15700
VmaAllocator allocator,
15701
uint32_t allocationCount,
15702
const VmaAllocation* allocations,
15703
const VkDeviceSize* offsets,
15704
const VkDeviceSize* sizes)
15705
{
15706
VMA_ASSERT(allocator);
15707
15708
if(allocationCount == 0)
15709
{
15710
return VK_SUCCESS;
15711
}
15712
15713
VMA_ASSERT(allocations);
15714
15715
VMA_DEBUG_LOG("vmaInvalidateAllocations");
15716
15717
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15718
15719
return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
15720
}
15721
15722
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(
15723
VmaAllocator allocator,
15724
const void* pSrcHostPointer,
15725
VmaAllocation dstAllocation,
15726
VkDeviceSize dstAllocationLocalOffset,
15727
VkDeviceSize size)
15728
{
15729
VMA_ASSERT(allocator && pSrcHostPointer && dstAllocation);
15730
15731
if(size == 0)
15732
{
15733
return VK_SUCCESS;
15734
}
15735
15736
VMA_DEBUG_LOG("vmaCopyMemoryToAllocation");
15737
15738
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15739
15740
return allocator->CopyMemoryToAllocation(pSrcHostPointer, dstAllocation, dstAllocationLocalOffset, size);
15741
}
15742
15743
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(
15744
VmaAllocator allocator,
15745
VmaAllocation srcAllocation,
15746
VkDeviceSize srcAllocationLocalOffset,
15747
void* pDstHostPointer,
15748
VkDeviceSize size)
15749
{
15750
VMA_ASSERT(allocator && srcAllocation && pDstHostPointer);
15751
15752
if(size == 0)
15753
{
15754
return VK_SUCCESS;
15755
}
15756
15757
VMA_DEBUG_LOG("vmaCopyAllocationToMemory");
15758
15759
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15760
15761
return allocator->CopyAllocationToMemory(srcAllocation, srcAllocationLocalOffset, pDstHostPointer, size);
15762
}
15763
15764
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
15765
VmaAllocator allocator,
15766
uint32_t memoryTypeBits)
15767
{
15768
VMA_ASSERT(allocator);
15769
15770
VMA_DEBUG_LOG("vmaCheckCorruption");
15771
15772
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15773
15774
return allocator->CheckCorruption(memoryTypeBits);
15775
}
15776
15777
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
15778
VmaAllocator allocator,
15779
const VmaDefragmentationInfo* pInfo,
15780
VmaDefragmentationContext* pContext)
15781
{
15782
VMA_ASSERT(allocator && pInfo && pContext);
15783
15784
VMA_DEBUG_LOG("vmaBeginDefragmentation");
15785
15786
if (pInfo->pool != VMA_NULL)
15787
{
15788
// Check if run on supported algorithms
15789
if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
15790
return VK_ERROR_FEATURE_NOT_PRESENT;
15791
}
15792
15793
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15794
15795
*pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);
15796
return VK_SUCCESS;
15797
}
15798
15799
VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
15800
VmaAllocator allocator,
15801
VmaDefragmentationContext context,
15802
VmaDefragmentationStats* pStats)
15803
{
15804
VMA_ASSERT(allocator && context);
15805
15806
VMA_DEBUG_LOG("vmaEndDefragmentation");
15807
15808
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15809
15810
if (pStats)
15811
context->GetStats(*pStats);
15812
vma_delete(allocator, context);
15813
}
15814
15815
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
15816
VmaAllocator VMA_NOT_NULL allocator,
15817
VmaDefragmentationContext VMA_NOT_NULL context,
15818
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
15819
{
15820
VMA_ASSERT(context && pPassInfo);
15821
15822
VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
15823
15824
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15825
15826
return context->DefragmentPassBegin(*pPassInfo);
15827
}
15828
15829
VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
15830
VmaAllocator VMA_NOT_NULL allocator,
15831
VmaDefragmentationContext VMA_NOT_NULL context,
15832
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
15833
{
15834
VMA_ASSERT(context && pPassInfo);
15835
15836
VMA_DEBUG_LOG("vmaEndDefragmentationPass");
15837
15838
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15839
15840
return context->DefragmentPassEnd(*pPassInfo);
15841
}
15842
15843
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
15844
VmaAllocator allocator,
15845
VmaAllocation allocation,
15846
VkBuffer buffer)
15847
{
15848
VMA_ASSERT(allocator && allocation && buffer);
15849
15850
VMA_DEBUG_LOG("vmaBindBufferMemory");
15851
15852
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15853
15854
return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
15855
}
15856
15857
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
15858
VmaAllocator allocator,
15859
VmaAllocation allocation,
15860
VkDeviceSize allocationLocalOffset,
15861
VkBuffer buffer,
15862
const void* pNext)
15863
{
15864
VMA_ASSERT(allocator && allocation && buffer);
15865
15866
VMA_DEBUG_LOG("vmaBindBufferMemory2");
15867
15868
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15869
15870
return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
15871
}
15872
15873
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
15874
VmaAllocator allocator,
15875
VmaAllocation allocation,
15876
VkImage image)
15877
{
15878
VMA_ASSERT(allocator && allocation && image);
15879
15880
VMA_DEBUG_LOG("vmaBindImageMemory");
15881
15882
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15883
15884
return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
15885
}
15886
15887
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
15888
VmaAllocator allocator,
15889
VmaAllocation allocation,
15890
VkDeviceSize allocationLocalOffset,
15891
VkImage image,
15892
const void* pNext)
15893
{
15894
VMA_ASSERT(allocator && allocation && image);
15895
15896
VMA_DEBUG_LOG("vmaBindImageMemory2");
15897
15898
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15899
15900
return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
15901
}
15902
15903
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
15904
VmaAllocator allocator,
15905
const VkBufferCreateInfo* pBufferCreateInfo,
15906
const VmaAllocationCreateInfo* pAllocationCreateInfo,
15907
VkBuffer* pBuffer,
15908
VmaAllocation* pAllocation,
15909
VmaAllocationInfo* pAllocationInfo)
15910
{
15911
VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
15912
15913
if(pBufferCreateInfo->size == 0)
15914
{
15915
return VK_ERROR_INITIALIZATION_FAILED;
15916
}
15917
if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
15918
!allocator->m_UseKhrBufferDeviceAddress)
15919
{
15920
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.");
15921
return VK_ERROR_INITIALIZATION_FAILED;
15922
}
15923
15924
VMA_DEBUG_LOG("vmaCreateBuffer");
15925
15926
VMA_DEBUG_GLOBAL_MUTEX_LOCK
15927
15928
*pBuffer = VK_NULL_HANDLE;
15929
*pAllocation = VK_NULL_HANDLE;
15930
15931
// 1. Create VkBuffer.
15932
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
15933
allocator->m_hDevice,
15934
pBufferCreateInfo,
15935
allocator->GetAllocationCallbacks(),
15936
pBuffer);
15937
if(res >= 0)
15938
{
15939
// 2. vkGetBufferMemoryRequirements.
15940
VkMemoryRequirements vkMemReq = {};
15941
bool requiresDedicatedAllocation = false;
15942
bool prefersDedicatedAllocation = false;
15943
allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
15944
requiresDedicatedAllocation, prefersDedicatedAllocation);
15945
15946
// 3. Allocate memory using allocator.
15947
res = allocator->AllocateMemory(
15948
vkMemReq,
15949
requiresDedicatedAllocation,
15950
prefersDedicatedAllocation,
15951
*pBuffer, // dedicatedBuffer
15952
VK_NULL_HANDLE, // dedicatedImage
15953
VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage
15954
*pAllocationCreateInfo,
15955
VMA_SUBALLOCATION_TYPE_BUFFER,
15956
1, // allocationCount
15957
pAllocation);
15958
15959
if(res >= 0)
15960
{
15961
// 3. Bind buffer with memory.
15962
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
15963
{
15964
res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
15965
}
15966
if(res >= 0)
15967
{
15968
// All steps succeeded.
15969
#if VMA_STATS_STRING_ENABLED
15970
(*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5);
15971
#endif
15972
if(pAllocationInfo != VMA_NULL)
15973
{
15974
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15975
}
15976
15977
return VK_SUCCESS;
15978
}
15979
allocator->FreeMemory(
15980
1, // allocationCount
15981
pAllocation);
15982
*pAllocation = VK_NULL_HANDLE;
15983
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
15984
*pBuffer = VK_NULL_HANDLE;
15985
return res;
15986
}
15987
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
15988
*pBuffer = VK_NULL_HANDLE;
15989
return res;
15990
}
15991
return res;
15992
}
15993
15994
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
15995
VmaAllocator allocator,
15996
const VkBufferCreateInfo* pBufferCreateInfo,
15997
const VmaAllocationCreateInfo* pAllocationCreateInfo,
15998
VkDeviceSize minAlignment,
15999
VkBuffer* pBuffer,
16000
VmaAllocation* pAllocation,
16001
VmaAllocationInfo* pAllocationInfo)
16002
{
16003
VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
16004
16005
if(pBufferCreateInfo->size == 0)
16006
{
16007
return VK_ERROR_INITIALIZATION_FAILED;
16008
}
16009
if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
16010
!allocator->m_UseKhrBufferDeviceAddress)
16011
{
16012
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.");
16013
return VK_ERROR_INITIALIZATION_FAILED;
16014
}
16015
16016
VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
16017
16018
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16019
16020
*pBuffer = VK_NULL_HANDLE;
16021
*pAllocation = VK_NULL_HANDLE;
16022
16023
// 1. Create VkBuffer.
16024
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16025
allocator->m_hDevice,
16026
pBufferCreateInfo,
16027
allocator->GetAllocationCallbacks(),
16028
pBuffer);
16029
if(res >= 0)
16030
{
16031
// 2. vkGetBufferMemoryRequirements.
16032
VkMemoryRequirements vkMemReq = {};
16033
bool requiresDedicatedAllocation = false;
16034
bool prefersDedicatedAllocation = false;
16035
allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16036
requiresDedicatedAllocation, prefersDedicatedAllocation);
16037
16038
// 2a. Include minAlignment
16039
vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
16040
16041
// 3. Allocate memory using allocator.
16042
res = allocator->AllocateMemory(
16043
vkMemReq,
16044
requiresDedicatedAllocation,
16045
prefersDedicatedAllocation,
16046
*pBuffer, // dedicatedBuffer
16047
VK_NULL_HANDLE, // dedicatedImage
16048
VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage
16049
*pAllocationCreateInfo,
16050
VMA_SUBALLOCATION_TYPE_BUFFER,
16051
1, // allocationCount
16052
pAllocation);
16053
16054
if(res >= 0)
16055
{
16056
// 3. Bind buffer with memory.
16057
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
16058
{
16059
res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
16060
}
16061
if(res >= 0)
16062
{
16063
// All steps succeeded.
16064
#if VMA_STATS_STRING_ENABLED
16065
(*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5);
16066
#endif
16067
if(pAllocationInfo != VMA_NULL)
16068
{
16069
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16070
}
16071
16072
return VK_SUCCESS;
16073
}
16074
allocator->FreeMemory(
16075
1, // allocationCount
16076
pAllocation);
16077
*pAllocation = VK_NULL_HANDLE;
16078
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16079
*pBuffer = VK_NULL_HANDLE;
16080
return res;
16081
}
16082
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16083
*pBuffer = VK_NULL_HANDLE;
16084
return res;
16085
}
16086
return res;
16087
}
16088
16089
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
16090
VmaAllocator VMA_NOT_NULL allocator,
16091
VmaAllocation VMA_NOT_NULL allocation,
16092
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
16093
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
16094
{
16095
return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer);
16096
}
16097
16098
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(
16099
VmaAllocator VMA_NOT_NULL allocator,
16100
VmaAllocation VMA_NOT_NULL allocation,
16101
VkDeviceSize allocationLocalOffset,
16102
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
16103
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
16104
{
16105
VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);
16106
VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize());
16107
16108
VMA_DEBUG_LOG("vmaCreateAliasingBuffer2");
16109
16110
*pBuffer = VK_NULL_HANDLE;
16111
16112
if (pBufferCreateInfo->size == 0)
16113
{
16114
return VK_ERROR_INITIALIZATION_FAILED;
16115
}
16116
if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
16117
!allocator->m_UseKhrBufferDeviceAddress)
16118
{
16119
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.");
16120
return VK_ERROR_INITIALIZATION_FAILED;
16121
}
16122
16123
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16124
16125
// 1. Create VkBuffer.
16126
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16127
allocator->m_hDevice,
16128
pBufferCreateInfo,
16129
allocator->GetAllocationCallbacks(),
16130
pBuffer);
16131
if (res >= 0)
16132
{
16133
// 2. Bind buffer with memory.
16134
res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL);
16135
if (res >= 0)
16136
{
16137
return VK_SUCCESS;
16138
}
16139
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16140
}
16141
return res;
16142
}
16143
16144
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
16145
VmaAllocator allocator,
16146
VkBuffer buffer,
16147
VmaAllocation allocation)
16148
{
16149
VMA_ASSERT(allocator);
16150
16151
if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16152
{
16153
return;
16154
}
16155
16156
VMA_DEBUG_LOG("vmaDestroyBuffer");
16157
16158
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16159
16160
if(buffer != VK_NULL_HANDLE)
16161
{
16162
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16163
}
16164
16165
if(allocation != VK_NULL_HANDLE)
16166
{
16167
allocator->FreeMemory(
16168
1, // allocationCount
16169
&allocation);
16170
}
16171
}
16172
16173
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
16174
VmaAllocator allocator,
16175
const VkImageCreateInfo* pImageCreateInfo,
16176
const VmaAllocationCreateInfo* pAllocationCreateInfo,
16177
VkImage* pImage,
16178
VmaAllocation* pAllocation,
16179
VmaAllocationInfo* pAllocationInfo)
16180
{
16181
VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16182
16183
if(pImageCreateInfo->extent.width == 0 ||
16184
pImageCreateInfo->extent.height == 0 ||
16185
pImageCreateInfo->extent.depth == 0 ||
16186
pImageCreateInfo->mipLevels == 0 ||
16187
pImageCreateInfo->arrayLayers == 0)
16188
{
16189
return VK_ERROR_INITIALIZATION_FAILED;
16190
}
16191
16192
VMA_DEBUG_LOG("vmaCreateImage");
16193
16194
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16195
16196
*pImage = VK_NULL_HANDLE;
16197
*pAllocation = VK_NULL_HANDLE;
16198
16199
// 1. Create VkImage.
16200
VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16201
allocator->m_hDevice,
16202
pImageCreateInfo,
16203
allocator->GetAllocationCallbacks(),
16204
pImage);
16205
if(res >= 0)
16206
{
16207
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16208
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16209
VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16210
16211
// 2. Allocate memory using allocator.
16212
VkMemoryRequirements vkMemReq = {};
16213
bool requiresDedicatedAllocation = false;
16214
bool prefersDedicatedAllocation = false;
16215
allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16216
requiresDedicatedAllocation, prefersDedicatedAllocation);
16217
16218
res = allocator->AllocateMemory(
16219
vkMemReq,
16220
requiresDedicatedAllocation,
16221
prefersDedicatedAllocation,
16222
VK_NULL_HANDLE, // dedicatedBuffer
16223
*pImage, // dedicatedImage
16224
VmaBufferImageUsage(*pImageCreateInfo), // dedicatedBufferImageUsage
16225
*pAllocationCreateInfo,
16226
suballocType,
16227
1, // allocationCount
16228
pAllocation);
16229
16230
if(res >= 0)
16231
{
16232
// 3. Bind image with memory.
16233
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
16234
{
16235
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
16236
}
16237
if(res >= 0)
16238
{
16239
// All steps succeeded.
16240
#if VMA_STATS_STRING_ENABLED
16241
(*pAllocation)->InitImageUsage(*pImageCreateInfo);
16242
#endif
16243
if(pAllocationInfo != VMA_NULL)
16244
{
16245
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16246
}
16247
16248
return VK_SUCCESS;
16249
}
16250
allocator->FreeMemory(
16251
1, // allocationCount
16252
pAllocation);
16253
*pAllocation = VK_NULL_HANDLE;
16254
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16255
*pImage = VK_NULL_HANDLE;
16256
return res;
16257
}
16258
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16259
*pImage = VK_NULL_HANDLE;
16260
return res;
16261
}
16262
return res;
16263
}
16264
16265
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
16266
VmaAllocator VMA_NOT_NULL allocator,
16267
VmaAllocation VMA_NOT_NULL allocation,
16268
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
16269
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
16270
{
16271
return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage);
16272
}
16273
16274
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(
16275
VmaAllocator VMA_NOT_NULL allocator,
16276
VmaAllocation VMA_NOT_NULL allocation,
16277
VkDeviceSize allocationLocalOffset,
16278
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
16279
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
16280
{
16281
VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);
16282
16283
*pImage = VK_NULL_HANDLE;
16284
16285
VMA_DEBUG_LOG("vmaCreateImage2");
16286
16287
if (pImageCreateInfo->extent.width == 0 ||
16288
pImageCreateInfo->extent.height == 0 ||
16289
pImageCreateInfo->extent.depth == 0 ||
16290
pImageCreateInfo->mipLevels == 0 ||
16291
pImageCreateInfo->arrayLayers == 0)
16292
{
16293
return VK_ERROR_INITIALIZATION_FAILED;
16294
}
16295
16296
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16297
16298
// 1. Create VkImage.
16299
VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16300
allocator->m_hDevice,
16301
pImageCreateInfo,
16302
allocator->GetAllocationCallbacks(),
16303
pImage);
16304
if (res >= 0)
16305
{
16306
// 2. Bind image with memory.
16307
res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL);
16308
if (res >= 0)
16309
{
16310
return VK_SUCCESS;
16311
}
16312
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16313
}
16314
return res;
16315
}
16316
16317
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
16318
VmaAllocator VMA_NOT_NULL allocator,
16319
VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
16320
VmaAllocation VMA_NULLABLE allocation)
16321
{
16322
VMA_ASSERT(allocator);
16323
16324
if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16325
{
16326
return;
16327
}
16328
16329
VMA_DEBUG_LOG("vmaDestroyImage");
16330
16331
VMA_DEBUG_GLOBAL_MUTEX_LOCK
16332
16333
if(image != VK_NULL_HANDLE)
16334
{
16335
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16336
}
16337
if(allocation != VK_NULL_HANDLE)
16338
{
16339
allocator->FreeMemory(
16340
1, // allocationCount
16341
&allocation);
16342
}
16343
}
16344
16345
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
16346
const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
16347
VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
16348
{
16349
VMA_ASSERT(pCreateInfo && pVirtualBlock);
16350
VMA_ASSERT(pCreateInfo->size > 0);
16351
VMA_DEBUG_LOG("vmaCreateVirtualBlock");
16352
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16353
*pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
16354
VkResult res = (*pVirtualBlock)->Init();
16355
if(res < 0)
16356
{
16357
vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
16358
*pVirtualBlock = VK_NULL_HANDLE;
16359
}
16360
return res;
16361
}
16362
16363
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
16364
{
16365
if(virtualBlock != VK_NULL_HANDLE)
16366
{
16367
VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
16368
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16369
VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
16370
vma_delete(&allocationCallbacks, virtualBlock);
16371
}
16372
}
16373
16374
VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
16375
{
16376
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
16377
VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
16378
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16379
return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
16380
}
16381
16382
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16383
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
16384
{
16385
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
16386
VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
16387
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16388
virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);
16389
}
16390
16391
VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16392
const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
16393
VkDeviceSize* VMA_NULLABLE pOffset)
16394
{
16395
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);
16396
VMA_DEBUG_LOG("vmaVirtualAllocate");
16397
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16398
return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);
16399
}
16400
16401
VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)
16402
{
16403
if(allocation != VK_NULL_HANDLE)
16404
{
16405
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
16406
VMA_DEBUG_LOG("vmaVirtualFree");
16407
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16408
virtualBlock->Free(allocation);
16409
}
16410
}
16411
16412
VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
16413
{
16414
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
16415
VMA_DEBUG_LOG("vmaClearVirtualBlock");
16416
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16417
virtualBlock->Clear();
16418
}
16419
16420
VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16421
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)
16422
{
16423
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
16424
VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
16425
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16426
virtualBlock->SetAllocationUserData(allocation, pUserData);
16427
}
16428
16429
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16430
VmaStatistics* VMA_NOT_NULL pStats)
16431
{
16432
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
16433
VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");
16434
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16435
virtualBlock->GetStatistics(*pStats);
16436
}
16437
16438
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16439
VmaDetailedStatistics* VMA_NOT_NULL pStats)
16440
{
16441
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
16442
VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");
16443
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16444
virtualBlock->CalculateDetailedStatistics(*pStats);
16445
}
16446
16447
#if VMA_STATS_STRING_ENABLED
16448
16449
VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16450
char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
16451
{
16452
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
16453
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16454
const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
16455
VmaStringBuilder sb(allocationCallbacks);
16456
virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
16457
*ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
16458
}
16459
16460
VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
16461
char* VMA_NULLABLE pStatsString)
16462
{
16463
if(pStatsString != VMA_NULL)
16464
{
16465
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
16466
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16467
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
16468
}
16469
}
16470
#endif // VMA_STATS_STRING_ENABLED
16471
#endif // _VMA_PUBLIC_INTERFACE
16472
#endif // VMA_IMPLEMENTATION
16473
16474
/**
16475
\page quick_start Quick start
16476
16477
\section quick_start_project_setup Project setup
16478
16479
Vulkan Memory Allocator comes in form of a "stb-style" single header file.
16480
While you can pull the entire repository e.g. as Git module, there is also Cmake script provided,
16481
you don't need to build it as a separate library project.
16482
You can add file "vk_mem_alloc.h" directly to your project and submit it to code repository next to your other source files.
16483
16484
"Single header" doesn't mean that everything is contained in C/C++ declarations,
16485
like it tends to be in case of inline functions or C++ templates.
16486
It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
16487
If you don't do it properly, it will result in linker errors.
16488
16489
To do it properly:
16490
16491
-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
16492
This includes declarations of all members of the library.
16493
-# In exactly one CPP file define following macro before this include.
16494
It enables also internal definitions.
16495
16496
\code
16497
#define VMA_IMPLEMENTATION
16498
#include "vk_mem_alloc.h"
16499
\endcode
16500
16501
It may be a good idea to create dedicated CPP file just for this purpose, e.g. "VmaUsage.cpp".
16502
16503
This library includes header `<vulkan/vulkan.h>`, which in turn
16504
includes `<windows.h>` on Windows. If you need some specific macros defined
16505
before including these headers (like `WIN32_LEAN_AND_MEAN` or
16506
`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
16507
them before every `#include` of this library.
16508
It may be a good idea to create a dedicate header file for this purpose, e.g. "VmaUsage.h",
16509
that will be included in other source files instead of VMA header directly.
16510
16511
This library is written in C++, but has C-compatible interface.
16512
Thus, you can include and use "vk_mem_alloc.h" in C or C++ code, but full
16513
implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
16514
Some features of C++14 are used and required. Features of C++20 are used optionally when available.
16515
Some headers of standard C and C++ library are used, but STL containers, RTTI, or C++ exceptions are not used.
16516
16517
16518
\section quick_start_initialization Initialization
16519
16520
VMA offers library interface in a style similar to Vulkan, with object handles like #VmaAllocation,
16521
structures describing parameters of objects to be created like #VmaAllocationCreateInfo,
16522
and errors codes returned from functions using `VkResult` type.
16523
16524
The first and the main object that needs to be created is #VmaAllocator.
16525
It represents the initialization of the entire library.
16526
Only one such object should be created per `VkDevice`.
16527
You should create it at program startup, after `VkDevice` was created, and before any device memory allocator needs to be made.
16528
It must be destroyed before `VkDevice` is destroyed.
16529
16530
At program startup:
16531
16532
-# Initialize Vulkan to have `VkInstance`, `VkPhysicalDevice`, `VkDevice` object.
16533
-# Fill VmaAllocatorCreateInfo structure and call vmaCreateAllocator() to create #VmaAllocator object.
16534
16535
Only members `physicalDevice`, `device`, `instance` are required.
16536
However, you should inform the library which Vulkan version do you use by setting
16537
VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable
16538
by setting VmaAllocatorCreateInfo::flags.
16539
Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.
16540
See below for details.
16541
16542
\subsection quick_start_initialization_selecting_vulkan_version Selecting Vulkan version
16543
16544
VMA supports Vulkan version down to 1.0, for backward compatibility.
16545
If you want to use higher version, you need to inform the library about it.
16546
This is a two-step process.
16547
16548
<b>Step 1: Compile time.</b> By default, VMA compiles with code supporting the highest
16549
Vulkan version found in the included `<vulkan/vulkan.h>` that is also supported by the library.
16550
If this is OK, you don't need to do anything.
16551
However, if you want to compile VMA as if only some lower Vulkan version was available,
16552
define macro `VMA_VULKAN_VERSION` before every `#include "vk_mem_alloc.h"`.
16553
It should have decimal numeric value in form of ABBBCCC, where A = major, BBB = minor, CCC = patch Vulkan version.
16554
For example, to compile against Vulkan 1.2:
16555
16556
\code
16557
#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2
16558
#include "vk_mem_alloc.h"
16559
\endcode
16560
16561
<b>Step 2: Runtime.</b> Even when compiled with higher Vulkan version available,
16562
VMA can use only features of a lower version, which is configurable during creation of the #VmaAllocator object.
16563
By default, only Vulkan 1.0 is used.
16564
To initialize the allocator with support for higher Vulkan version, you need to set member
16565
VmaAllocatorCreateInfo::vulkanApiVersion to an appropriate value, e.g. using constants like `VK_API_VERSION_1_2`.
16566
See code sample below.
16567
16568
\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions
16569
16570
You may need to configure importing Vulkan functions. There are 3 ways to do this:
16571
16572
-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):
16573
- You don't need to do anything.
16574
- VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default.
16575
-# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`,
16576
`vkGetDeviceProcAddr` (this is the option presented in the example below):
16577
- Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1.
16578
- Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,
16579
VmaVulkanFunctions::vkGetDeviceProcAddr.
16580
- The library will fetch pointers to all other functions it needs internally.
16581
-# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like
16582
[Volk](https://github.com/zeux/volk):
16583
- Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.
16584
- Pass these pointers via structure #VmaVulkanFunctions.
16585
16586
\subsection quick_start_initialization_enabling_extensions Enabling extensions
16587
16588
VMA can automatically use following Vulkan extensions.
16589
If you found them available on the selected physical device and you enabled them
16590
while creating `VkInstance` / `VkDevice` object, inform VMA about their availability
16591
by setting appropriate flags in VmaAllocatorCreateInfo::flags.
16592
16593
Vulkan extension | VMA flag
16594
------------------------------|-----------------------------------------------------
16595
VK_KHR_dedicated_allocation | #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
16596
VK_KHR_bind_memory2 | #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
16597
VK_KHR_maintenance4 | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT
16598
VK_KHR_maintenance5 | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT
16599
VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
16600
VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
16601
VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
16602
VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
16603
16604
Example with fetching pointers to Vulkan functions dynamically:
16605
16606
\code
16607
#define VMA_STATIC_VULKAN_FUNCTIONS 0
16608
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
16609
#include "vk_mem_alloc.h"
16610
16611
...
16612
16613
VmaVulkanFunctions vulkanFunctions = {};
16614
vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
16615
vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
16616
16617
VmaAllocatorCreateInfo allocatorCreateInfo = {};
16618
allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
16619
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
16620
allocatorCreateInfo.physicalDevice = physicalDevice;
16621
allocatorCreateInfo.device = device;
16622
allocatorCreateInfo.instance = instance;
16623
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
16624
16625
VmaAllocator allocator;
16626
vmaCreateAllocator(&allocatorCreateInfo, &allocator);
16627
16628
// Entire program...
16629
16630
// At the end, don't forget to:
16631
vmaDestroyAllocator(allocator);
16632
\endcode
16633
16634
16635
\subsection quick_start_initialization_other_config Other configuration options
16636
16637
There are additional configuration options available through preprocessor macros that you can define
16638
before including VMA header and through parameters passed in #VmaAllocatorCreateInfo.
16639
They include a possibility to use your own callbacks for host memory allocations (`VkAllocationCallbacks`),
16640
callbacks for device memory allocations (instead of `vkAllocateMemory`, `vkFreeMemory`),
16641
or your custom `VMA_ASSERT` macro, among others.
16642
For more information, see: @ref configuration.
16643
16644
16645
\section quick_start_resource_allocation Resource allocation
16646
16647
When you want to create a buffer or image:
16648
16649
-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
16650
-# Fill VmaAllocationCreateInfo structure.
16651
-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
16652
already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory.
16653
16654
\code
16655
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
16656
bufferInfo.size = 65536;
16657
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
16658
16659
VmaAllocationCreateInfo allocInfo = {};
16660
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
16661
16662
VkBuffer buffer;
16663
VmaAllocation allocation;
16664
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
16665
\endcode
16666
16667
Don't forget to destroy your buffer and allocation objects when no longer needed:
16668
16669
\code
16670
vmaDestroyBuffer(allocator, buffer, allocation);
16671
\endcode
16672
16673
If you need to map the buffer, you must set flag
16674
#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
16675
in VmaAllocationCreateInfo::flags.
16676
There are many additional parameters that can control the choice of memory type to be used for the allocation
16677
and other features.
16678
For more information, see documentation chapters: @ref choosing_memory_type, @ref memory_mapping.
16679
16680
16681
\page choosing_memory_type Choosing memory type
16682
16683
Physical devices in Vulkan support various combinations of memory heaps and
16684
types. Help with choosing correct and optimal memory type for your specific
16685
resource is one of the key features of this library. You can use it by filling
16686
appropriate members of VmaAllocationCreateInfo structure, as described below.
16687
You can also combine multiple methods.
16688
16689
-# If you just want to find memory type index that meets your requirements, you
16690
can use function: vmaFindMemoryTypeIndexForBufferInfo(),
16691
vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex().
16692
-# If you want to allocate a region of device memory without association with any
16693
specific image or buffer, you can use function vmaAllocateMemory(). Usage of
16694
this function is not recommended and usually not needed.
16695
vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
16696
which may be useful for sparse binding.
16697
-# If you already have a buffer or an image created, you want to allocate memory
16698
for it and then you will bind it yourself, you can use function
16699
vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
16700
For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
16701
or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
16702
-# If you want to create a buffer or an image, allocate memory for it, and bind
16703
them together, all in one call, you can use function vmaCreateBuffer(),
16704
vmaCreateImage().
16705
<b>This is the easiest and recommended way to use this library!</b>
16706
16707
When using 3. or 4., the library internally queries Vulkan for memory types
16708
supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
16709
and uses only one of these types.
16710
16711
If no memory type can be found that meets all the requirements, these functions
16712
return `VK_ERROR_FEATURE_NOT_PRESENT`.
16713
16714
You can leave VmaAllocationCreateInfo structure completely filled with zeros.
16715
It means no requirements are specified for memory type.
16716
It is valid, although not very useful.
16717
16718
\section choosing_memory_type_usage Usage
16719
16720
The easiest way to specify memory requirements is to fill member
16721
VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
16722
It defines high level, common usage types.
16723
Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.
16724
16725
For example, if you want to create a uniform buffer that will be filled using
16726
transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can
16727
do it using following code. The buffer will most likely end up in a memory type with
16728
`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device.
16729
16730
\code
16731
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
16732
bufferInfo.size = 65536;
16733
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
16734
16735
VmaAllocationCreateInfo allocInfo = {};
16736
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
16737
16738
VkBuffer buffer;
16739
VmaAllocation allocation;
16740
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
16741
\endcode
16742
16743
If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory
16744
on systems with discrete graphics card that have the memories separate, you can use
16745
#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST.
16746
16747
When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory,
16748
you also need to specify one of the host access flags:
16749
#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
16750
This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
16751
so you can map it.
16752
16753
For example, a staging buffer that will be filled via mapped pointer and then
16754
used as a source of transfer to the buffer described previously can be created like this.
16755
It will likely end up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT`
16756
but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM).
16757
16758
\code
16759
VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
16760
stagingBufferInfo.size = 65536;
16761
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
16762
16763
VmaAllocationCreateInfo stagingAllocInfo = {};
16764
stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
16765
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
16766
16767
VkBuffer stagingBuffer;
16768
VmaAllocation stagingAllocation;
16769
vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);
16770
\endcode
16771
16772
For more examples of creating different kinds of resources, see chapter \ref usage_patterns.
16773
See also: @ref memory_mapping.
16774
16775
Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows
16776
about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed,
16777
so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc.
16778
If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting
16779
memory type, as described below.
16780
16781
\note
16782
Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`,
16783
`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`)
16784
are still available and work same way as in previous versions of the library
16785
for backward compatibility, but they are deprecated.
16786
16787
\section choosing_memory_type_required_preferred_flags Required and preferred flags
16788
16789
You can specify more detailed requirements by filling members
16790
VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
16791
with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
16792
if you want to create a buffer that will be persistently mapped on host (so it
16793
must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
16794
use following code:
16795
16796
\code
16797
VmaAllocationCreateInfo allocInfo = {};
16798
allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
16799
allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
16800
allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
16801
16802
VkBuffer buffer;
16803
VmaAllocation allocation;
16804
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
16805
\endcode
16806
16807
A memory type is chosen that has all the required flags and as many preferred
16808
flags set as possible.
16809
16810
Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags,
16811
plus some extra "magic" (heuristics).
16812
16813
\section choosing_memory_type_explicit_memory_types Explicit memory types
16814
16815
If you inspected memory types available on the physical device and <b>you have
16816
a preference for memory types that you want to use</b>, you can fill member
16817
VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
16818
means that a memory type with that index is allowed to be used for the
16819
allocation. Special value 0, just like `UINT32_MAX`, means there are no
16820
restrictions to memory type index.
16821
16822
Please note that this member is NOT just a memory type index.
16823
Still you can use it to choose just one, specific memory type.
16824
For example, if you already determined that your buffer should be created in
16825
memory type 2, use following code:
16826
16827
\code
16828
uint32_t memoryTypeIndex = 2;
16829
16830
VmaAllocationCreateInfo allocInfo = {};
16831
allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
16832
16833
VkBuffer buffer;
16834
VmaAllocation allocation;
16835
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
16836
\endcode
16837
16838
You can also use this parameter to <b>exclude some memory types</b>.
16839
If you inspect memory heaps and types available on the current physical device and
16840
you determine that for some reason you don't want to use a specific memory type for the allocation,
16841
you can enable automatic memory type selection but exclude certain memory type or types
16842
by setting all bits of `memoryTypeBits` to 1 except the ones you choose.
16843
16844
\code
16845
// ...
16846
uint32_t excludedMemoryTypeIndex = 2;
16847
VmaAllocationCreateInfo allocInfo = {};
16848
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
16849
allocInfo.memoryTypeBits = ~(1u << excludedMemoryTypeIndex);
16850
// ...
16851
\endcode
16852
16853
16854
\section choosing_memory_type_custom_memory_pools Custom memory pools
16855
16856
If you allocate from custom memory pool, all the ways of specifying memory
16857
requirements described above are not applicable and the aforementioned members
16858
of VmaAllocationCreateInfo structure are ignored. Memory type is selected
16859
explicitly when creating the pool and then used to make all the allocations from
16860
that pool. For further details, see \ref custom_memory_pools.
16861
16862
\section choosing_memory_type_dedicated_allocations Dedicated allocations
16863
16864
Memory for allocations is reserved out of larger block of `VkDeviceMemory`
16865
allocated from Vulkan internally. That is the main feature of this whole library.
16866
You can still request a separate memory block to be created for an allocation,
16867
just like you would do in a trivial solution without using any allocator.
16868
In that case, a buffer or image is always bound to that memory at offset 0.
16869
This is called a "dedicated allocation".
16870
You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
16871
The library can also internally decide to use dedicated allocation in some cases, e.g.:
16872
16873
- When the size of the allocation is large.
16874
- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
16875
and it reports that dedicated allocation is required or recommended for the resource.
16876
- When allocation of next big memory block fails due to not enough device memory,
16877
but allocation with the exact requested size succeeds.
16878
16879
16880
\page memory_mapping Memory mapping
16881
16882
To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
16883
to be able to read from it or write to it in CPU code.
16884
Mapping is possible only of memory allocated from a memory type that has
16885
`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
16886
Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
16887
You can use them directly with memory allocated by this library,
16888
but it is not recommended because of following issue:
16889
Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
16890
This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
16891
It is also not thread-safe.
16892
Because of this, Vulkan Memory Allocator provides following facilities:
16893
16894
\note If you want to be able to map an allocation, you need to specify one of the flags
16895
#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
16896
in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable
16897
when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.
16898
For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,
16899
but these flags can still be used for consistency.
16900
16901
\section memory_mapping_copy_functions Copy functions
16902
16903
The easiest way to copy data from a host pointer to an allocation is to use convenience function vmaCopyMemoryToAllocation().
16904
It automatically maps the Vulkan memory temporarily (if not already mapped), performs `memcpy`,
16905
and calls `vkFlushMappedMemoryRanges` (if required - if memory type is not `HOST_COHERENT`).
16906
16907
It is also the safest one, because using `memcpy` avoids a risk of accidentally introducing memory reads
16908
(e.g. by doing `pMappedVectors[i] += v`), which may be very slow on memory types that are not `HOST_CACHED`.
16909
16910
\code
16911
struct ConstantBuffer
16912
{
16913
...
16914
};
16915
ConstantBuffer constantBufferData = ...
16916
16917
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
16918
bufCreateInfo.size = sizeof(ConstantBuffer);
16919
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
16920
16921
VmaAllocationCreateInfo allocCreateInfo = {};
16922
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
16923
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
16924
16925
VkBuffer buf;
16926
VmaAllocation alloc;
16927
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
16928
16929
vmaCopyMemoryToAllocation(allocator, &constantBufferData, alloc, 0, sizeof(ConstantBuffer));
16930
\endcode
16931
16932
Copy in the other direction - from an allocation to a host pointer can be performed the same way using function vmaCopyAllocationToMemory().
16933
16934
\section memory_mapping_mapping_functions Mapping functions
16935
16936
The library provides following functions for mapping of a specific allocation: vmaMapMemory(), vmaUnmapMemory().
16937
They are safer and more convenient to use than standard Vulkan functions.
16938
You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
16939
You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
16940
The way it is implemented is that the library always maps entire memory block, not just region of the allocation.
16941
For further details, see description of vmaMapMemory() function.
16942
Example:
16943
16944
\code
16945
// Having these objects initialized:
16946
struct ConstantBuffer
16947
{
16948
...
16949
};
16950
ConstantBuffer constantBufferData = ...
16951
16952
VmaAllocator allocator = ...
16953
VkBuffer constantBuffer = ...
16954
VmaAllocation constantBufferAllocation = ...
16955
16956
// You can map and fill your buffer using following code:
16957
16958
void* mappedData;
16959
vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
16960
memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
16961
vmaUnmapMemory(allocator, constantBufferAllocation);
16962
\endcode
16963
16964
When mapping, you may see a warning from Vulkan validation layer similar to this one:
16965
16966
<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>
16967
16968
It happens because the library maps entire `VkDeviceMemory` block, where different
16969
types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
16970
You can safely ignore it if you are sure you access only memory of the intended
16971
object that you wanted to map.
16972
16973
16974
\section memory_mapping_persistently_mapped_memory Persistently mapped memory
16975
16976
Keeping your memory persistently mapped is generally OK in Vulkan.
16977
You don't need to unmap it before using its data on the GPU.
16978
The library provides a special feature designed for that:
16979
Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
16980
VmaAllocationCreateInfo::flags stay mapped all the time,
16981
so you can just access CPU pointer to it any time
16982
without a need to call any "map" or "unmap" function.
16983
Example:
16984
16985
\code
16986
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
16987
bufCreateInfo.size = sizeof(ConstantBuffer);
16988
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
16989
16990
VmaAllocationCreateInfo allocCreateInfo = {};
16991
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
16992
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
16993
VMA_ALLOCATION_CREATE_MAPPED_BIT;
16994
16995
VkBuffer buf;
16996
VmaAllocation alloc;
16997
VmaAllocationInfo allocInfo;
16998
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
16999
17000
// Buffer is already mapped. You can access its memory.
17001
memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
17002
\endcode
17003
17004
\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up
17005
in a mappable memory type.
17006
For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or
17007
#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
17008
#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation.
17009
For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading.
17010
17011
\section memory_mapping_cache_control Cache flush and invalidate
17012
17013
Memory in Vulkan doesn't need to be unmapped before using it on GPU,
17014
but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
17015
you need to manually **invalidate** cache before reading of mapped pointer
17016
and **flush** cache after writing to mapped pointer.
17017
Map/unmap operations don't do that automatically.
17018
Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
17019
`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
17020
functions that refer to given allocation object: vmaFlushAllocation(),
17021
vmaInvalidateAllocation(),
17022
or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
17023
17024
Regions of memory specified for flush/invalidate must be aligned to
17025
`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
17026
In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
17027
within blocks are aligned to this value, so their offsets are always multiply of
17028
`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
17029
17030
Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
17031
currently provide `HOST_COHERENT` flag on all memory types that are
17032
`HOST_VISIBLE`, so on PC you may not need to bother.
17033
17034
17035
\page staying_within_budget Staying within budget
17036
17037
When developing a graphics-intensive game or program, it is important to avoid allocating
17038
more GPU memory than it is physically available. When the memory is over-committed,
17039
various bad things can happen, depending on the specific GPU, graphics driver, and
17040
operating system:
17041
17042
- It may just work without any problems.
17043
- The application may slow down because some memory blocks are moved to system RAM
17044
and the GPU has to access them through PCI Express bus.
17045
- A new allocation may take very long time to complete, even few seconds, and possibly
17046
freeze entire system.
17047
- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
17048
- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
17049
returned somewhere later.
17050
17051
\section staying_within_budget_querying_for_budget Querying for budget
17052
17053
To query for current memory usage and available budget, use function vmaGetHeapBudgets().
17054
Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
17055
17056
Please note that this function returns different information and works faster than
17057
vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every
17058
allocation, while vmaCalculateStatistics() is intended to be used rarely,
17059
only to obtain statistical information, e.g. for debugging purposes.
17060
17061
It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
17062
about the budget from Vulkan device. VMA is able to use this extension automatically.
17063
When not enabled, the allocator behaves same way, but then it estimates current usage
17064
and available budget based on its internal information and Vulkan memory heap sizes,
17065
which may be less precise. In order to use this extension:
17066
17067
1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
17068
required by it are available and enable them. Please note that the first is a device
17069
extension and the second is instance extension!
17070
2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
17071
3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
17072
Vulkan inside of it to avoid overhead of querying it with every allocation.
17073
17074
\section staying_within_budget_controlling_memory_usage Controlling memory usage
17075
17076
There are many ways in which you can try to stay within the budget.
17077
17078
First, when making new allocation requires allocating a new memory block, the library
17079
tries not to exceed the budget automatically. If a block with default recommended size
17080
(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
17081
dedicated memory for just this resource.
17082
17083
If the size of the requested resource plus current memory usage is more than the
17084
budget, by default the library still tries to create it, leaving it to the Vulkan
17085
implementation whether the allocation succeeds or fails. You can change this behavior
17086
by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
17087
not made if it would exceed the budget or if the budget is already exceeded.
17088
VMA then tries to make the allocation from the next eligible Vulkan memory type.
17089
The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
17090
Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
17091
when creating resources that are not essential for the application (e.g. the texture
17092
of a specific object) and not to pass it when creating critically important resources
17093
(e.g. render targets).
17094
17095
On AMD graphics cards there is a custom vendor extension available: <b>VK_AMD_memory_overallocation_behavior</b>
17096
that allows to control the behavior of the Vulkan implementation in out-of-memory cases -
17097
whether it should fail with an error code or still allow the allocation.
17098
Usage of this extension involves only passing extra structure on Vulkan device creation,
17099
so it is out of scope of this library.
17100
17101
Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
17102
a new allocation is created only when it fits inside one of the existing memory blocks.
17103
If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
17104
This also ensures that the function call is very fast because it never goes to Vulkan
17105
to obtain a new block.
17106
17107
\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
17108
set to more than 0 will currently try to allocate memory blocks without checking whether they
17109
fit within budget.
17110
17111
17112
\page resource_aliasing Resource aliasing (overlap)
17113
17114
New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
17115
management, give an opportunity to alias (overlap) multiple resources in the
17116
same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
17117
It can be useful to save video memory, but it must be used with caution.
17118
17119
For example, if you know the flow of your whole render frame in advance, you
17120
are going to use some intermediate textures or buffers only during a small range of render passes,
17121
and you know these ranges don't overlap in time, you can bind these resources to
17122
the same place in memory, even if they have completely different parameters (width, height, format etc.).
17123
17124
![Resource aliasing (overlap)](../gfx/Aliasing.png)
17125
17126
Such scenario is possible using VMA, but you need to create your images manually.
17127
Then you need to calculate parameters of an allocation to be made using formula:
17128
17129
- allocation size = max(size of each image)
17130
- allocation alignment = max(alignment of each image)
17131
- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)
17132
17133
Following example shows two different images bound to the same place in memory,
17134
allocated to fit largest of them.
17135
17136
\code
17137
// A 512x512 texture to be sampled.
17138
VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
17139
img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
17140
img1CreateInfo.extent.width = 512;
17141
img1CreateInfo.extent.height = 512;
17142
img1CreateInfo.extent.depth = 1;
17143
img1CreateInfo.mipLevels = 10;
17144
img1CreateInfo.arrayLayers = 1;
17145
img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
17146
img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
17147
img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
17148
img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
17149
img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
17150
17151
// A full screen texture to be used as color attachment.
17152
VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
17153
img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
17154
img2CreateInfo.extent.width = 1920;
17155
img2CreateInfo.extent.height = 1080;
17156
img2CreateInfo.extent.depth = 1;
17157
img2CreateInfo.mipLevels = 1;
17158
img2CreateInfo.arrayLayers = 1;
17159
img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
17160
img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
17161
img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
17162
img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
17163
img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
17164
17165
VkImage img1;
17166
res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);
17167
VkImage img2;
17168
res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);
17169
17170
VkMemoryRequirements img1MemReq;
17171
vkGetImageMemoryRequirements(device, img1, &img1MemReq);
17172
VkMemoryRequirements img2MemReq;
17173
vkGetImageMemoryRequirements(device, img2, &img2MemReq);
17174
17175
VkMemoryRequirements finalMemReq = {};
17176
finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
17177
finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
17178
finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
17179
// Validate if(finalMemReq.memoryTypeBits != 0)
17180
17181
VmaAllocationCreateInfo allocCreateInfo = {};
17182
allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17183
17184
VmaAllocation alloc;
17185
res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);
17186
17187
res = vmaBindImageMemory(allocator, alloc, img1);
17188
res = vmaBindImageMemory(allocator, alloc, img2);
17189
17190
// You can use img1, img2 here, but not at the same time!
17191
17192
vmaFreeMemory(allocator, alloc);
17193
vkDestroyImage(allocator, img2, nullptr);
17194
vkDestroyImage(allocator, img1, nullptr);
17195
\endcode
17196
17197
VMA also provides convenience functions that create a buffer or image and bind it to memory
17198
represented by an existing #VmaAllocation:
17199
vmaCreateAliasingBuffer(), vmaCreateAliasingBuffer2(),
17200
vmaCreateAliasingImage(), vmaCreateAliasingImage2().
17201
Versions with "2" offer additional parameter `allocationLocalOffset`.
17202
17203
Remember that using resources that alias in memory requires proper synchronization.
17204
You need to issue a memory barrier to make sure commands that use `img1` and `img2`
17205
don't overlap on GPU timeline.
17206
You also need to treat a resource after aliasing as uninitialized - containing garbage data.
17207
For example, if you use `img1` and then want to use `img2`, you need to issue
17208
an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.
17209
17210
Additional considerations:
17211
17212
- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.
17213
See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.
17214
- You can create more complex layout where different images and buffers are bound
17215
at different offsets inside one large allocation. For example, one can imagine
17216
a big texture used in some render passes, aliasing with a set of many small buffers
17217
used between in some further passes. To bind a resource at non-zero offset in an allocation,
17218
use vmaBindBufferMemory2() / vmaBindImageMemory2().
17219
- Before allocating memory for the resources you want to alias, check `memoryTypeBits`
17220
returned in memory requirements of each resource to make sure the bits overlap.
17221
Some GPUs may expose multiple memory types suitable e.g. only for buffers or
17222
images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your
17223
resources may be disjoint. Aliasing them is not possible in that case.
17224
17225
17226
\page custom_memory_pools Custom memory pools
17227
17228
A memory pool contains a number of `VkDeviceMemory` blocks.
17229
The library automatically creates and manages default pool for each memory type available on the device.
17230
Default memory pool automatically grows in size.
17231
Size of allocated blocks is also variable and managed automatically.
17232
You are using default pools whenever you leave VmaAllocationCreateInfo::pool = null.
17233
17234
You can create custom pool and allocate memory out of it.
17235
It can be useful if you want to:
17236
17237
- Keep certain kind of allocations separate from others.
17238
- Enforce particular, fixed size of Vulkan memory blocks.
17239
- Limit maximum amount of Vulkan memory allocated for that pool.
17240
- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
17241
- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in
17242
#VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.
17243
- Perform defragmentation on a specific subset of your allocations.
17244
17245
To use custom memory pools:
17246
17247
-# Fill VmaPoolCreateInfo structure.
17248
-# Call vmaCreatePool() to obtain #VmaPool handle.
17249
-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
17250
You don't need to specify any other parameters of this structure, like `usage`.
17251
17252
Example:
17253
17254
\code
17255
// Find memoryTypeIndex for the pool.
17256
VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17257
sampleBufCreateInfo.size = 0x10000; // Doesn't matter.
17258
sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17259
17260
VmaAllocationCreateInfo sampleAllocCreateInfo = {};
17261
sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
17262
17263
uint32_t memTypeIndex;
17264
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator,
17265
&sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex);
17266
// Check res...
17267
17268
// Create a pool that can have at most 2 blocks, 128 MiB each.
17269
VmaPoolCreateInfo poolCreateInfo = {};
17270
poolCreateInfo.memoryTypeIndex = memTypeIndex;
17271
poolCreateInfo.blockSize = 128ull * 1024 * 1024;
17272
poolCreateInfo.maxBlockCount = 2;
17273
17274
VmaPool pool;
17275
res = vmaCreatePool(allocator, &poolCreateInfo, &pool);
17276
// Check res...
17277
17278
// Allocate a buffer out of it.
17279
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17280
bufCreateInfo.size = 1024;
17281
bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17282
17283
VmaAllocationCreateInfo allocCreateInfo = {};
17284
allocCreateInfo.pool = pool;
17285
17286
VkBuffer buf;
17287
VmaAllocation alloc;
17288
res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
17289
// Check res...
17290
\endcode
17291
17292
You have to free all allocations made from this pool before destroying it.
17293
17294
\code
17295
vmaDestroyBuffer(allocator, buf, alloc);
17296
vmaDestroyPool(allocator, pool);
17297
\endcode
17298
17299
New versions of this library support creating dedicated allocations in custom pools.
17300
It is supported only when VmaPoolCreateInfo::blockSize = 0.
17301
To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and
17302
VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
17303
17304
17305
\section custom_memory_pools_MemTypeIndex Choosing memory type index
17306
17307
When creating a pool, you must explicitly specify memory type index.
17308
To find the one suitable for your buffers or images, you can use helper functions
17309
vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
17310
You need to provide structures with example parameters of buffers or images
17311
that you are going to create in that pool.
17312
17313
\code
17314
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17315
exampleBufCreateInfo.size = 1024; // Doesn't matter
17316
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17317
17318
VmaAllocationCreateInfo allocCreateInfo = {};
17319
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
17320
17321
uint32_t memTypeIndex;
17322
vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
17323
17324
VmaPoolCreateInfo poolCreateInfo = {};
17325
poolCreateInfo.memoryTypeIndex = memTypeIndex;
17326
// ...
17327
\endcode
17328
17329
When creating buffers/images allocated in that pool, provide following parameters:
17330
17331
- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
17332
Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
17333
Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
17334
or the other way around.
17335
- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
17336
Other members are ignored anyway.
17337
17338
17339
\section custom_memory_pools_when_not_use When not to use custom pools
17340
17341
Custom pools are commonly overused by VMA users.
17342
While it may feel natural to keep some logical groups of resources separate in memory,
17343
in most cases it does more harm than good.
17344
Using custom pool shouldn't be your first choice.
17345
Instead, please make all allocations from default pools first and only use custom pools
17346
if you can prove and measure that it is beneficial in some way,
17347
e.g. it results in lower memory usage, better performance, etc.
17348
17349
Using custom pools has disadvantages:
17350
17351
- Each pool has its own collection of `VkDeviceMemory` blocks.
17352
Some of them may be partially or even completely empty.
17353
Spreading allocations across multiple pools increases the amount of wasted (allocated but unbound) memory.
17354
- You must manually choose specific memory type to be used by a custom pool (set as VmaPoolCreateInfo::memoryTypeIndex).
17355
When using default pools, best memory type for each of your allocations can be selected automatically
17356
using a carefully design algorithm that works across all kinds of GPUs.
17357
- If an allocation from a custom pool at specific memory type fails, entire allocation operation returns failure.
17358
When using default pools, VMA tries another compatible memory type.
17359
- If you set VmaPoolCreateInfo::blockSize != 0, each memory block has the same size,
17360
while default pools start from small blocks and only allocate next blocks larger and larger
17361
up to the preferred block size.
17362
17363
Many of the common concerns can be addressed in a different way than using custom pools:
17364
17365
- If you want to keep your allocations of certain size (small versus large) or certain lifetime (transient versus long lived)
17366
separate, you likely don't need to.
17367
VMA uses a high quality allocation algorithm that manages memory well in various cases.
17368
Please measure and check if using custom pools provides a benefit.
17369
- If you want to keep your images and buffers separate, you don't need to.
17370
VMA respects `bufferImageGranularity` limit automatically.
17371
- If you want to keep your mapped and not mapped allocations separate, you don't need to.
17372
VMA respects `nonCoherentAtomSize` limit automatically.
17373
It also maps only those `VkDeviceMemory` blocks that need to map any allocation.
17374
It even tries to keep mappable and non-mappable allocations in separate blocks to minimize the amount of mapped memory.
17375
- If you want to choose a custom size for the default memory block, you can set it globally instead
17376
using VmaAllocatorCreateInfo::preferredLargeHeapBlockSize.
17377
- If you want to select specific memory type for your allocation,
17378
you can set VmaAllocationCreateInfo::memoryTypeBits to `(1u << myMemoryTypeIndex)` instead.
17379
- If you need to create a buffer with certain minimum alignment, you can still do it
17380
using default pools with dedicated function vmaCreateBufferWithAlignment().
17381
17382
17383
\section linear_algorithm Linear allocation algorithm
17384
17385
Each Vulkan memory block managed by this library has accompanying metadata that
17386
keeps track of used and unused regions. By default, the metadata structure and
17387
algorithm tries to find best place for new allocations among free regions to
17388
optimize memory usage. This way you can allocate and free objects in any order.
17389
17390
![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
17391
17392
Sometimes there is a need to use simpler, linear allocation algorithm. You can
17393
create custom pool that uses such algorithm by adding flag
17394
#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
17395
#VmaPool object. Then an alternative metadata management is used. It always
17396
creates new allocations after last one and doesn't reuse free regions after
17397
allocations freed in the middle. It results in better allocation performance and
17398
less memory consumed by metadata.
17399
17400
![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
17401
17402
With this one flag, you can create a custom pool that can be used in many ways:
17403
free-at-once, stack, double stack, and ring buffer. See below for details.
17404
You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
17405
17406
\subsection linear_algorithm_free_at_once Free-at-once
17407
17408
In a pool that uses linear algorithm, you still need to free all the allocations
17409
individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
17410
them in any order. New allocations are always made after last one - free space
17411
in the middle is not reused. However, when you release all the allocation and
17412
the pool becomes empty, allocation starts from the beginning again. This way you
17413
can use linear algorithm to speed up creation of allocations that you are going
17414
to release all at once.
17415
17416
![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
17417
17418
This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
17419
value that allows multiple memory blocks.
17420
17421
\subsection linear_algorithm_stack Stack
17422
17423
When you free an allocation that was created last, its space can be reused.
17424
Thanks to this, if you always release allocations in the order opposite to their
17425
creation (LIFO - Last In First Out), you can achieve behavior of a stack.
17426
17427
![Stack](../gfx/Linear_allocator_4_stack.png)
17428
17429
This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
17430
value that allows multiple memory blocks.
17431
17432
\subsection linear_algorithm_double_stack Double stack
17433
17434
The space reserved by a custom pool with linear algorithm may be used by two
17435
stacks:
17436
17437
- First, default one, growing up from offset 0.
17438
- Second, "upper" one, growing down from the end towards lower offsets.
17439
17440
To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
17441
to VmaAllocationCreateInfo::flags.
17442
17443
![Double stack](../gfx/Linear_allocator_7_double_stack.png)
17444
17445
Double stack is available only in pools with one memory block -
17446
VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
17447
17448
When the two stacks' ends meet so there is not enough space between them for a
17449
new allocation, such allocation fails with usual
17450
`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
17451
17452
\subsection linear_algorithm_ring_buffer Ring buffer
17453
17454
When you free some allocations from the beginning and there is not enough free space
17455
for a new one at the end of a pool, allocator's "cursor" wraps around to the
17456
beginning and starts allocation there. Thanks to this, if you always release
17457
allocations in the same order as you created them (FIFO - First In First Out),
17458
you can achieve behavior of a ring buffer / queue.
17459
17460
![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
17461
17462
Ring buffer is available only in pools with one memory block -
17463
VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
17464
17465
\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
17466
17467
17468
\page defragmentation Defragmentation
17469
17470
Interleaved allocations and deallocations of many objects of varying size can
17471
cause fragmentation over time, which can lead to a situation where the library is unable
17472
to find a continuous range of free memory for a new allocation despite there is
17473
enough free space, just scattered across many small free ranges between existing
17474
allocations.
17475
17476
To mitigate this problem, you can use defragmentation feature.
17477
It doesn't happen automatically though and needs your cooperation,
17478
because VMA is a low level library that only allocates memory.
17479
It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures.
17480
It cannot copy their contents as it doesn't record any commands to a command buffer.
17481
17482
Example:
17483
17484
\code
17485
VmaDefragmentationInfo defragInfo = {};
17486
defragInfo.pool = myPool;
17487
defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT;
17488
17489
VmaDefragmentationContext defragCtx;
17490
VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);
17491
// Check res...
17492
17493
for(;;)
17494
{
17495
VmaDefragmentationPassMoveInfo pass;
17496
res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);
17497
if(res == VK_SUCCESS)
17498
break;
17499
else if(res != VK_INCOMPLETE)
17500
// Handle error...
17501
17502
for(uint32_t i = 0; i < pass.moveCount; ++i)
17503
{
17504
// Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents.
17505
VmaAllocationInfo allocInfo;
17506
vmaGetAllocationInfo(allocator, pass.pMoves[i].srcAllocation, &allocInfo);
17507
MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData;
17508
17509
// Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.
17510
VkImageCreateInfo imgCreateInfo = ...
17511
VkImage newImg;
17512
res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg);
17513
// Check res...
17514
res = vmaBindImageMemory(allocator, pass.pMoves[i].dstTmpAllocation, newImg);
17515
// Check res...
17516
17517
// Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.
17518
vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...);
17519
}
17520
17521
// Make sure the copy commands finished executing.
17522
vkWaitForFences(...);
17523
17524
// Destroy old buffers/images bound with pass.pMoves[i].srcAllocation.
17525
for(uint32_t i = 0; i < pass.moveCount; ++i)
17526
{
17527
// ...
17528
vkDestroyImage(device, resData->img, nullptr);
17529
}
17530
17531
// Update appropriate descriptors to point to the new places...
17532
17533
res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);
17534
if(res == VK_SUCCESS)
17535
break;
17536
else if(res != VK_INCOMPLETE)
17537
// Handle error...
17538
}
17539
17540
vmaEndDefragmentation(allocator, defragCtx, nullptr);
17541
\endcode
17542
17543
Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage()
17544
create/destroy an allocation and a buffer/image at once, these are just a shortcut for
17545
creating the resource, allocating memory, and binding them together.
17546
Defragmentation works on memory allocations only. You must handle the rest manually.
17547
Defragmentation is an iterative process that should repreat "passes" as long as related functions
17548
return `VK_INCOMPLETE` not `VK_SUCCESS`.
17549
In each pass:
17550
17551
1. vmaBeginDefragmentationPass() function call:
17552
- Calculates and returns the list of allocations to be moved in this pass.
17553
Note this can be a time-consuming process.
17554
- Reserves destination memory for them by creating temporary destination allocations
17555
that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo().
17556
2. Inside the pass, **you should**:
17557
- Inspect the returned list of allocations to be moved.
17558
- Create new buffers/images and bind them at the returned destination temporary allocations.
17559
- Copy data from source to destination resources if necessary.
17560
- Destroy the source buffers/images, but NOT their allocations.
17561
3. vmaEndDefragmentationPass() function call:
17562
- Frees the source memory reserved for the allocations that are moved.
17563
- Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory.
17564
- Frees `VkDeviceMemory` blocks that became empty.
17565
17566
Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter.
17567
Defragmentation algorithm tries to move all suitable allocations.
17568
You can, however, refuse to move some of them inside a defragmentation pass, by setting
17569
`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
17570
This is not recommended and may result in suboptimal packing of the allocations after defragmentation.
17571
If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.
17572
17573
Inside a pass, for each allocation that should be moved:
17574
17575
- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`.
17576
- You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass().
17577
- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared,
17578
filled, and used temporarily in each rendering frame, you can just recreate this image
17579
without copying its data.
17580
- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU
17581
using `memcpy()`.
17582
- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
17583
This will cancel the move.
17584
- vmaEndDefragmentationPass() will then free the destination memory
17585
not the source memory of the allocation, leaving it unchanged.
17586
- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),
17587
you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
17588
- vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object.
17589
17590
You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool
17591
(like in the example above) or all the default pools by setting this member to null.
17592
17593
Defragmentation is always performed in each pool separately.
17594
Allocations are never moved between different Vulkan memory types.
17595
The size of the destination memory reserved for a moved allocation is the same as the original one.
17596
Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation.
17597
Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones.
17598
17599
You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved
17600
in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.
17601
See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.
17602
17603
It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA
17604
usage, possibly from multiple threads, with the exception that allocations
17605
returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.
17606
17607
<b>Mapping</b> is preserved on allocations that are moved during defragmentation.
17608
Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations
17609
are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried
17610
using VmaAllocationInfo::pMappedData.
17611
17612
\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
17613
17614
17615
\page statistics Statistics
17616
17617
This library contains several functions that return information about its internal state,
17618
especially the amount of memory allocated from Vulkan.
17619
17620
\section statistics_numeric_statistics Numeric statistics
17621
17622
If you need to obtain basic statistics about memory usage per heap, together with current budget,
17623
you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget.
17624
This is useful to keep track of memory usage and stay within budget
17625
(see also \ref staying_within_budget).
17626
Example:
17627
17628
\code
17629
uint32_t heapIndex = ...
17630
17631
VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
17632
vmaGetHeapBudgets(allocator, budgets);
17633
17634
printf("My heap currently has %u allocations taking %llu B,\n",
17635
budgets[heapIndex].statistics.allocationCount,
17636
budgets[heapIndex].statistics.allocationBytes);
17637
printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n",
17638
budgets[heapIndex].statistics.blockCount,
17639
budgets[heapIndex].statistics.blockBytes);
17640
printf("Vulkan reports total usage %llu B with budget %llu B.\n",
17641
budgets[heapIndex].usage,
17642
budgets[heapIndex].budget);
17643
\endcode
17644
17645
You can query for more detailed statistics per memory heap, type, and totals,
17646
including minimum and maximum allocation size and unused range size,
17647
by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics.
17648
This function is slower though, as it has to traverse all the internal data structures,
17649
so it should be used only for debugging purposes.
17650
17651
You can query for statistics of a custom pool using function vmaGetPoolStatistics()
17652
or vmaCalculatePoolStatistics().
17653
17654
You can query for information about a specific allocation using function vmaGetAllocationInfo().
17655
It fill structure #VmaAllocationInfo.
17656
17657
\section statistics_json_dump JSON dump
17658
17659
You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
17660
The result is guaranteed to be correct JSON.
17661
It uses ANSI encoding.
17662
Any strings provided by user (see [Allocation names](@ref allocation_names))
17663
are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
17664
this JSON string can be treated as using this encoding.
17665
It must be freed using function vmaFreeStatsString().
17666
17667
The format of this JSON string is not part of official documentation of the library,
17668
but it will not change in backward-incompatible way without increasing library major version number
17669
and appropriate mention in changelog.
17670
17671
The JSON string contains all the data that can be obtained using vmaCalculateStatistics().
17672
It can also contain detailed map of allocated memory blocks and their regions -
17673
free and occupied by allocations.
17674
This allows e.g. to visualize the memory or assess fragmentation.
17675
17676
17677
\page allocation_annotation Allocation names and user data
17678
17679
\section allocation_user_data Allocation user data
17680
17681
You can annotate allocations with your own information, e.g. for debugging purposes.
17682
To do that, fill VmaAllocationCreateInfo::pUserData field when creating
17683
an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,
17684
some handle, index, key, ordinal number or any other value that would associate
17685
the allocation with your custom metadata.
17686
It is useful to identify appropriate data structures in your engine given #VmaAllocation,
17687
e.g. when doing \ref defragmentation.
17688
17689
\code
17690
VkBufferCreateInfo bufCreateInfo = ...
17691
17692
MyBufferMetadata* pMetadata = CreateBufferMetadata();
17693
17694
VmaAllocationCreateInfo allocCreateInfo = {};
17695
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
17696
allocCreateInfo.pUserData = pMetadata;
17697
17698
VkBuffer buffer;
17699
VmaAllocation allocation;
17700
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
17701
\endcode
17702
17703
The pointer may be later retrieved as VmaAllocationInfo::pUserData:
17704
17705
\code
17706
VmaAllocationInfo allocInfo;
17707
vmaGetAllocationInfo(allocator, allocation, &allocInfo);
17708
MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
17709
\endcode
17710
17711
It can also be changed using function vmaSetAllocationUserData().
17712
17713
Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
17714
vmaBuildStatsString() in hexadecimal form.
17715
17716
\section allocation_names Allocation names
17717
17718
An allocation can also carry a null-terminated string, giving a name to the allocation.
17719
To set it, call vmaSetAllocationName().
17720
The library creates internal copy of the string, so the pointer you pass doesn't need
17721
to be valid for whole lifetime of the allocation. You can free it after the call.
17722
17723
\code
17724
std::string imageName = "Texture: ";
17725
imageName += fileName;
17726
vmaSetAllocationName(allocator, allocation, imageName.c_str());
17727
\endcode
17728
17729
The string can be later retrieved by inspecting VmaAllocationInfo::pName.
17730
It is also printed in JSON report created by vmaBuildStatsString().
17731
17732
\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
17733
You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
17734
17735
17736
\page virtual_allocator Virtual allocator
17737
17738
As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
17739
It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
17740
You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.
17741
A common use case is sub-allocation of pieces of one large GPU buffer.
17742
17743
\section virtual_allocator_creating_virtual_block Creating virtual block
17744
17745
To use this functionality, there is no main "allocator" object.
17746
You don't need to have #VmaAllocator object created.
17747
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:
17748
17749
-# Fill in #VmaVirtualBlockCreateInfo structure.
17750
-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.
17751
17752
Example:
17753
17754
\code
17755
VmaVirtualBlockCreateInfo blockCreateInfo = {};
17756
blockCreateInfo.size = 1048576; // 1 MB
17757
17758
VmaVirtualBlock block;
17759
VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);
17760
\endcode
17761
17762
\section virtual_allocator_making_virtual_allocations Making virtual allocations
17763
17764
#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions
17765
using the same code as the main Vulkan memory allocator.
17766
Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type
17767
that represents an opaque handle to an allocation within the virtual block.
17768
17769
In order to make such allocation:
17770
17771
-# Fill in #VmaVirtualAllocationCreateInfo structure.
17772
-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation.
17773
You can also receive `VkDeviceSize offset` that was assigned to the allocation.
17774
17775
Example:
17776
17777
\code
17778
VmaVirtualAllocationCreateInfo allocCreateInfo = {};
17779
allocCreateInfo.size = 4096; // 4 KB
17780
17781
VmaVirtualAllocation alloc;
17782
VkDeviceSize offset;
17783
res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset);
17784
if(res == VK_SUCCESS)
17785
{
17786
// Use the 4 KB of your memory starting at offset.
17787
}
17788
else
17789
{
17790
// Allocation failed - no space for it could be found. Handle this error!
17791
}
17792
\endcode
17793
17794
\section virtual_allocator_deallocation Deallocation
17795
17796
When no longer needed, an allocation can be freed by calling vmaVirtualFree().
17797
You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate()
17798
called for the same #VmaVirtualBlock.
17799
17800
When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().
17801
All allocations must be freed before the block is destroyed, which is checked internally by an assert.
17802
However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -
17803
a feature not available in normal Vulkan memory allocator. Example:
17804
17805
\code
17806
vmaVirtualFree(block, alloc);
17807
vmaDestroyVirtualBlock(block);
17808
\endcode
17809
17810
\section virtual_allocator_allocation_parameters Allocation parameters
17811
17812
You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().
17813
Its default value is null.
17814
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
17815
larger data structure containing more information. Example:
17816
17817
\code
17818
struct CustomAllocData
17819
{
17820
std::string m_AllocName;
17821
};
17822
CustomAllocData* allocData = new CustomAllocData();
17823
allocData->m_AllocName = "My allocation 1";
17824
vmaSetVirtualAllocationUserData(block, alloc, allocData);
17825
\endcode
17826
17827
The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function
17828
vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.
17829
If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
17830
Example:
17831
17832
\code
17833
VmaVirtualAllocationInfo allocInfo;
17834
vmaGetVirtualAllocationInfo(block, alloc, &allocInfo);
17835
delete (CustomAllocData*)allocInfo.pUserData;
17836
17837
vmaVirtualFree(block, alloc);
17838
\endcode
17839
17840
\section virtual_allocator_alignment_and_units Alignment and units
17841
17842
It feels natural to express sizes and offsets in bytes.
17843
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
17844
VmaVirtualAllocationCreateInfo::alignment to request it. Example:
17845
17846
\code
17847
VmaVirtualAllocationCreateInfo allocCreateInfo = {};
17848
allocCreateInfo.size = 4096; // 4 KB
17849
allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
17850
17851
VmaVirtualAllocation alloc;
17852
res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr);
17853
\endcode
17854
17855
Alignments of different allocations made from one block may vary.
17856
However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
17857
you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
17858
It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
17859
17860
- VmaVirtualBlockCreateInfo::size
17861
- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment
17862
- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset
17863
17864
\section virtual_allocator_statistics Statistics
17865
17866
You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics()
17867
(to get brief statistics that are fast to calculate)
17868
or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate).
17869
The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator.
17870
Example:
17871
17872
\code
17873
VmaStatistics stats;
17874
vmaGetVirtualBlockStatistics(block, &stats);
17875
printf("My virtual block has %llu bytes used by %u virtual allocations\n",
17876
stats.allocationBytes, stats.allocationCount);
17877
\endcode
17878
17879
You can also request a full list of allocations and free regions as a string in JSON format by calling
17880
vmaBuildVirtualBlockStatsString().
17881
Returned string must be later freed using vmaFreeVirtualBlockStatsString().
17882
The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.
17883
17884
\section virtual_allocator_additional_considerations Additional considerations
17885
17886
The "virtual allocator" functionality is implemented on a level of individual memory blocks.
17887
Keeping track of a whole collection of blocks, allocating new ones when out of free space,
17888
deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
17889
17890
Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.
17891
See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).
17892
You can find their description in chapter \ref custom_memory_pools.
17893
Allocation strategies are also supported.
17894
See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).
17895
17896
Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:
17897
buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.
17898
17899
17900
\page debugging_memory_usage Debugging incorrect memory usage
17901
17902
If you suspect a bug with memory usage, like usage of uninitialized memory or
17903
memory being overwritten out of bounds of an allocation,
17904
you can use debug features of this library to verify this.
17905
17906
\section debugging_memory_usage_initialization Memory initialization
17907
17908
If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
17909
you can enable automatic memory initialization to verify this.
17910
To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
17911
17912
\code
17913
#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
17914
#include "vk_mem_alloc.h"
17915
\endcode
17916
17917
It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`.
17918
Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
17919
Memory is automatically mapped and unmapped if necessary.
17920
17921
If you find these values while debugging your program, good chances are that you incorrectly
17922
read Vulkan memory that is allocated but not initialized, or already freed, respectively.
17923
17924
Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped.
17925
It works also with dedicated allocations.
17926
17927
\section debugging_memory_usage_margins Margins
17928
17929
By default, allocations are laid out in memory blocks next to each other if possible
17930
(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
17931
17932
![Allocations without margin](../gfx/Margins_1.png)
17933
17934
Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
17935
number of bytes as a margin after every allocation.
17936
17937
\code
17938
#define VMA_DEBUG_MARGIN 16
17939
#include "vk_mem_alloc.h"
17940
\endcode
17941
17942
![Allocations with margin](../gfx/Margins_2.png)
17943
17944
If your bug goes away after enabling margins, it means it may be caused by memory
17945
being overwritten outside of allocation boundaries. It is not 100% certain though.
17946
Change in application behavior may also be caused by different order and distribution
17947
of allocations across memory blocks after margins are applied.
17948
17949
Margins work with all types of memory.
17950
17951
Margin is applied only to allocations made out of memory blocks and not to dedicated
17952
allocations, which have their own memory block of specific size.
17953
It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
17954
or those automatically decided to put into dedicated allocations, e.g. due to its
17955
large size or recommended by VK_KHR_dedicated_allocation extension.
17956
17957
Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
17958
17959
Note that enabling margins increases memory usage and fragmentation.
17960
17961
Margins do not apply to \ref virtual_allocator.
17962
17963
\section debugging_memory_usage_corruption_detection Corruption detection
17964
17965
You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
17966
of contents of the margins.
17967
17968
\code
17969
#define VMA_DEBUG_MARGIN 16
17970
#define VMA_DEBUG_DETECT_CORRUPTION 1
17971
#include "vk_mem_alloc.h"
17972
\endcode
17973
17974
When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
17975
(it must be multiply of 4) after every allocation is filled with a magic number.
17976
This idea is also know as "canary".
17977
Memory is automatically mapped and unmapped if necessary.
17978
17979
This number is validated automatically when the allocation is destroyed.
17980
If it is not equal to the expected value, `VMA_ASSERT()` is executed.
17981
It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
17982
which indicates a serious bug.
17983
17984
You can also explicitly request checking margins of all allocations in all memory blocks
17985
that belong to specified memory types by using function vmaCheckCorruption(),
17986
or in memory blocks that belong to specified custom pool, by using function
17987
vmaCheckPoolCorruption().
17988
17989
Margin validation (corruption detection) works only for memory types that are
17990
`HOST_VISIBLE` and `HOST_COHERENT`.
17991
17992
17993
\section debugging_memory_usage_leak_detection Leak detection features
17994
17995
At allocation and allocator destruction time VMA checks for unfreed and unmapped blocks using
17996
`VMA_ASSERT_LEAK()`. This macro defaults to an assertion, triggering a typically fatal error in Debug
17997
builds, and doing nothing in Release builds. You can provide your own definition of `VMA_ASSERT_LEAK()`
17998
to change this behavior.
17999
18000
At memory block destruction time VMA lists out all unfreed allocations using the `VMA_LEAK_LOG_FORMAT()`
18001
macro, which defaults to `VMA_DEBUG_LOG_FORMAT`, which in turn defaults to a no-op.
18002
If you're having trouble with leaks - for example, the aforementioned assertion triggers, but you don't
18003
quite know \em why -, overriding this macro to print out the the leaking blocks, combined with assigning
18004
individual names to allocations using vmaSetAllocationName(), can greatly aid in fixing them.
18005
18006
\page other_api_interop Interop with other graphics APIs
18007
18008
VMA provides some features that help with interoperability with other graphics APIs, e.g. OpenGL.
18009
18010
\section opengl_interop_exporting_memory Exporting memory
18011
18012
If you want to attach `VkExportMemoryAllocateInfoKHR` or other structure to `pNext` chain of memory allocations made by the library:
18013
18014
You can create \ref custom_memory_pools for such allocations.
18015
Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext
18016
while creating the custom pool.
18017
Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,
18018
not only while creating it, as no copy of the structure is made,
18019
but its original pointer is used for each allocation instead.
18020
18021
If you want to export all memory allocated by VMA from certain memory types,
18022
also dedicated allocations or other allocations made from default pools,
18023
an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.
18024
It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library
18025
through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.
18026
Please note that new versions of the library also support dedicated allocations created in custom pools.
18027
18028
You should not mix these two methods in a way that allows to apply both to the same memory type.
18029
Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.
18030
18031
18032
\section opengl_interop_custom_alignment Custom alignment
18033
18034
Buffers or images exported to a different API like OpenGL may require a different alignment,
18035
higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.
18036
To impose such alignment:
18037
18038
You can create \ref custom_memory_pools for such allocations.
18039
Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation
18040
to be made out of this pool.
18041
The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image
18042
from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.
18043
18044
If you want to create a buffer with a specific minimum alignment out of default pools,
18045
use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.
18046
18047
Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated
18048
allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.
18049
You can ensure that an allocation is created as dedicated by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18050
Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.
18051
18052
\section opengl_interop_extended_allocation_information Extended allocation information
18053
18054
If you want to rely on VMA to allocate your buffers and images inside larger memory blocks,
18055
but you need to know the size of the entire block and whether the allocation was made
18056
with its own dedicated memory, use function vmaGetAllocationInfo2() to retrieve
18057
extended allocation information in structure #VmaAllocationInfo2.
18058
18059
18060
18061
\page usage_patterns Recommended usage patterns
18062
18063
Vulkan gives great flexibility in memory allocation.
18064
This chapter shows the most common patterns.
18065
18066
See also slides from talk:
18067
[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)
18068
18069
18070
\section usage_patterns_gpu_only GPU-only resource
18071
18072
<b>When:</b>
18073
Any resources that you frequently write and read on GPU,
18074
e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
18075
images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
18076
18077
<b>What to do:</b>
18078
Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
18079
18080
\code
18081
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18082
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
18083
imgCreateInfo.extent.width = 3840;
18084
imgCreateInfo.extent.height = 2160;
18085
imgCreateInfo.extent.depth = 1;
18086
imgCreateInfo.mipLevels = 1;
18087
imgCreateInfo.arrayLayers = 1;
18088
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18089
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18090
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18091
imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18092
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18093
18094
VmaAllocationCreateInfo allocCreateInfo = {};
18095
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18096
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
18097
allocCreateInfo.priority = 1.0f;
18098
18099
VkImage img;
18100
VmaAllocation alloc;
18101
vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
18102
\endcode
18103
18104
<b>Also consider:</b>
18105
Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
18106
especially if they are large or if you plan to destroy and recreate them with different sizes
18107
e.g. when display resolution changes.
18108
Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
18109
When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation
18110
to decrease chances to be evicted to system memory by the operating system.
18111
18112
\section usage_patterns_staging_copy_upload Staging copy for upload
18113
18114
<b>When:</b>
18115
A "staging" buffer than you want to map and fill from CPU code, then use as a source of transfer
18116
to some GPU resource.
18117
18118
<b>What to do:</b>
18119
Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT.
18120
Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`.
18121
18122
\code
18123
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18124
bufCreateInfo.size = 65536;
18125
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
18126
18127
VmaAllocationCreateInfo allocCreateInfo = {};
18128
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18129
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
18130
VMA_ALLOCATION_CREATE_MAPPED_BIT;
18131
18132
VkBuffer buf;
18133
VmaAllocation alloc;
18134
VmaAllocationInfo allocInfo;
18135
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18136
18137
...
18138
18139
memcpy(allocInfo.pMappedData, myData, myDataSize);
18140
\endcode
18141
18142
<b>Also consider:</b>
18143
You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped
18144
using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above.
18145
18146
18147
\section usage_patterns_readback Readback
18148
18149
<b>When:</b>
18150
Buffers for data written by or transferred from the GPU that you want to read back on the CPU,
18151
e.g. results of some computations.
18152
18153
<b>What to do:</b>
18154
Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
18155
Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
18156
and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
18157
18158
\code
18159
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18160
bufCreateInfo.size = 65536;
18161
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18162
18163
VmaAllocationCreateInfo allocCreateInfo = {};
18164
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18165
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |
18166
VMA_ALLOCATION_CREATE_MAPPED_BIT;
18167
18168
VkBuffer buf;
18169
VmaAllocation alloc;
18170
VmaAllocationInfo allocInfo;
18171
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18172
18173
...
18174
18175
const float* downloadedData = (const float*)allocInfo.pMappedData;
18176
\endcode
18177
18178
18179
\section usage_patterns_advanced_data_uploading Advanced data uploading
18180
18181
For resources that you frequently write on CPU via mapped pointer and
18182
frequently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible:
18183
18184
-# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory,
18185
even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card,
18186
and make the device reach out to that resource directly.
18187
- Reads performed by the device will then go through PCI Express bus.
18188
The performance of this access may be limited, but it may be fine depending on the size
18189
of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity
18190
of access.
18191
-# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips),
18192
a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL`
18193
(fast to access from the GPU). Then, it is likely the best choice for such type of resource.
18194
-# Systems with a discrete graphics card and separate video memory may or may not expose
18195
a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR).
18196
If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS)
18197
that is available to CPU for mapping.
18198
- Writes performed by the host to that memory go through PCI Express bus.
18199
The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0,
18200
as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads.
18201
-# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory,
18202
a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them.
18203
18204
Thankfully, VMA offers an aid to create and use such resources in the the way optimal
18205
for the current Vulkan device. To help the library make the best choice,
18206
use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with
18207
#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT.
18208
It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR),
18209
but if no such memory type is available or allocation from it fails
18210
(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS),
18211
it will fall back to `DEVICE_LOCAL` memory for fast GPU access.
18212
It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`,
18213
so you need to create another "staging" allocation and perform explicit transfers.
18214
18215
\code
18216
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18217
bufCreateInfo.size = 65536;
18218
bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18219
18220
VmaAllocationCreateInfo allocCreateInfo = {};
18221
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18222
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
18223
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
18224
VMA_ALLOCATION_CREATE_MAPPED_BIT;
18225
18226
VkBuffer buf;
18227
VmaAllocation alloc;
18228
VmaAllocationInfo allocInfo;
18229
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18230
18231
VkMemoryPropertyFlags memPropFlags;
18232
vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
18233
18234
if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
18235
{
18236
// Allocation ended up in a mappable memory and is already mapped - write to it directly.
18237
18238
// [Executed in runtime]:
18239
memcpy(allocInfo.pMappedData, myData, myDataSize);
18240
}
18241
else
18242
{
18243
// Allocation ended up in a non-mappable memory - need to transfer.
18244
VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18245
stagingBufCreateInfo.size = 65536;
18246
stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
18247
18248
VmaAllocationCreateInfo stagingAllocCreateInfo = {};
18249
stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18250
stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
18251
VMA_ALLOCATION_CREATE_MAPPED_BIT;
18252
18253
VkBuffer stagingBuf;
18254
VmaAllocation stagingAlloc;
18255
VmaAllocationInfo stagingAllocInfo;
18256
vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
18257
&stagingBuf, &stagingAlloc, stagingAllocInfo);
18258
18259
// [Executed in runtime]:
18260
memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
18261
vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);
18262
//vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT
18263
VkBufferCopy bufCopy = {
18264
0, // srcOffset
18265
0, // dstOffset,
18266
myDataSize); // size
18267
vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
18268
}
18269
\endcode
18270
18271
\section usage_patterns_other_use_cases Other use cases
18272
18273
Here are some other, less obvious use cases and their recommended settings:
18274
18275
- An image that is used only as transfer source and destination, but it should stay on the device,
18276
as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame,
18277
for temporal antialiasing or other temporal effects.
18278
- Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
18279
- Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO
18280
- An image that is used only as transfer source and destination, but it should be placed
18281
in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict
18282
least recently used textures from VRAM.
18283
- Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
18284
- Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
18285
as VMA needs a hint here to differentiate from the previous case.
18286
- A buffer that you want to map and write from the CPU, directly read from the GPU
18287
(e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or
18288
host memory due to its large size.
18289
- Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`
18290
- Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST
18291
- Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT
18292
18293
18294
\page configuration Configuration
18295
18296
Please check "CONFIGURATION SECTION" in the code to find macros that you can define
18297
before each include of this file or change directly in this file to provide
18298
your own implementation of basic facilities like assert, `min()` and `max()` functions,
18299
mutex, atomic etc.
18300
The library uses its own implementation of containers by default, but you can switch to using
18301
STL containers instead.
18302
18303
For example, define `VMA_ASSERT(expr)` before including the library to provide
18304
custom implementation of the assertion, compatible with your project.
18305
By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
18306
and empty otherwise.
18307
18308
\section config_Vulkan_functions Pointers to Vulkan functions
18309
18310
There are multiple ways to import pointers to Vulkan functions in the library.
18311
In the simplest case you don't need to do anything.
18312
If the compilation or linking of your program or the initialization of the #VmaAllocator
18313
doesn't work for you, you can try to reconfigure it.
18314
18315
First, the allocator tries to fetch pointers to Vulkan functions linked statically,
18316
like this:
18317
18318
\code
18319
m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
18320
\endcode
18321
18322
If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
18323
18324
Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
18325
You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
18326
by using a helper library like [volk](https://github.com/zeux/volk).
18327
18328
Third, VMA tries to fetch remaining pointers that are still null by calling
18329
`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
18330
You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr.
18331
Other pointers will be fetched automatically.
18332
If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
18333
18334
Finally, all the function pointers required by the library (considering selected
18335
Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
18336
18337
18338
\section custom_memory_allocator Custom host memory allocator
18339
18340
If you use custom allocator for CPU memory rather than default operator `new`
18341
and `delete` from C++, you can make this library using your allocator as well
18342
by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
18343
functions will be passed to Vulkan, as well as used by the library itself to
18344
make any CPU-side allocations.
18345
18346
\section allocation_callbacks Device memory allocation callbacks
18347
18348
The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
18349
You can setup callbacks to be informed about these calls, e.g. for the purpose
18350
of gathering some statistics. To do it, fill optional member
18351
VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
18352
18353
\section heap_memory_limit Device heap memory limit
18354
18355
When device memory of certain heap runs out of free space, new allocations may
18356
fail (returning error code) or they may succeed, silently pushing some existing_
18357
memory blocks from GPU VRAM to system RAM (which degrades performance). This
18358
behavior is implementation-dependent - it depends on GPU vendor and graphics
18359
driver.
18360
18361
On AMD cards it can be controlled while creating Vulkan device object by using
18362
VK_AMD_memory_overallocation_behavior extension, if available.
18363
18364
Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
18365
memory available without switching your graphics card to one that really has
18366
smaller VRAM, you can use a feature of this library intended for this purpose.
18367
To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
18368
18369
18370
18371
\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
18372
18373
VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
18374
performance on some GPUs. It augments Vulkan API with possibility to query
18375
driver whether it prefers particular buffer or image to have its own, dedicated
18376
allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
18377
to do some internal optimizations. The extension is supported by this library.
18378
It will be used automatically when enabled.
18379
18380
It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version
18381
and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion,
18382
you are all set.
18383
18384
Otherwise, if you want to use it as an extension:
18385
18386
1 . When creating Vulkan device, check if following 2 device extensions are
18387
supported (call `vkEnumerateDeviceExtensionProperties()`).
18388
If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
18389
18390
- VK_KHR_get_memory_requirements2
18391
- VK_KHR_dedicated_allocation
18392
18393
If you enabled these extensions:
18394
18395
2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
18396
your #VmaAllocator to inform the library that you enabled required extensions
18397
and you want the library to use them.
18398
18399
\code
18400
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
18401
18402
vmaCreateAllocator(&allocatorInfo, &allocator);
18403
\endcode
18404
18405
That is all. The extension will be automatically used whenever you create a
18406
buffer using vmaCreateBuffer() or image using vmaCreateImage().
18407
18408
When using the extension together with Vulkan Validation Layer, you will receive
18409
warnings like this:
18410
18411
_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._
18412
18413
It is OK, you should just ignore it. It happens because you use function
18414
`vkGetBufferMemoryRequirements2KHR()` instead of standard
18415
`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
18416
unaware of it.
18417
18418
To learn more about this extension, see:
18419
18420
- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)
18421
- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
18422
18423
18424
18425
\page vk_ext_memory_priority VK_EXT_memory_priority
18426
18427
VK_EXT_memory_priority is a device extension that allows to pass additional "priority"
18428
value to Vulkan memory allocations that the implementation may use prefer certain
18429
buffers and images that are critical for performance to stay in device-local memory
18430
in cases when the memory is over-subscribed, while some others may be moved to the system memory.
18431
18432
VMA offers convenient usage of this extension.
18433
If you enable it, you can pass "priority" parameter when creating allocations or custom pools
18434
and the library automatically passes the value to Vulkan using this extension.
18435
18436
If you want to use this extension in connection with VMA, follow these steps:
18437
18438
\section vk_ext_memory_priority_initialization Initialization
18439
18440
1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
18441
Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority".
18442
18443
2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
18444
Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
18445
Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true.
18446
18447
3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority"
18448
to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
18449
18450
4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
18451
Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
18452
Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to
18453
`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`.
18454
18455
5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
18456
have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
18457
to VmaAllocatorCreateInfo::flags.
18458
18459
\section vk_ext_memory_priority_usage Usage
18460
18461
When using this extension, you should initialize following member:
18462
18463
- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18464
- VmaPoolCreateInfo::priority when creating a custom pool.
18465
18466
It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`.
18467
Memory allocated with higher value can be treated by the Vulkan implementation as higher priority
18468
and so it can have lower chances of being pushed out to system memory, experiencing degraded performance.
18469
18470
It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images
18471
as dedicated and set high priority to them. For example:
18472
18473
\code
18474
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18475
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
18476
imgCreateInfo.extent.width = 3840;
18477
imgCreateInfo.extent.height = 2160;
18478
imgCreateInfo.extent.depth = 1;
18479
imgCreateInfo.mipLevels = 1;
18480
imgCreateInfo.arrayLayers = 1;
18481
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18482
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18483
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18484
imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18485
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18486
18487
VmaAllocationCreateInfo allocCreateInfo = {};
18488
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18489
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
18490
allocCreateInfo.priority = 1.0f;
18491
18492
VkImage img;
18493
VmaAllocation alloc;
18494
vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
18495
\endcode
18496
18497
`priority` member is ignored in the following situations:
18498
18499
- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters
18500
from the parameters passed in #VmaPoolCreateInfo when the pool was created.
18501
- Allocations created in default pools: They inherit the priority from the parameters
18502
VMA used when creating default pools, which means `priority == 0.5f`.
18503
18504
18505
\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
18506
18507
VK_AMD_device_coherent_memory is a device extension that enables access to
18508
additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
18509
`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
18510
allocation of buffers intended for writing "breadcrumb markers" in between passes
18511
or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
18512
18513
When the extension is available but has not been enabled, Vulkan physical device
18514
still exposes those memory types, but their usage is forbidden. VMA automatically
18515
takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
18516
to allocate memory of such type is made.
18517
18518
If you want to use this extension in connection with VMA, follow these steps:
18519
18520
\section vk_amd_device_coherent_memory_initialization Initialization
18521
18522
1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
18523
Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
18524
18525
2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
18526
Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
18527
Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
18528
18529
3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
18530
to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
18531
18532
4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
18533
Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
18534
Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
18535
`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
18536
18537
5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
18538
have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
18539
to VmaAllocatorCreateInfo::flags.
18540
18541
\section vk_amd_device_coherent_memory_usage Usage
18542
18543
After following steps described above, you can create VMA allocations and custom pools
18544
out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
18545
devices. There are multiple ways to do it, for example:
18546
18547
- You can request or prefer to allocate out of such memory types by adding
18548
`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
18549
or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
18550
other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
18551
- If you manually found memory type index to use for this purpose, force allocation
18552
from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
18553
18554
\section vk_amd_device_coherent_memory_more_information More information
18555
18556
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)
18557
18558
Example use of this extension can be found in the code of the sample and test suite
18559
accompanying this library.
18560
18561
18562
\page enabling_buffer_device_address Enabling buffer device address
18563
18564
Device extension VK_KHR_buffer_device_address
18565
allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
18566
It has been promoted to core Vulkan 1.2.
18567
18568
If you want to use this feature in connection with VMA, follow these steps:
18569
18570
\section enabling_buffer_device_address_initialization Initialization
18571
18572
1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
18573
Check if the extension is supported - if returned array of `VkExtensionProperties` contains
18574
"VK_KHR_buffer_device_address".
18575
18576
2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
18577
Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
18578
Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true.
18579
18580
3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
18581
"VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
18582
18583
4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
18584
Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
18585
Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
18586
`VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
18587
18588
5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
18589
have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
18590
to VmaAllocatorCreateInfo::flags.
18591
18592
\section enabling_buffer_device_address_usage Usage
18593
18594
After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
18595
The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
18596
allocated memory blocks wherever it might be needed.
18597
18598
Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
18599
The second part of this functionality related to "capture and replay" is not supported,
18600
as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
18601
18602
\section enabling_buffer_device_address_more_information More information
18603
18604
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)
18605
18606
Example use of this extension can be found in the code of the sample and test suite
18607
accompanying this library.
18608
18609
\page general_considerations General considerations
18610
18611
\section general_considerations_thread_safety Thread safety
18612
18613
- The library has no global state, so separate #VmaAllocator objects can be used
18614
independently.
18615
There should be no need to create multiple such objects though - one per `VkDevice` is enough.
18616
- By default, all calls to functions that take #VmaAllocator as first parameter
18617
are safe to call from multiple threads simultaneously because they are
18618
synchronized internally when needed.
18619
This includes allocation and deallocation from default memory pool, as well as custom #VmaPool.
18620
- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
18621
flag, calls to functions that take such #VmaAllocator object must be
18622
synchronized externally.
18623
- Access to a #VmaAllocation object must be externally synchronized. For example,
18624
you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
18625
threads at the same time if you pass the same #VmaAllocation object to these
18626
functions.
18627
- #VmaVirtualBlock is not safe to be used from multiple threads simultaneously.
18628
18629
\section general_considerations_versioning_and_compatibility Versioning and compatibility
18630
18631
The library uses [**Semantic Versioning**](https://semver.org/),
18632
which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where:
18633
18634
- Incremented Patch version means a release is backward- and forward-compatible,
18635
introducing only some internal improvements, bug fixes, optimizations etc.
18636
or changes that are out of scope of the official API described in this documentation.
18637
- Incremented Minor version means a release is backward-compatible,
18638
so existing code that uses the library should continue to work, while some new
18639
symbols could have been added: new structures, functions, new values in existing
18640
enums and bit flags, new structure members, but not new function parameters.
18641
- Incrementing Major version means a release could break some backward compatibility.
18642
18643
All changes between official releases are documented in file "CHANGELOG.md".
18644
18645
\warning Backward compatibility is considered on the level of C++ source code, not binary linkage.
18646
Adding new members to existing structures is treated as backward compatible if initializing
18647
the new members to binary zero results in the old behavior.
18648
You should always fully initialize all library structures to zeros and not rely on their
18649
exact binary size.
18650
18651
\section general_considerations_validation_layer_warnings Validation layer warnings
18652
18653
When using this library, you can meet following types of warnings issued by
18654
Vulkan validation layer. They don't necessarily indicate a bug, so you may need
18655
to just ignore them.
18656
18657
- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
18658
- It happens when VK_KHR_dedicated_allocation extension is enabled.
18659
`vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
18660
- *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.*
18661
- It happens when you map a buffer or image, because the library maps entire
18662
`VkDeviceMemory` block, where different types of images and buffers may end
18663
up together, especially on GPUs with unified memory like Intel.
18664
- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
18665
- It may happen when you use [defragmentation](@ref defragmentation).
18666
18667
\section general_considerations_allocation_algorithm Allocation algorithm
18668
18669
The library uses following algorithm for allocation, in order:
18670
18671
-# Try to find free range of memory in existing blocks.
18672
-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
18673
-# If failed, try to create such block with size / 2, size / 4, size / 8.
18674
-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
18675
just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18676
-# If failed, choose other memory type that meets the requirements specified in
18677
VmaAllocationCreateInfo and go to point 1.
18678
-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18679
18680
\section general_considerations_features_not_supported Features not supported
18681
18682
Features deliberately excluded from the scope of this library:
18683
18684
-# **Data transfer.** Uploading (streaming) and downloading data of buffers and images
18685
between CPU and GPU memory and related synchronization is responsibility of the user.
18686
Defining some "texture" object that would automatically stream its data from a
18687
staging copy in CPU memory to GPU memory would rather be a feature of another,
18688
higher-level library implemented on top of VMA.
18689
VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory.
18690
-# **Recreation of buffers and images.** Although the library has functions for
18691
buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to
18692
recreate these objects yourself after defragmentation. That is because the big
18693
structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
18694
#VmaAllocation object.
18695
-# **Handling CPU memory allocation failures.** When dynamically creating small C++
18696
objects in CPU memory (not Vulkan memory), allocation failures are not checked
18697
and handled gracefully, because that would complicate code significantly and
18698
is usually not needed in desktop PC applications anyway.
18699
Success of an allocation is just checked with an assert.
18700
-# **Code free of any compiler warnings.** Maintaining the library to compile and
18701
work correctly on so many different platforms is hard enough. Being free of
18702
any warnings, on any version of any compiler, is simply not feasible.
18703
There are many preprocessor macros that make some variables unused, function parameters unreferenced,
18704
or conditional expressions constant in some configurations.
18705
The code of this library should not be bigger or more complicated just to silence these warnings.
18706
It is recommended to disable such warnings instead.
18707
-# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but
18708
are not going to be included into this repository.
18709
*/
18710
18711