1 // 2 // Copyright (c) 2019-2020 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 #pragma once 24 25 /** \mainpage D3D12 Memory Allocator 26 27 <b>Version 2.0.0-development</b> (2020-07-16) 28 29 Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. \n 30 License: MIT 31 32 Documentation of all members: D3D12MemAlloc.h 33 34 \section main_table_of_contents Table of contents 35 36 - <b>User guide</b> 37 - \subpage quick_start 38 - [Project setup](@ref quick_start_project_setup) 39 - [Creating resources](@ref quick_start_creating_resources) 40 - [Mapping memory](@ref quick_start_mapping_memory) 41 - \subpage resource_aliasing 42 - \subpage reserving_memory 43 - \subpage virtual_allocator 44 - \subpage configuration 45 - [Custom CPU memory allocator](@ref custom_memory_allocator) 46 - \subpage general_considerations 47 - [Thread safety](@ref general_considerations_thread_safety) 48 - [Future plans](@ref general_considerations_future_plans) 49 - [Features not supported](@ref general_considerations_features_not_supported) 50 51 \section main_see_also See also 52 53 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/d3d12-memory-allocator/) 54 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator) 55 56 57 \page quick_start Quick start 58 59 \section quick_start_project_setup Project setup and initialization 60 61 This is a small, standalone C++ library. It consists of a pair of 2 files: 62 "%D3D12MemAlloc.h" header file with public interface and "D3D12MemAlloc.cpp" with 63 internal implementation. The only external dependencies are WinAPI, Direct3D 12, 64 and parts of C/C++ standard library (but STL containers, exceptions, or RTTI are 65 not used). 66 67 The library is developed and tested using Microsoft Visual Studio 2019, but it 68 should work with other compilers as well. It is designed for 64-bit code. 69 70 To use the library in your project: 71 72 (1.) Copy files `D3D12MemAlloc.cpp`, `%D3D12MemAlloc.h` to your project. 73 74 (2.) Make `D3D12MemAlloc.cpp` compiling as part of the project, as C++ code. 75 76 (3.) Include library header in each CPP file that needs to use the library. 77 78 \code 79 #include "D3D12MemAlloc.h" 80 \endcode 81 82 (4.) Right after you created `ID3D12Device`, fill D3D12MA::ALLOCATOR_DESC 83 structure and call function D3D12MA::CreateAllocator to create the main 84 D3D12MA::Allocator object. 85 86 Please note that all symbols of the library are declared inside #D3D12MA namespace. 87 88 \code 89 IDXGIAdapter* adapter = (...) 90 ID3D12Device* device = (...) 91 92 D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; 93 allocatorDesc.pDevice = device; 94 allocatorDesc.pAdapter = adapter; 95 96 D3D12MA::Allocator* allocator; 97 HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator); 98 \endcode 99 100 (5.) Right before destroying the D3D12 device, destroy the allocator object. 101 102 Please note that objects of this library must be destroyed by calling `Release` 103 method (despite they are not COM interfaces and no reference counting is involved). 104 105 \code 106 allocator->Release(); 107 \endcode 108 109 110 \section quick_start_creating_resources Creating resources 111 112 To use the library for creating resources (textures and buffers), call method 113 D3D12MA::Allocator::CreateResource in the place where you would previously call 114 `ID3D12Device::CreateCommittedResource`. 115 116 The function has similar syntax, but it expects structure D3D12MA::ALLOCATION_DESC 117 to be passed along with `D3D12_RESOURCE_DESC` and other parameters for created 118 resource. This structure describes parameters of the desired memory allocation, 119 including choice of `D3D12_HEAP_TYPE`. 120 121 The function also returns a new object of type D3D12MA::Allocation, created along 122 with usual `ID3D12Resource`. It represents allocated memory and can be queried 123 for size, offset, `ID3D12Resource`, and `ID3D12Heap` if needed. 124 125 \code 126 D3D12_RESOURCE_DESC resourceDesc = {}; 127 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 128 resourceDesc.Alignment = 0; 129 resourceDesc.Width = 1024; 130 resourceDesc.Height = 1024; 131 resourceDesc.DepthOrArraySize = 1; 132 resourceDesc.MipLevels = 1; 133 resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 134 resourceDesc.SampleDesc.Count = 1; 135 resourceDesc.SampleDesc.Quality = 0; 136 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 137 resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 138 139 D3D12MA::ALLOCATION_DESC allocationDesc = {}; 140 allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; 141 142 D3D12Resource* resource; 143 D3D12MA::Allocation* allocation; 144 HRESULT hr = allocator->CreateResource( 145 &allocationDesc, 146 &resourceDesc, 147 D3D12_RESOURCE_STATE_COPY_DEST, 148 NULL, 149 &allocation, 150 IID_PPV_ARGS(&resource)); 151 \endcode 152 153 You need to remember both resource and allocation objects and destroy them 154 separately when no longer needed. 155 156 \code 157 allocation->Release(); 158 resource->Release(); 159 \endcode 160 161 The advantage of using the allocator instead of creating committed resource, and 162 the main purpose of this library, is that it can decide to allocate bigger memory 163 heap internally using `ID3D12Device::CreateHeap` and place multiple resources in 164 it, at different offsets, using `ID3D12Device::CreatePlacedResource`. The library 165 manages its own collection of allocated memory blocks (heaps) and remembers which 166 parts of them are occupied and which parts are free to be used for new resources. 167 168 It is important to remember that resources created as placed don't have their memory 169 initialized to zeros, but may contain garbage data, so they need to be fully initialized 170 before usage, e.g. using Clear (`ClearRenderTargetView`), Discard (`DiscardResource`), 171 or copy (`CopyResource`). 172 173 The library also automatically handles resource heap tier. 174 When `D3D12_FEATURE_DATA_D3D12_OPTIONS::ResourceHeapTier` equals `D3D12_RESOURCE_HEAP_TIER_1`, 175 resources of 3 types: buffers, textures that are render targets or depth-stencil, 176 and other textures must be kept in separate heaps. When `D3D12_RESOURCE_HEAP_TIER_2`, 177 they can be kept together. By using this library, you don't need to handle this 178 manually. 179 180 181 \section quick_start_mapping_memory Mapping memory 182 183 The process of getting regular CPU-side pointer to the memory of a resource in 184 Direct3D is called "mapping". There are rules and restrictions to this process, 185 as described in D3D12 documentation of [ID3D12Resource::Map method](https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12resource-map). 186 187 Mapping happens on the level of particular resources, not entire memory heaps, 188 and so it is out of scope of this library. Just as the linked documentation says: 189 190 - Returned pointer refers to data of particular subresource, not entire memory heap. 191 - You can map same resource multiple times. It is ref-counted internally. 192 - Mapping is thread-safe. 193 - Unmapping is not required before resource destruction. 194 - Unmapping may not be required before using written data - some heap types on 195 some platforms support resources persistently mapped. 196 197 When using this library, you can map and use your resources normally without 198 considering whether they are created as committed resources or placed resources in one large heap. 199 200 Example for buffer created and filled in `UPLOAD` heap type: 201 202 \code 203 const UINT64 bufSize = 65536; 204 const float* bufData = (...); 205 206 D3D12_RESOURCE_DESC resourceDesc = {}; 207 resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 208 resourceDesc.Alignment = 0; 209 resourceDesc.Width = bufSize; 210 resourceDesc.Height = 1; 211 resourceDesc.DepthOrArraySize = 1; 212 resourceDesc.MipLevels = 1; 213 resourceDesc.Format = DXGI_FORMAT_UNKNOWN; 214 resourceDesc.SampleDesc.Count = 1; 215 resourceDesc.SampleDesc.Quality = 0; 216 resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 217 resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; 218 219 D3D12MA::ALLOCATION_DESC allocationDesc = {}; 220 allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; 221 222 D3D12Resource* resource; 223 D3D12MA::Allocation* allocation; 224 HRESULT hr = allocator->CreateResource( 225 &allocationDesc, 226 &resourceDesc, 227 D3D12_RESOURCE_STATE_GENERIC_READ, 228 NULL, 229 &allocation, 230 IID_PPV_ARGS(&resource)); 231 232 void* mappedPtr; 233 hr = resource->Map(0, NULL, &mappedPtr); 234 235 memcpy(mappedPtr, bufData, bufSize); 236 237 resource->Unmap(0, NULL); 238 \endcode 239 240 241 \page resource_aliasing Resource aliasing (overlap) 242 243 New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory 244 management, give an opportunity to alias (overlap) multiple resources in the 245 same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). 246 It can be useful to save video memory, but it must be used with caution. 247 248 For example, if you know the flow of your whole render frame in advance, you 249 are going to use some intermediate textures or buffers only during a small range of render passes, 250 and you know these ranges don't overlap in time, you can create these resources in 251 the same place in memory, even if they have completely different parameters (width, height, format etc.). 252 253 ![Resource aliasing (overlap)](../gfx/Aliasing.png) 254 255 Such scenario is possible using D3D12MA, but you need to create your resources 256 using special function D3D12MA::Allocator::CreateAliasingResource. 257 Before that, you need to allocate memory with parameters calculated using formula: 258 259 - allocation size = max(size of each resource) 260 - allocation alignment = max(alignment of each resource) 261 262 Following example shows two different textures created in the same place in memory, 263 allocated to fit largest of them. 264 265 \code 266 D3D12_RESOURCE_DESC resDesc1 = {}; 267 resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 268 resDesc1.Alignment = 0; 269 resDesc1.Width = 1920; 270 resDesc1.Height = 1080; 271 resDesc1.DepthOrArraySize = 1; 272 resDesc1.MipLevels = 1; 273 resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 274 resDesc1.SampleDesc.Count = 1; 275 resDesc1.SampleDesc.Quality = 0; 276 resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 277 resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; 278 279 D3D12_RESOURCE_DESC resDesc2 = {}; 280 resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 281 resDesc2.Alignment = 0; 282 resDesc2.Width = 1024; 283 resDesc2.Height = 1024; 284 resDesc2.DepthOrArraySize = 1; 285 resDesc2.MipLevels = 0; 286 resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 287 resDesc2.SampleDesc.Count = 1; 288 resDesc2.SampleDesc.Quality = 0; 289 resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 290 resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; 291 292 const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 = 293 device->GetResourceAllocationInfo(0, 1, &resDesc1); 294 const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 = 295 device->GetResourceAllocationInfo(0, 1, &resDesc2); 296 297 D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {}; 298 finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment); 299 finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes); 300 301 D3D12MA::ALLOCATION_DESC allocDesc = {}; 302 allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; 303 allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; 304 305 D3D12MA::Allocation* alloc; 306 hr = allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc); 307 assert(alloc != NULL && alloc->GetHeap() != NULL); 308 309 ID3D12Resource* res1; 310 hr = allocator->CreateAliasingResource( 311 alloc, 312 0, // AllocationLocalOffset 313 &resDesc1, 314 D3D12_RESOURCE_STATE_COMMON, 315 NULL, // pOptimizedClearValue 316 IID_PPV_ARGS(&res1)); 317 318 ID3D12Resource* res2; 319 hr = allocator->CreateAliasingResource( 320 alloc, 321 0, // AllocationLocalOffset 322 &resDesc2, 323 D3D12_RESOURCE_STATE_COMMON, 324 NULL, // pOptimizedClearValue 325 IID_PPV_ARGS(&res2)); 326 327 // You can use res1 and res2, but not at the same time! 328 329 res2->Release(); 330 res1->Release(); 331 alloc->Release(); 332 \endcode 333 334 Remember that using resouces that alias in memory requires proper synchronization. 335 You need to issue a special barrier of type `D3D12_RESOURCE_BARRIER_TYPE_ALIASING`. 336 You also need to treat a resource after aliasing as uninitialized - containing garbage data. 337 For example, if you use `res1` and then want to use `res2`, you need to first initialize `res2` 338 using either Clear, Discard, or Copy to the entire resource. 339 340 Additional considerations: 341 342 - D3D12 also allows to interpret contents of memory between aliasing resources consistently in some cases, 343 which is called "data inheritance". For details, see 344 Microsoft documentation, chapter [Memory Aliasing and Data Inheritance](https://docs.microsoft.com/en-us/windows/win32/direct3d12/memory-aliasing-and-data-inheritance). 345 - You can create more complex layout where different textures and buffers are bound 346 at different offsets inside one large allocation. For example, one can imagine 347 a big texture used in some render passes, aliasing with a set of many small buffers 348 used in some further passes. To bind a resource at non-zero offset of an allocation, 349 call D3D12MA::Allocator::CreateAliasingResource with appropriate value of `AllocationLocalOffset` parameter. 350 - Resources of the three categories: buffers, textures with `RENDER_TARGET` or `DEPTH_STENCIL` flags, and all other textures, 351 can be placed in the same memory only when `allocator->GetD3D12Options().ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2`. 352 Otherwise they must be placed in different memory heap types, and thus aliasing them is not possible. 353 354 355 \page reserving_memory Reserving minimum amount of memory 356 357 The library automatically allocates and frees memory heaps. 358 It also applies some hysteresis so that it doesn't allocate and free entire heap 359 when you repeatedly create and release a single resource. 360 However, if you want to make sure certain number of bytes is always allocated as heaps in a specific pool, 361 you can use functions designed for this purpose: 362 363 - For default heaps use D3D12MA::Allocator::SetDefaultHeapMinBytes. 364 - For custom heaps use D3D12MA::Pool::SetMinBytes. 365 366 Default is 0. You can change this parameter any time. 367 Setting it to higher value may cause new heaps to be allocated. 368 If this allocation fails, the function returns appropriate error code, but the parameter remains set to the new value. 369 Setting it to lower value may cause some empty heaps to be released. 370 371 You can always call D3D12MA::Allocator::SetDefaultHeapMinBytes for 3 sets of heap flags separately: 372 `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. 373 When ResourceHeapTier = 2, so that all types of resourced are kept together, 374 these 3 values as simply summed up to calculate minimum amount of bytes for default pool with certain heap type. 375 Alternatively, when ResourceHeapTier = 2, you can call this function with 376 `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0. This will set a single value for the default pool and 377 will override the sum of those three. 378 379 Reservation of minimum number of bytes interacts correctly with 380 D3D12MA::POOL_DESC::MinBlockCount and D3D12MA::POOL_DESC::MaxBlockCount. 381 For example, free blocks (heaps) of a custom pool will be released only when 382 their number doesn't fall below `MinBlockCount` and their sum size doesn't fall below `MinBytes`. 383 384 Some restrictions apply: 385 386 - Setting `MinBytes` doesn't interact with memory budget. The allocator tries 387 to create additional heaps when necessary without checking if they will exceed the budget. 388 - Resources created as committed don't count into the number of bytes compared with `MinBytes` set. 389 Only placed resources are considered. 390 391 392 \page virtual_allocator Virtual allocator 393 394 As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". 395 It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". 396 You can use it to allocate your own memory or other objects, even completely unrelated to D3D12. 397 A common use case is sub-allocation of pieces of one large GPU buffer. 398 399 \section virtual_allocator_creating_virtual_block Creating virtual block 400 401 To use this functionality, there is no main "allocator" object. 402 You don't need to have D3D12MA::Allocator object created. 403 All you need to do is to create a separate D3D12MA::VirtualBlock object for each block of memory you want to be managed by the allocator: 404 405 -# Fill in D3D12MA::ALLOCATOR_DESC structure. 406 -# Call D3D12MA::CreateVirtualBlock. Get new D3D12MA::VirtualBlock object. 407 408 Example: 409 410 \code 411 D3D12MA::VIRTUAL_BLOCK_DESC blockDesc = {}; 412 blockDesc.Size = 1048576; // 1 MB 413 414 D3D12MA::VirtualBlock *block; 415 HRESULT hr = CreateVirtualBlock(&blockDesc, &block); 416 \endcode 417 418 \section virtual_allocator_making_virtual_allocations Making virtual allocations 419 420 D3D12MA::VirtualBlock object contains internal data structure that keeps track of free and occupied regions 421 using the same code as the main D3D12 memory allocator. 422 However, there is no "virtual allocation" object. 423 When you request a new allocation, a `UINT64` number is returned. 424 It is an offset inside the block where the allocation has been placed, but it also uniquely identifies the allocation within this block. 425 426 In order to make an allocation: 427 428 -# Fill in D3D12MA::VIRTUAL_ALLOCATION_DESC structure. 429 -# Call D3D12MA::VirtualBlock::Allocate. Get new `UINT64 offset` that identifies the allocation. 430 431 Example: 432 433 \code 434 D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {}; 435 allocDesc.Size = 4096; // 4 KB 436 437 UINT64 allocOffset; 438 hr = block->Allocate(&allocDesc, &allocOffset); 439 if(SUCCEEDED(hr)) 440 { 441 // Use the 4 KB of your memory starting at allocOffset. 442 } 443 else 444 { 445 // Allocation failed - no space for it could be found. Handle this error! 446 } 447 \endcode 448 449 \section virtual_allocator_deallocation Deallocation 450 451 When no longer needed, an allocation can be freed by calling D3D12MA::VirtualBlock::FreeAllocation. 452 You can only pass to this function the exact offset that was previously returned by D3D12MA::VirtualBlock::Allocate 453 and not any other location within the memory. 454 455 When whole block is no longer needed, the block object can be released by calling D3D12MA::VirtualBlock::Release. 456 All allocations must be freed before the block is destroyed, which is checked internally by an assert. 457 However, if you don't want to call `block->FreeAllocation` for each allocation, you can use D3D12MA::VirtualBlock::Clear to free them all at once - 458 a feature not available in normal D3D12 memory allocator. Example: 459 460 \code 461 block->FreeAllocation(allocOffset); 462 block->Release(); 463 \endcode 464 465 \section virtual_allocator_allocation_parameters Allocation parameters 466 467 You can attach a custom pointer to each allocation by using D3D12MA::VirtualBlock::SetAllocationUserData. 468 Its default value is `NULL`. 469 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 470 larger data structure containing more information. Example: 471 472 \code 473 struct CustomAllocData 474 { 475 std::string m_AllocName; 476 }; 477 CustomAllocData* allocData = new CustomAllocData(); 478 allocData->m_AllocName = "My allocation 1"; 479 block->SetAllocationUserData(allocOffset, allocData); 480 \endcode 481 482 The pointer can later be fetched, along with allocation size, by passing the allocation offset to function 483 D3D12MA::VirtualBlock::GetAllocationInfo and inspecting returned structure D3D12MA::VIRTUAL_ALLOCATION_INFO. 484 If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! 485 Example: 486 487 \code 488 VIRTUAL_ALLOCATION_INFO allocInfo; 489 block->GetAllocationInfo(allocOffset, &allocInfo); 490 delete (CustomAllocData*)allocInfo.pUserData; 491 492 block->FreeAllocation(allocOffset); 493 \endcode 494 495 \section virtual_allocator_alignment_and_units Alignment and units 496 497 It feels natural to express sizes and offsets in bytes. 498 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 499 D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment to request it. Example: 500 501 \code 502 D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {}; 503 allocDesc.Size = 4096; // 4 KB 504 allocDesc.Alignment = 4; // Returned offset must be a multiply of 4 B 505 506 UINT64 allocOffset; 507 hr = block->Allocate(&allocDesc, &allocOffset); 508 \endcode 509 510 Alignments of different allocations made from one block may vary. 511 However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, 512 you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. 513 It might be more convenient, but you need to make sure to use this new unit consistently in all the places: 514 515 - D3D12MA::VIRTUAL_BLOCK_DESC::Size 516 - D3D12MA::VIRTUAL_ALLOCATION_DESC::Size and D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment 517 - Using offset returned by D3D12MA::VirtualBlock::Allocate 518 519 \section virtual_allocator_statistics Statistics 520 521 You can obtain statistics of a virtual block using D3D12MA::VirtualBlock::CalculateStats. 522 The function fills structure D3D12MA::StatInfo - same as used by the normal D3D12 memory allocator. 523 Example: 524 525 \code 526 D3D12MA::StatInfo statInfo; 527 block->CalculateStats(&statInfo); 528 printf("My virtual block has %llu bytes used by %u virtual allocations\n", 529 statInfo.UsedBytes, statInfo.AllocationCount); 530 \endcode 531 532 You can also request a full list of allocations and free regions as a string in JSON format by calling 533 D3D12MA::VirtualBlock::BuildStatsString. 534 Returned string must be later freed using D3D12MA::VirtualBlock::FreeStatsString. 535 The format of this string may differ from the one returned by the main D3D12 allocator, but it is similar. 536 537 \section virtual_allocator_additional_considerations Additional considerations 538 539 Note that the "virtual allocator" functionality is implemented on a level of individual memory blocks. 540 Keeping track of a whole collection of blocks, allocating new ones when out of free space, 541 deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. 542 543 544 \page configuration Configuration 545 546 Please check file `D3D12MemAlloc.cpp` lines between "Configuration Begin" and 547 "Configuration End" to find macros that you can define to change the behavior of 548 the library, primarily for debugging purposes. 549 550 \section custom_memory_allocator Custom CPU memory allocator 551 552 If you use custom allocator for CPU memory rather than default C++ operator `new` 553 and `delete` or `malloc` and `free` functions, you can make this library using 554 your allocator as well by filling structure D3D12MA::ALLOCATION_CALLBACKS and 555 passing it as optional member D3D12MA::ALLOCATOR_DESC::pAllocationCallbacks. 556 Functions pointed there will be used by the library to make any CPU-side 557 allocations. Example: 558 559 \code 560 #include <malloc.h> 561 562 void* CustomAllocate(size_t Size, size_t Alignment, void* pUserData) 563 { 564 void* memory = _aligned_malloc(Size, Alignment); 565 // Your extra bookkeeping here... 566 return memory; 567 } 568 569 void CustomFree(void* pMemory, void* pUserData) 570 { 571 // Your extra bookkeeping here... 572 _aligned_free(pMemory); 573 } 574 575 (...) 576 577 D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {}; 578 allocationCallbacks.pAllocate = &CustomAllocate; 579 allocationCallbacks.pFree = &CustomFree; 580 581 D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; 582 allocatorDesc.pDevice = device; 583 allocatorDesc.pAdapter = adapter; 584 allocatorDesc.pAllocationCallbacks = &allocationCallbacks; 585 586 D3D12MA::Allocator* allocator; 587 HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator); 588 \endcode 589 590 591 \page general_considerations General considerations 592 593 \section general_considerations_thread_safety Thread safety 594 595 - The library has no global state, so separate D3D12MA::Allocator objects can be used independently. 596 In typical applications there should be no need to create multiple such objects though - one per `ID3D12Device` is enough. 597 - All calls to methods of D3D12MA::Allocator class are safe to be made from multiple 598 threads simultaneously because they are synchronized internally when needed. 599 - When the allocator is created with D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED, 600 calls to methods of D3D12MA::Allocator class must be made from a single thread or synchronized by the user. 601 Using this flag may improve performance. 602 603 \section general_considerations_future_plans Future plans 604 605 Features planned for future releases: 606 607 Near future: feature parity with [Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/), including: 608 609 - Alternative allocation algorithms: linear allocator, buddy allocator 610 - Support for priorities using `ID3D12Device1::SetResidencyPriority` 611 612 Later: 613 614 - Memory defragmentation 615 - Support for multi-GPU (multi-adapter) 616 617 \section general_considerations_features_not_supported Features not supported 618 619 Features deliberately excluded from the scope of this library: 620 621 - Descriptor allocation. Although also called "heaps", objects that represent 622 descriptors are separate part of the D3D12 API from buffers and textures. 623 - Support for `D3D12_HEAP_TYPE_CUSTOM`. Only the default heap types are supported: 624 `UPLOAD`, `DEFAULT`, `READBACK`. 625 - Support for reserved (tiled) resources. We don't recommend using them. 626 - Support for `ID3D12Device::Evict` and `MakeResident`. We don't recommend using them. 627 - Handling CPU memory allocation failures. When dynamically creating small C++ 628 objects in CPU memory (not the GPU memory), allocation failures are not 629 handled gracefully, because that would complicate code significantly and 630 is usually not needed in desktop PC applications anyway. 631 Success of an allocation is just checked with an assert. 632 - Code free of any compiler warnings - especially those that would require complicating the code 633 just to please the compiler complaining about unused parameters, variables, or expressions being 634 constant in Relese configuration, e.g. because they are only used inside an assert. 635 - This is a C++ library. 636 Bindings or ports to any other programming languages are welcomed as external projects and 637 are not going to be included into this repository. 638 */ 639 640 // Define this macro to 0 to disable usage of DXGI 1.4 (needed for IDXGIAdapter3 and query for memory budget). 641 #ifndef D3D12MA_DXGI_1_4 642 #define D3D12MA_DXGI_1_4 1 643 #endif 644 645 // If using this library on a platform different than Windows PC, you should 646 // include D3D12-compatible header before this library on your own and define this macro. 647 #ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED 648 #include <d3d12.h> 649 #include <dxgi.h> 650 #endif 651 652 /* 653 When defined to value other than 0, the library will try to use 654 D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT or D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT 655 for created textures when possible, which can save memory because some small textures 656 may get their alignment 4K and their size a multiply of 4K instead of 64K. 657 658 #define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 0 659 Disables small texture alignment. 660 #define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1 661 Enables conservative algorithm that will use small alignment only for some textures 662 that are surely known to support it. 663 #define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 2 664 Enables query for small alignment to D3D12 (based on Microsoft sample) which will 665 enable small alignment for more textures, but will also generate D3D Debug Layer 666 error #721 on call to ID3D12Device::GetResourceAllocationInfo, which you should just 667 ignore. 668 */ 669 #ifndef D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 670 #define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1 671 #endif 672 673 /// \cond INTERNAL 674 675 #define D3D12MA_CLASS_NO_COPY(className) \ 676 private: \ 677 className(const className&) = delete; \ 678 className(className&&) = delete; \ 679 className& operator=(const className&) = delete; \ 680 className& operator=(className&&) = delete; 681 682 // To be used with MAKE_HRESULT to define custom error codes. 683 #define FACILITY_D3D12MA 3542 684 685 /// \endcond 686 687 namespace D3D12MA 688 { 689 690 /// \cond INTERNAL 691 class AllocatorPimpl; 692 class PoolPimpl; 693 class NormalBlock; 694 class BlockVector; 695 class JsonWriter; 696 class VirtualBlockPimpl; 697 /// \endcond 698 699 class Pool; 700 class Allocator; 701 struct StatInfo; 702 703 /// Pointer to custom callback function that allocates CPU memory. 704 typedef void* (*ALLOCATE_FUNC_PTR)(size_t Size, size_t Alignment, void* pUserData); 705 /** 706 \brief Pointer to custom callback function that deallocates CPU memory. 707 708 `pMemory = null` should be accepted and ignored. 709 */ 710 typedef void (*FREE_FUNC_PTR)(void* pMemory, void* pUserData); 711 712 /// Custom callbacks to CPU memory allocation functions. 713 struct ALLOCATION_CALLBACKS 714 { 715 /// %Allocation function. 716 ALLOCATE_FUNC_PTR pAllocate; 717 /// Dellocation function. 718 FREE_FUNC_PTR pFree; 719 /// Custom data that will be passed to allocation and deallocation functions as `pUserData` parameter. 720 void* pUserData; 721 }; 722 723 /// \brief Bit flags to be used with ALLOCATION_DESC::Flags. 724 typedef enum ALLOCATION_FLAGS 725 { 726 /// Zero 727 ALLOCATION_FLAG_NONE = 0, 728 729 /** 730 Set this flag if the allocation should have its own dedicated memory allocation (committed resource with implicit heap). 731 732 Use it for special, big resources, like fullscreen textures used as render targets. 733 */ 734 ALLOCATION_FLAG_COMMITTED = 0x1, 735 736 /** 737 Set this flag to only try to allocate from existing memory heaps and never create new such heap. 738 739 If new allocation cannot be placed in any of the existing heaps, allocation 740 fails with `E_OUTOFMEMORY` error. 741 742 You should not use D3D12MA::ALLOCATION_FLAG_COMMITTED and 743 D3D12MA::ALLOCATION_FLAG_NEVER_ALLOCATE at the same time. It makes no sense. 744 */ 745 ALLOCATION_FLAG_NEVER_ALLOCATE = 0x2, 746 747 /** Create allocation only if additional memory required for it, if any, won't exceed 748 memory budget. Otherwise return `E_OUTOFMEMORY`. 749 */ 750 ALLOCATION_FLAG_WITHIN_BUDGET = 0x4, 751 } ALLOCATION_FLAGS; 752 753 /// \brief Parameters of created D3D12MA::Allocation object. To be used with Allocator::CreateResource. 754 struct ALLOCATION_DESC 755 { 756 /// Flags. 757 ALLOCATION_FLAGS Flags; 758 /** \brief The type of memory heap where the new allocation should be placed. 759 760 It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. 761 762 When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored. 763 */ 764 D3D12_HEAP_TYPE HeapType; 765 /** \brief Additional heap flags to be used when allocating memory. 766 767 In most cases it can be 0. 768 769 - If you use D3D12MA::Allocator::CreateResource(), you don't need to care. 770 Necessary flag `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, 771 or `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES` is added automatically. 772 - If you use D3D12MA::Allocator::AllocateMemory(), you should specify one of those `ALLOW_ONLY` flags. 773 Except when you validate that D3D12MA::Allocator::GetD3D12Options()`.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1` - 774 then you can leave it 0. 775 - You can specify additional flags if needed. Then the memory will always be allocated as 776 separate block using `D3D12Device::CreateCommittedResource` or `CreateHeap`, not as part of an existing larget block. 777 778 When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored. 779 */ 780 D3D12_HEAP_FLAGS ExtraHeapFlags; 781 /** \brief Custom pool to place the new resource in. Optional. 782 783 When not NULL, the resource will be created inside specified custom pool. 784 It will then never be created as committed. 785 */ 786 Pool* CustomPool; 787 }; 788 789 /** \brief Represents single memory allocation. 790 791 It may be either implicit memory heap dedicated to a single resource or a 792 specific region of a bigger heap plus unique offset. 793 794 To create such object, fill structure D3D12MA::ALLOCATION_DESC and call function 795 Allocator::CreateResource. 796 797 The object remembers size and some other information. 798 To retrieve this information, use methods of this class. 799 800 The object also remembers `ID3D12Resource` and "owns" a reference to it, 801 so it calls `Release()` on the resource when destroyed. 802 */ 803 class Allocation 804 { 805 public: 806 /** \brief Deletes this object. 807 808 This function must be used instead of destructor, which is private. 809 There is no reference counting involved. 810 */ 811 void Release(); 812 813 /** \brief Returns offset in bytes from the start of memory heap. 814 815 If the Allocation represents committed resource with implicit heap, returns 0. 816 */ 817 UINT64 GetOffset() const; 818 819 /** \brief Returns size in bytes of the resource. 820 821 Works also with committed resources. 822 */ GetSize()823 UINT64 GetSize() const { return m_Size; } 824 825 /** \brief Returns D3D12 resource associated with this object. 826 827 Calling this method doesn't increment resource's reference counter. 828 */ GetResource()829 ID3D12Resource* GetResource() const { return m_Resource; } 830 831 /** \brief Returns memory heap that the resource is created in. 832 833 If the Allocation represents committed resource with implicit heap, returns NULL. 834 */ 835 ID3D12Heap* GetHeap() const; 836 837 /** \brief Associates a name with the allocation object. This name is for use in debug diagnostics and tools. 838 839 Internal copy of the string is made, so the memory pointed by the argument can be 840 changed of freed immediately after this call. 841 842 `Name` can be null. 843 */ 844 void SetName(LPCWSTR Name); 845 846 /** \brief Returns the name associated with the allocation object. 847 848 Returned string points to an internal copy. 849 850 If no name was associated with the allocation, returns null. 851 */ GetName()852 LPCWSTR GetName() const { return m_Name; } 853 854 /** \brief Returns `TRUE` if the memory of the allocation was filled with zeros when the allocation was created. 855 856 Returns `TRUE` only if the allocator is sure that the entire memory where the 857 allocation was created was filled with zeros at the moment the allocation was made. 858 859 Returns `FALSE` if the memory could potentially contain garbage data. 860 If it's a render-target or depth-stencil texture, it then needs proper 861 initialization with `ClearRenderTargetView`, `ClearDepthStencilView`, `DiscardResource`, 862 or a copy operation, as described on page: 863 [ID3D12Device::CreatePlacedResource method - Notes on the required resource initialization](https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource#notes-on-the-required-resource-initialization). 864 Please note that rendering a fullscreen triangle or quad to the texture as 865 a render target is not a proper way of initialization! 866 867 See also articles: 868 ["Coming to DirectX 12: More control over memory allocation"](https://devblogs.microsoft.com/directx/coming-to-directx-12-more-control-over-memory-allocation/), 869 ["Initializing DX12 Textures After Allocation and Aliasing"](https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing). 870 */ WasZeroInitialized()871 BOOL WasZeroInitialized() const { return m_PackedData.WasZeroInitialized(); } 872 873 private: 874 friend class AllocatorPimpl; 875 friend class BlockVector; 876 friend class JsonWriter; 877 template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); 878 template<typename T> friend class PoolAllocator; 879 880 enum Type 881 { 882 TYPE_COMMITTED, 883 TYPE_PLACED, 884 TYPE_HEAP, 885 TYPE_COUNT 886 }; 887 888 AllocatorPimpl* m_Allocator; 889 UINT64 m_Size; 890 ID3D12Resource* m_Resource; 891 UINT m_CreationFrameIndex; 892 wchar_t* m_Name; 893 894 union 895 { 896 struct 897 { 898 D3D12_HEAP_TYPE heapType; 899 } m_Committed; 900 901 struct 902 { 903 UINT64 offset; 904 NormalBlock* block; 905 } m_Placed; 906 907 struct 908 { 909 D3D12_HEAP_TYPE heapType; 910 ID3D12Heap* heap; 911 } m_Heap; 912 }; 913 914 struct PackedData 915 { 916 public: PackedDataPackedData917 PackedData() : 918 m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0), m_WasZeroInitialized(0) { } 919 GetTypePackedData920 Type GetType() const { return (Type)m_Type; } GetResourceDimensionPackedData921 D3D12_RESOURCE_DIMENSION GetResourceDimension() const { return (D3D12_RESOURCE_DIMENSION)m_ResourceDimension; } GetResourceFlagsPackedData922 D3D12_RESOURCE_FLAGS GetResourceFlags() const { return (D3D12_RESOURCE_FLAGS)m_ResourceFlags; } GetTextureLayoutPackedData923 D3D12_TEXTURE_LAYOUT GetTextureLayout() const { return (D3D12_TEXTURE_LAYOUT)m_TextureLayout; } WasZeroInitializedPackedData924 BOOL WasZeroInitialized() const { return (BOOL)m_WasZeroInitialized; } 925 926 void SetType(Type type); 927 void SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension); 928 void SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags); 929 void SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout); SetWasZeroInitializedPackedData930 void SetWasZeroInitialized(BOOL wasZeroInitialized) { m_WasZeroInitialized = wasZeroInitialized ? 1 : 0; } 931 932 private: 933 UINT m_Type : 2; // enum Type 934 UINT m_ResourceDimension : 3; // enum D3D12_RESOURCE_DIMENSION 935 UINT m_ResourceFlags : 24; // flags D3D12_RESOURCE_FLAGS 936 UINT m_TextureLayout : 9; // enum D3D12_TEXTURE_LAYOUT 937 UINT m_WasZeroInitialized : 1; // BOOL 938 } m_PackedData; 939 940 Allocation(AllocatorPimpl* allocator, UINT64 size, BOOL wasZeroInitialized); 941 ~Allocation(); 942 void InitCommitted(D3D12_HEAP_TYPE heapType); 943 void InitPlaced(UINT64 offset, UINT64 alignment, NormalBlock* block); 944 void InitHeap(D3D12_HEAP_TYPE heapType, ID3D12Heap* heap); 945 void SetResource(ID3D12Resource* resource, const D3D12_RESOURCE_DESC* pResourceDesc); 946 void FreeName(); 947 948 D3D12MA_CLASS_NO_COPY(Allocation) 949 }; 950 951 /// \brief Parameters of created D3D12MA::Pool object. To be used with D3D12MA::Allocator::CreatePool. 952 struct POOL_DESC 953 { 954 /** \brief The type of memory heap where allocations of this pool should be placed. 955 956 It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. 957 */ 958 D3D12_HEAP_TYPE HeapType; 959 /** \brief Heap flags to be used when allocating heaps of this pool. 960 961 It should contain one of these values, depending on type of resources you are going to create in this heap: 962 `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, 963 `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, 964 `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. 965 Except if ResourceHeapTier = 2, then it may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0. 966 967 You can specify additional flags if needed. 968 */ 969 D3D12_HEAP_FLAGS HeapFlags; 970 /** \brief Size of a single heap (memory block) to be allocated as part of this pool, in bytes. Optional. 971 972 Specify nonzero to set explicit, constant size of memory blocks used by this pool. 973 Leave 0 to use default and let the library manage block sizes automatically. 974 Then sizes of particular blocks may vary. 975 */ 976 UINT64 BlockSize; 977 /** \brief Minimum number of heaps (memory blocks) to be always allocated in this pool, even if they stay empty. Optional. 978 979 Set to 0 to have no preallocated blocks and allow the pool be completely empty. 980 */ 981 UINT MinBlockCount; 982 /** \brief Maximum number of heaps (memory blocks) that can be allocated in this pool. Optional. 983 984 Set to 0 to use default, which is `UINT64_MAX`, which means no limit. 985 986 Set to same value as D3D12MA::POOL_DESC::MinBlockCount to have fixed amount of memory allocated 987 throughout whole lifetime of this pool. 988 */ 989 UINT MaxBlockCount; 990 }; 991 992 /** \brief Custom memory pool 993 994 Represents a separate set of heaps (memory blocks) that can be used to create 995 D3D12MA::Allocation-s and resources in it. Usually there is no need to create custom 996 pools - creating resources in default pool is sufficient. 997 998 To create custom pool, fill D3D12MA::POOL_DESC and call D3D12MA::Allocator::CreatePool. 999 */ 1000 class Pool 1001 { 1002 public: 1003 /** \brief Deletes pool object, frees D3D12 heaps (memory blocks) managed by it. Allocations and resources must already be released! 1004 1005 It doesn't delete allocations and resources created in this pool. They must be all 1006 released before calling this function! 1007 */ 1008 void Release(); 1009 1010 /** \brief Returns copy of parameters of the pool. 1011 1012 These are the same parameters as passed to D3D12MA::Allocator::CreatePool. 1013 */ 1014 POOL_DESC GetDesc() const; 1015 1016 /** \brief Sets the minimum number of bytes that should always be allocated (reserved) in this pool. 1017 1018 See also: \subpage reserving_memory. 1019 */ 1020 HRESULT SetMinBytes(UINT64 minBytes); 1021 1022 /** \brief Retrieves statistics from the current state of this pool. 1023 */ 1024 void CalculateStats(StatInfo* pStats); 1025 1026 /** \brief Associates a name with the pool. This name is for use in debug diagnostics and tools. 1027 1028 Internal copy of the string is made, so the memory pointed by the argument can be 1029 changed of freed immediately after this call. 1030 1031 `Name` can be NULL. 1032 */ 1033 void SetName(LPCWSTR Name); 1034 1035 /** \brief Returns the name associated with the pool object. 1036 1037 Returned string points to an internal copy. 1038 1039 If no name was associated with the allocation, returns NULL. 1040 */ 1041 LPCWSTR GetName() const; 1042 1043 private: 1044 friend class Allocator; 1045 friend class AllocatorPimpl; 1046 template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); 1047 1048 PoolPimpl* m_Pimpl; 1049 1050 Pool(Allocator* allocator, const POOL_DESC &desc); 1051 ~Pool(); 1052 1053 D3D12MA_CLASS_NO_COPY(Pool) 1054 }; 1055 1056 /// \brief Bit flags to be used with ALLOCATOR_DESC::Flags. 1057 typedef enum ALLOCATOR_FLAGS 1058 { 1059 /// Zero 1060 ALLOCATOR_FLAG_NONE = 0, 1061 1062 /** 1063 Allocator and all objects created from it will not be synchronized internally, 1064 so you must guarantee they are used from only one thread at a time or 1065 synchronized by you. 1066 1067 Using this flag may increase performance because internal mutexes are not used. 1068 */ 1069 ALLOCATOR_FLAG_SINGLETHREADED = 0x1, 1070 1071 /** 1072 Every allocation will have its own memory block. 1073 To be used for debugging purposes. 1074 */ 1075 ALLOCATOR_FLAG_ALWAYS_COMMITTED = 0x2, 1076 } ALLOCATOR_FLAGS; 1077 1078 /// \brief Parameters of created Allocator object. To be used with CreateAllocator(). 1079 struct ALLOCATOR_DESC 1080 { 1081 /// Flags. 1082 ALLOCATOR_FLAGS Flags; 1083 1084 /** Direct3D device object that the allocator should be attached to. 1085 1086 Allocator is doing `AddRef`/`Release` on this object. 1087 */ 1088 ID3D12Device* pDevice; 1089 1090 /** \brief Preferred size of a single `ID3D12Heap` block to be allocated. 1091 1092 Set to 0 to use default, which is currently 256 MiB. 1093 */ 1094 UINT64 PreferredBlockSize; 1095 1096 /** \brief Custom CPU memory allocation callbacks. Optional. 1097 1098 Optional, can be null. When specified, will be used for all CPU-side memory allocations. 1099 */ 1100 const ALLOCATION_CALLBACKS* pAllocationCallbacks; 1101 1102 /** DXGI Adapter object that you use for D3D12 and this allocator. 1103 1104 Allocator is doing `AddRef`/`Release` on this object. 1105 */ 1106 IDXGIAdapter* pAdapter; 1107 }; 1108 1109 /** 1110 \brief Number of D3D12 memory heap types supported. 1111 */ 1112 const UINT HEAP_TYPE_COUNT = 3; 1113 1114 /** 1115 \brief Calculated statistics of memory usage in entire allocator. 1116 */ 1117 struct StatInfo 1118 { 1119 /// Number of memory blocks (heaps) allocated. 1120 UINT BlockCount; 1121 /// Number of D3D12MA::Allocation objects allocated. 1122 UINT AllocationCount; 1123 /// Number of free ranges of memory between allocations. 1124 UINT UnusedRangeCount; 1125 /// Total number of bytes occupied by all allocations. 1126 UINT64 UsedBytes; 1127 /// Total number of bytes occupied by unused ranges. 1128 UINT64 UnusedBytes; 1129 UINT64 AllocationSizeMin; 1130 UINT64 AllocationSizeAvg; 1131 UINT64 AllocationSizeMax; 1132 UINT64 UnusedRangeSizeMin; 1133 UINT64 UnusedRangeSizeAvg; 1134 UINT64 UnusedRangeSizeMax; 1135 }; 1136 1137 /** 1138 \brief General statistics from the current state of the allocator. 1139 */ 1140 struct Stats 1141 { 1142 /// Total statistics from all heap types. 1143 StatInfo Total; 1144 /** 1145 One StatInfo for each type of heap located at the following indices: 1146 0 - DEFAULT, 1 - UPLOAD, 2 - READBACK. 1147 */ 1148 StatInfo HeapType[HEAP_TYPE_COUNT]; 1149 }; 1150 1151 /** \brief Statistics of current memory usage and available budget, in bytes, for GPU or CPU memory. 1152 */ 1153 struct Budget 1154 { 1155 /** \brief Sum size of all memory blocks allocated from particular heap type, in bytes. 1156 */ 1157 UINT64 BlockBytes; 1158 1159 /** \brief Sum size of all allocations created in particular heap type, in bytes. 1160 1161 Always less or equal than `BlockBytes`. 1162 Difference `BlockBytes - AllocationBytes` is the amount of memory allocated but unused - 1163 available for new allocations or wasted due to fragmentation. 1164 */ 1165 UINT64 AllocationBytes; 1166 1167 /** \brief Estimated current memory usage of the program, in bytes. 1168 1169 Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if enabled. 1170 1171 It might be different than `BlockBytes` (usually higher) due to additional implicit objects 1172 also occupying the memory, like swapchain, pipeline state objects, descriptor heaps, command lists, or 1173 memory blocks allocated outside of this library, if any. 1174 */ 1175 UINT64 UsageBytes; 1176 1177 /** \brief Estimated amount of memory available to the program, in bytes. 1178 1179 Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if enabled. 1180 1181 It might be different (most probably smaller) than memory sizes reported in `DXGI_ADAPTER_DESC` due to factors 1182 external to the program, like other programs also consuming system resources. 1183 Difference `BudgetBytes - UsageBytes` is the amount of additional memory that can probably 1184 be allocated without problems. Exceeding the budget may result in various problems. 1185 */ 1186 UINT64 BudgetBytes; 1187 }; 1188 1189 /** 1190 \brief Represents main object of this library initialized for particular `ID3D12Device`. 1191 1192 Fill structure D3D12MA::ALLOCATOR_DESC and call function CreateAllocator() to create it. 1193 Call method Allocator::Release to destroy it. 1194 1195 It is recommended to create just one object of this type per `ID3D12Device` object, 1196 right after Direct3D 12 is initialized and keep it alive until before Direct3D device is destroyed. 1197 */ 1198 class Allocator 1199 { 1200 public: 1201 /** \brief Deletes this object. 1202 1203 This function must be used instead of destructor, which is private. 1204 There is no reference counting involved. 1205 */ 1206 void Release(); 1207 1208 /// Returns cached options retrieved from D3D12 device. 1209 const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const; 1210 1211 /** \brief Allocates memory and creates a D3D12 resource (buffer or texture). This is the main allocation function. 1212 1213 The function is similar to `ID3D12Device::CreateCommittedResource`, but it may 1214 really call `ID3D12Device::CreatePlacedResource` to assign part of a larger, 1215 existing memory heap to the new resource, which is the main purpose of this 1216 whole library. 1217 1218 If `ppvResource` is null, you receive only `ppAllocation` object from this function. 1219 It holds pointer to `ID3D12Resource` that can be queried using function D3D12::Allocation::GetResource(). 1220 Reference count of the resource object is 1. 1221 It is automatically destroyed when you destroy the allocation object. 1222 1223 If 'ppvResource` is not null, you receive pointer to the resource next to allocation object. 1224 Reference count of the resource object is then increased by calling `QueryInterface`, so you need to manually `Release` it 1225 along with the allocation. 1226 1227 \param pAllocDesc Parameters of the allocation. 1228 \param pResourceDesc Description of created resource. 1229 \param InitialResourceState Initial resource state. 1230 \param pOptimizedClearValue Optional. Either null or optimized clear value. 1231 \param[out] ppAllocation Filled with pointer to new allocation object created. 1232 \param riidResource IID of a resource to be returned via `ppvResource`. 1233 \param[out] ppvResource Optional. If not null, filled with pointer to new resouce created. 1234 */ 1235 HRESULT CreateResource( 1236 const ALLOCATION_DESC* pAllocDesc, 1237 const D3D12_RESOURCE_DESC* pResourceDesc, 1238 D3D12_RESOURCE_STATES InitialResourceState, 1239 const D3D12_CLEAR_VALUE *pOptimizedClearValue, 1240 Allocation** ppAllocation, 1241 REFIID riidResource, 1242 void** ppvResource); 1243 1244 /** \brief Allocates memory without creating any resource placed in it. 1245 1246 This function is similar to `ID3D12Device::CreateHeap`, but it may really assign 1247 part of a larger, existing heap to the allocation. 1248 1249 `pAllocDesc->heapFlags` should contain one of these values, depending on type of resources you are going to create in this memory: 1250 `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, 1251 `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, 1252 `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. 1253 Except if you validate that ResourceHeapTier = 2 - then `heapFlags` 1254 may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0. 1255 Additional flags in `heapFlags` are allowed as well. 1256 1257 `pAllocInfo->SizeInBytes` must be multiply of 64KB. 1258 `pAllocInfo->Alignment` must be one of the legal values as described in documentation of `D3D12_HEAP_DESC`. 1259 1260 If you use D3D12MA::ALLOCATION_FLAG_COMMITTED you will get a separate memory block - 1261 a heap that always has offset 0. 1262 */ 1263 HRESULT AllocateMemory( 1264 const ALLOCATION_DESC* pAllocDesc, 1265 const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, 1266 Allocation** ppAllocation); 1267 1268 /** \brief Creates a new resource in place of an existing allocation. This is useful for memory aliasing. 1269 1270 \param pAllocation Existing allocation indicating the memory where the new resource should be created. 1271 It can be created using D3D12MA::Allocator::CreateResource and already have a resource bound to it, 1272 or can be a raw memory allocated with D3D12MA::Allocator::AllocateMemory. 1273 It must not be created as committed so that `ID3D12Heap` is available and not implicit. 1274 \param AllocationLocalOffset Additional offset in bytes to be applied when allocating the resource. 1275 Local from the start of `pAllocation`, not the beginning of the whole `ID3D12Heap`! 1276 If the new resource should start from the beginning of the `pAllocation` it should be 0. 1277 \param pResourceDesc Description of the new resource to be created. 1278 \param InitialResourceState 1279 \param pOptimizedClearValue 1280 \param riidResource 1281 \param[out] ppvResource Returns pointer to the new resource. 1282 The resource is not bound with `pAllocation`. 1283 This pointer must not be null - you must get the resource pointer and `Release` it when no longer needed. 1284 1285 Memory requirements of the new resource are checked for validation. 1286 If its size exceeds the end of `pAllocation` or required alignment is not fulfilled 1287 considering `pAllocation->GetOffset() + AllocationLocalOffset`, the function 1288 returns `E_INVALIDARG`. 1289 */ 1290 HRESULT CreateAliasingResource( 1291 Allocation* pAllocation, 1292 UINT64 AllocationLocalOffset, 1293 const D3D12_RESOURCE_DESC* pResourceDesc, 1294 D3D12_RESOURCE_STATES InitialResourceState, 1295 const D3D12_CLEAR_VALUE *pOptimizedClearValue, 1296 REFIID riidResource, 1297 void** ppvResource); 1298 1299 /** \brief Creates custom pool. 1300 */ 1301 HRESULT CreatePool( 1302 const POOL_DESC* pPoolDesc, 1303 Pool** ppPool); 1304 1305 /** \brief Sets the minimum number of bytes that should always be allocated (reserved) in a specific default pool. 1306 1307 \param heapType Must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. 1308 \param heapFlags Must be one of: `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`, 1309 `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`. If ResourceHeapTier = 2, it can also be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES`. 1310 \param minBytes Minimum number of bytes to keep allocated. 1311 1312 See also: \subpage reserving_memory. 1313 */ 1314 HRESULT SetDefaultHeapMinBytes( 1315 D3D12_HEAP_TYPE heapType, 1316 D3D12_HEAP_FLAGS heapFlags, 1317 UINT64 minBytes); 1318 1319 /** \brief Sets the index of the current frame. 1320 1321 This function is used to set the frame index in the allocator when a new game frame begins. 1322 */ 1323 void SetCurrentFrameIndex(UINT frameIndex); 1324 1325 /** \brief Retrieves statistics from the current state of the allocator. 1326 */ 1327 void CalculateStats(Stats* pStats); 1328 1329 /** \brief Retrieves information about current memory budget. 1330 1331 \param[out] pGpuBudget Optional, can be null. 1332 \param[out] pCpuBudget Optional, can be null. 1333 1334 This function is called "get" not "calculate" because it is very fast, suitable to be called 1335 every frame or every allocation. For more detailed statistics use CalculateStats(). 1336 1337 Note that when using allocator from multiple threads, returned information may immediately 1338 become outdated. 1339 */ 1340 void GetBudget(Budget* pGpuBudget, Budget* pCpuBudget); 1341 1342 /// Builds and returns statistics as a string in JSON format. 1343 /** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString. 1344 @param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics. 1345 */ 1346 void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const; 1347 1348 /// Frees memory of a string returned from Allocator::BuildStatsString. 1349 void FreeStatsString(WCHAR* pStatsString) const; 1350 1351 private: 1352 friend HRESULT CreateAllocator(const ALLOCATOR_DESC*, Allocator**); 1353 template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); 1354 friend class Pool; 1355 1356 Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc); 1357 ~Allocator(); 1358 1359 AllocatorPimpl* m_Pimpl; 1360 1361 D3D12MA_CLASS_NO_COPY(Allocator) 1362 }; 1363 1364 /// Parameters of created D3D12MA::VirtualBlock object to be passed to CreateVirtualBlock(). 1365 struct VIRTUAL_BLOCK_DESC 1366 { 1367 /** \brief Total size of the block. 1368 1369 Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. 1370 For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. 1371 */ 1372 UINT64 Size; 1373 /** \brief Custom CPU memory allocation callbacks. Optional. 1374 1375 Optional, can be null. When specified, will be used for all CPU-side memory allocations. 1376 */ 1377 const ALLOCATION_CALLBACKS* pAllocationCallbacks; 1378 }; 1379 1380 /// Parameters of created virtual allocation to be passed to VirtualBlock::Allocate(). 1381 struct VIRTUAL_ALLOCATION_DESC 1382 { 1383 /** \brief Size of the allocation. 1384 1385 Cannot be zero. 1386 */ 1387 UINT64 Size; 1388 /** \brief Required alignment of the allocation. 1389 1390 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. 1391 */ 1392 UINT64 Alignment; 1393 /** \brief Custom pointer to be associated with the allocation. 1394 1395 It can be fetched or changed later. 1396 */ 1397 void* pUserData; 1398 }; 1399 1400 /// Parameters of an existing virtual allocation, returned by VirtualBlock::GetAllocationInfo(). 1401 struct VIRTUAL_ALLOCATION_INFO 1402 { 1403 /** \brief Size of the allocation. 1404 1405 Same value as passed in VIRTUAL_ALLOCATION_DESC::Size. 1406 */ 1407 UINT64 size; 1408 /** \brief Custom pointer associated with the allocation. 1409 1410 Same value as passed in VIRTUAL_ALLOCATION_DESC::pUserData or VirtualBlock::SetAllocationUserData(). 1411 */ 1412 void* pUserData; 1413 }; 1414 1415 /** \brief Represents pure allocation algorithm and a data structure with allocations in some memory block, without actually allocating any GPU memory. 1416 1417 This class allows to use the core algorithm of the library custom allocations e.g. CPU memory or 1418 sub-allocation regions inside a single GPU buffer. 1419 1420 To create this object, fill in D3D12MA::VIRTUAL_BLOCK_DESC and call CreateVirtualBlock(). 1421 To destroy it, call its method VirtualBlock::Release(). 1422 */ 1423 class VirtualBlock 1424 { 1425 public: 1426 /** \brief Destroys this object and frees it from memory. 1427 1428 You need to free all the allocations within this block or call Clear() before destroying it. 1429 */ 1430 void Release(); 1431 1432 /** \brief Returns true if the block is empty - contains 0 allocations. 1433 */ 1434 BOOL IsEmpty() const; 1435 /** \brief Returns information about an allocation at given offset - its size and custom pointer. 1436 */ 1437 void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO* pInfo) const; 1438 1439 /** \brief Creates new allocation. 1440 \param pDesc 1441 \param[out] pOffset Offset of the new allocation, which can also be treated as an unique identifier of the allocation within this block. `UINT64_MAX` if allocation failed. 1442 \return `S_OK` if allocation succeeded, `E_OUTOFMEMORY` if it failed. 1443 */ 1444 HRESULT Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, UINT64* pOffset); 1445 /** \brief Frees the allocation at given offset. 1446 */ 1447 void FreeAllocation(UINT64 offset); 1448 /** \brief Frees all the allocations. 1449 */ 1450 void Clear(); 1451 /** \brief Changes custom pointer for an allocation at given offset to a new value. 1452 */ 1453 void SetAllocationUserData(UINT64 offset, void* pUserData); 1454 1455 /** \brief Retrieves statistics from the current state of the block. 1456 */ 1457 void CalculateStats(StatInfo* pInfo) const; 1458 1459 /** \brief Builds and returns statistics as a string in JSON format, including the list of allocations with their parameters. 1460 @param[out] ppStatsString Must be freed using VirtualBlock::FreeStatsString. 1461 */ 1462 void BuildStatsString(WCHAR** ppStatsString) const; 1463 1464 /** \brief Frees memory of a string returned from VirtualBlock::BuildStatsString. 1465 */ 1466 void FreeStatsString(WCHAR* pStatsString) const; 1467 1468 private: 1469 friend HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC*, VirtualBlock**); 1470 template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*); 1471 1472 VirtualBlockPimpl* m_Pimpl; 1473 1474 VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc); 1475 ~VirtualBlock(); 1476 1477 D3D12MA_CLASS_NO_COPY(VirtualBlock) 1478 }; 1479 1480 /** \brief Creates new main D3D12MA::Allocator object and returns it through `ppAllocator`. 1481 1482 You normally only need to call it once and keep a single Allocator object for your `ID3D12Device`. 1483 */ 1484 HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator); 1485 1486 /** \brief Creates new D3D12MA::VirtualBlock object and returns it through `ppVirtualBlock`. 1487 1488 Note you don't need to create D3D12MA::Allocator to use virtual blocks. 1489 */ 1490 HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock); 1491 1492 } // namespace D3D12MA 1493 1494 /// \cond INTERNAL 1495 DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATION_FLAGS); 1496 DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATOR_FLAGS); 1497 /// \endcond 1498