duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

D3D12MemAlloc.cpp (347582B)


      1 //
      2 // Copyright (c) 2019-2022 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 #include "D3D12MemAlloc.h"
     24 
     25 #include <combaseapi.h>
     26 #include <mutex>
     27 #include <algorithm>
     28 #include <utility>
     29 #include <cstdlib>
     30 #include <cstdint>
     31 #include <malloc.h> // for _aligned_malloc, _aligned_free
     32 #ifndef _WIN32
     33     #include <shared_mutex>
     34 #endif
     35 
     36 ////////////////////////////////////////////////////////////////////////////////
     37 ////////////////////////////////////////////////////////////////////////////////
     38 //
     39 // Configuration Begin
     40 //
     41 ////////////////////////////////////////////////////////////////////////////////
     42 ////////////////////////////////////////////////////////////////////////////////
     43 #ifndef _D3D12MA_CONFIGURATION
     44 
     45 #ifdef _WIN32
     46     #if !defined(WINVER) || WINVER < 0x0600
     47         #error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.
     48     #endif
     49 #endif
     50 
     51 #ifndef D3D12MA_SORT
     52     #define D3D12MA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
     53 #endif
     54 
     55 #ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
     56     #include <dxgi.h>
     57     #if D3D12MA_DXGI_1_4
     58         #include <dxgi1_4.h>
     59     #endif
     60 #endif
     61 
     62 #ifndef D3D12MA_ASSERT
     63     #include <cassert>
     64     #define D3D12MA_ASSERT(cond) assert(cond)
     65 #endif
     66 
     67 // Assert that will be called very often, like inside data structures e.g. operator[].
     68 // Making it non-empty can make program slow.
     69 #ifndef D3D12MA_HEAVY_ASSERT
     70     #ifdef _DEBUG
     71         #define D3D12MA_HEAVY_ASSERT(expr)   //D3D12MA_ASSERT(expr)
     72     #else
     73         #define D3D12MA_HEAVY_ASSERT(expr)
     74     #endif
     75 #endif
     76 
     77 #ifndef D3D12MA_DEBUG_ALIGNMENT
     78     /*
     79     Minimum alignment of all allocations, in bytes.
     80     Set to more than 1 for debugging purposes only. Must be power of two.
     81     */
     82     #define D3D12MA_DEBUG_ALIGNMENT (1)
     83 #endif
     84 
     85 #ifndef D3D12MA_DEBUG_MARGIN
     86     // Minimum margin before and after every allocation, in bytes.
     87     // Set nonzero for debugging purposes only.
     88     #define D3D12MA_DEBUG_MARGIN (0)
     89 #endif
     90 
     91 #ifndef D3D12MA_DEBUG_GLOBAL_MUTEX
     92     /*
     93     Set this to 1 for debugging purposes only, to enable single mutex protecting all
     94     entry calls to the library. Can be useful for debugging multithreading issues.
     95     */
     96     #define D3D12MA_DEBUG_GLOBAL_MUTEX (0)
     97 #endif
     98 
     99 /*
    100 Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER,
    101 especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.
    102 */
    103 //#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1
    104 
    105 #ifndef D3D12MA_DEFAULT_BLOCK_SIZE
    106    /// Default size of a block allocated as single ID3D12Heap.
    107    #define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024)
    108 #endif
    109 
    110 #ifndef D3D12MA_DEBUG_LOG
    111    #define D3D12MA_DEBUG_LOG(format, ...)
    112    /*
    113    #define D3D12MA_DEBUG_LOG(format, ...) do { \
    114        wprintf(format, __VA_ARGS__); \
    115        wprintf(L"\n"); \
    116    } while(false)
    117    */
    118 #endif
    119 
    120 #endif // _D3D12MA_CONFIGURATION
    121 ////////////////////////////////////////////////////////////////////////////////
    122 ////////////////////////////////////////////////////////////////////////////////
    123 //
    124 // Configuration End
    125 //
    126 ////////////////////////////////////////////////////////////////////////////////
    127 ////////////////////////////////////////////////////////////////////////////////
    128 
    129 #define D3D12MA_IID_PPV_ARGS(ppType)   __uuidof(**(ppType)), reinterpret_cast<void**>(ppType)
    130 
    131 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
    132     #define D3D12MA_CREATE_NOT_ZEROED_AVAILABLE 1
    133 #endif
    134 
    135 namespace D3D12MA
    136 {
    137 static constexpr UINT HEAP_TYPE_COUNT = 4;
    138 static constexpr UINT STANDARD_HEAP_TYPE_COUNT = 3; // Only DEFAULT, UPLOAD, READBACK.
    139 static constexpr UINT DEFAULT_POOL_MAX_COUNT = 9;
    140 static const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;
    141 // Minimum size of a free suballocation to register it in the free suballocation collection.
    142 static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
    143 
    144 static const WCHAR* const HeapTypeNames[] =
    145 {
    146     L"DEFAULT",
    147     L"UPLOAD",
    148     L"READBACK",
    149     L"CUSTOM",
    150 };
    151 static const WCHAR* const StandardHeapTypeNames[] =
    152 {
    153     L"DEFAULT",
    154     L"UPLOAD",
    155     L"READBACK",
    156 };
    157 
    158 static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS =
    159     D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
    160 
    161 static const D3D12_RESIDENCY_PRIORITY D3D12_RESIDENCY_PRIORITY_NONE = D3D12_RESIDENCY_PRIORITY(0);
    162 
    163 #ifndef _D3D12MA_ENUM_DECLARATIONS
    164 
    165 // Local copy of this enum, as it is provided only by <dxgi1_4.h>, so it may not be available.
    166 enum DXGI_MEMORY_SEGMENT_GROUP_COPY
    167 {
    168     DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY = 0,
    169     DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY = 1,
    170     DXGI_MEMORY_SEGMENT_GROUP_COUNT
    171 };
    172 
    173 enum class ResourceClass
    174 {
    175     Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture
    176 };
    177 
    178 enum SuballocationType
    179 {
    180     SUBALLOCATION_TYPE_FREE = 0,
    181     SUBALLOCATION_TYPE_ALLOCATION = 1,
    182 };
    183 
    184 #endif // _D3D12MA_ENUM_DECLARATIONS
    185 
    186 
    187 #ifndef _D3D12MA_FUNCTIONS
    188 
    189 static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pPrivateData*/)
    190 {
    191 #ifdef _WIN32
    192     return _aligned_malloc(Size, Alignment);
    193 #else
    194     return aligned_alloc(Alignment, Size);
    195 #endif
    196 }
    197 static void DefaultFree(void* pMemory, void* /*pPrivateData*/)
    198 {
    199 #ifdef _WIN32
    200     return _aligned_free(pMemory);
    201 #else
    202     return free(pMemory);
    203 #endif
    204 }
    205 
    206 static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)
    207 {
    208     void* const result = (*allocs.pAllocate)(size, alignment, allocs.pPrivateData);
    209     D3D12MA_ASSERT(result);
    210     return result;
    211 }
    212 static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)
    213 {
    214     (*allocs.pFree)(memory, allocs.pPrivateData);
    215 }
    216 
    217 template<typename T>
    218 static T* Allocate(const ALLOCATION_CALLBACKS& allocs)
    219 {
    220     return (T*)Malloc(allocs, sizeof(T), __alignof(T));
    221 }
    222 template<typename T>
    223 static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)
    224 {
    225     return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));
    226 }
    227 
    228 #define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)
    229 #define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)
    230 
    231 template<typename T>
    232 void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)
    233 {
    234     if (memory)
    235     {
    236         memory->~T();
    237         Free(allocs, memory);
    238     }
    239 }
    240 template<typename T>
    241 void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)
    242 {
    243     if (memory)
    244     {
    245         for (size_t i = count; i--; )
    246         {
    247             memory[i].~T();
    248         }
    249         Free(allocs, memory);
    250     }
    251 }
    252 
    253 static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)
    254 {
    255     if (allocationCallbacks)
    256     {
    257         outAllocs = *allocationCallbacks;
    258         D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);
    259     }
    260     else
    261     {
    262         outAllocs.pAllocate = &DefaultAllocate;
    263         outAllocs.pFree = &DefaultFree;
    264         outAllocs.pPrivateData = NULL;
    265     }
    266 }
    267 
    268 #define SAFE_RELEASE(ptr)   do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)
    269 
    270 #define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \
    271     D3D12MA_ASSERT(0 && "Validation failed: " #cond); \
    272     return false; \
    273 } } while(false)
    274 
    275 template<typename T>
    276 static T D3D12MA_MIN(const T& a, const T& b) { return a <= b ? a : b; }
    277 template<typename T>
    278 static T D3D12MA_MAX(const T& a, const T& b) { return a <= b ? b : a; }
    279 
    280 template<typename T>
    281 static void D3D12MA_SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; }
    282 
    283 // Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX
    284 static UINT8 BitScanLSB(UINT64 mask)
    285 {
    286 #if defined(_MSC_VER) && defined(_WIN64)
    287     unsigned long pos;
    288     if (_BitScanForward64(&pos, mask))
    289         return static_cast<UINT8>(pos);
    290     return UINT8_MAX;
    291 #elif defined __GNUC__ || defined __clang__
    292     return static_cast<UINT8>(__builtin_ffsll(mask)) - 1U;
    293 #else
    294     UINT8 pos = 0;
    295     UINT64 bit = 1;
    296     do
    297     {
    298         if (mask & bit)
    299             return pos;
    300         bit <<= 1;
    301     } while (pos++ < 63);
    302     return UINT8_MAX;
    303 #endif
    304 }
    305 // Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX
    306 static UINT8 BitScanLSB(UINT32 mask)
    307 {
    308 #ifdef _MSC_VER
    309     unsigned long pos;
    310     if (_BitScanForward(&pos, mask))
    311         return static_cast<UINT8>(pos);
    312     return UINT8_MAX;
    313 #elif defined __GNUC__ || defined __clang__
    314     return static_cast<UINT8>(__builtin_ffs(mask)) - 1U;
    315 #else
    316     UINT8 pos = 0;
    317     UINT32 bit = 1;
    318     do
    319     {
    320         if (mask & bit)
    321             return pos;
    322         bit <<= 1;
    323     } while (pos++ < 31);
    324     return UINT8_MAX;
    325 #endif
    326 }
    327 
    328 // Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX
    329 static UINT8 BitScanMSB(UINT64 mask)
    330 {
    331 #if defined(_MSC_VER) && defined(_WIN64)
    332     unsigned long pos;
    333     if (_BitScanReverse64(&pos, mask))
    334         return static_cast<UINT8>(pos);
    335 #elif defined __GNUC__ || defined __clang__
    336     if (mask)
    337         return 63 - static_cast<UINT8>(__builtin_clzll(mask));
    338 #else
    339     UINT8 pos = 63;
    340     UINT64 bit = 1ULL << 63;
    341     do
    342     {
    343         if (mask & bit)
    344             return pos;
    345         bit >>= 1;
    346     } while (pos-- > 0);
    347 #endif
    348     return UINT8_MAX;
    349 }
    350 // Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX
    351 static UINT8 BitScanMSB(UINT32 mask)
    352 {
    353 #ifdef _MSC_VER
    354     unsigned long pos;
    355     if (_BitScanReverse(&pos, mask))
    356         return static_cast<UINT8>(pos);
    357 #elif defined __GNUC__ || defined __clang__
    358     if (mask)
    359         return 31 - static_cast<UINT8>(__builtin_clz(mask));
    360 #else
    361     UINT8 pos = 31;
    362     UINT32 bit = 1UL << 31;
    363     do
    364     {
    365         if (mask & bit)
    366             return pos;
    367         bit >>= 1;
    368     } while (pos-- > 0);
    369 #endif
    370     return UINT8_MAX;
    371 }
    372 
    373 /*
    374 Returns true if given number is a power of two.
    375 T must be unsigned integer number or signed integer but always nonnegative.
    376 For 0 returns true.
    377 */
    378 template <typename T>
    379 static bool IsPow2(T x) { return (x & (x - 1)) == 0; }
    380 
    381 // Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.
    382 // Use types like UINT, uint64_t as T.
    383 template <typename T>
    384 static T AlignUp(T val, T alignment)
    385 {
    386     D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
    387     return (val + alignment - 1) & ~(alignment - 1);
    388 }
    389 // Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.
    390 // Use types like UINT, uint64_t as T.
    391 template <typename T>
    392 static T AlignDown(T val, T alignment)
    393 {
    394     D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
    395     return val & ~(alignment - 1);
    396 }
    397 
    398 // Division with mathematical rounding to nearest number.
    399 template <typename T>
    400 static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }
    401 template <typename T>
    402 static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }
    403 
    404 static WCHAR HexDigitToChar(UINT8 digit)
    405 {
    406     if(digit < 10)
    407         return L'0' + digit;
    408     else
    409         return L'A' + (digit - 10);
    410 }
    411 
    412 /*
    413 Performs binary search and returns iterator to first element that is greater or
    414 equal to `key`, according to comparison `cmp`.
    415 
    416 Cmp should return true if first argument is less than second argument.
    417 
    418 Returned value is the found element, if present in the collection or place where
    419 new element with value (key) should be inserted.
    420 */
    421 template <typename CmpLess, typename IterT, typename KeyT>
    422 static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
    423 {
    424     size_t down = 0, up = (end - beg);
    425     while (down < up)
    426     {
    427         const size_t mid = (down + up) / 2;
    428         if (cmp(*(beg + mid), key))
    429         {
    430             down = mid + 1;
    431         }
    432         else
    433         {
    434             up = mid;
    435         }
    436     }
    437     return beg + down;
    438 }
    439 
    440 /*
    441 Performs binary search and returns iterator to an element that is equal to `key`,
    442 according to comparison `cmp`.
    443 
    444 Cmp should return true if first argument is less than second argument.
    445 
    446 Returned value is the found element, if present in the collection or end if not
    447 found.
    448 */
    449 template<typename CmpLess, typename IterT, typename KeyT>
    450 static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
    451 {
    452     IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);
    453     if (it == end ||
    454         (!cmp(*it, value) && !cmp(value, *it)))
    455     {
    456         return it;
    457     }
    458     return end;
    459 }
    460 
    461 static UINT StandardHeapTypeToIndex(D3D12_HEAP_TYPE type)
    462 {
    463     switch (type)
    464     {
    465     case D3D12_HEAP_TYPE_DEFAULT:  return 0;
    466     case D3D12_HEAP_TYPE_UPLOAD:   return 1;
    467     case D3D12_HEAP_TYPE_READBACK: return 2;
    468     default: D3D12MA_ASSERT(0); return UINT_MAX;
    469     }
    470 }
    471 
    472 static D3D12_HEAP_TYPE IndexToStandardHeapType(UINT heapTypeIndex)
    473 {
    474     switch(heapTypeIndex)
    475     {
    476     case 0: return D3D12_HEAP_TYPE_DEFAULT;
    477     case 1: return D3D12_HEAP_TYPE_UPLOAD;
    478     case 2: return D3D12_HEAP_TYPE_READBACK;
    479     default: D3D12MA_ASSERT(0); return D3D12_HEAP_TYPE_CUSTOM;
    480     }
    481 }
    482 
    483 static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures)
    484 {
    485     /*
    486     Documentation of D3D12_HEAP_DESC structure says:
    487 
    488     - D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT   defined as 64KB.
    489     - D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT   defined as 4MB. An
    490     application must decide whether the heap will contain multi-sample
    491     anti-aliasing (MSAA), in which case, the application must choose [this flag].
    492 
    493     https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc
    494     */
    495 
    496     if (denyMsaaTextures)
    497         return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    498 
    499     const D3D12_HEAP_FLAGS denyAllTexturesFlags =
    500         D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
    501     const bool canContainAnyTextures =
    502         (flags & denyAllTexturesFlags) != denyAllTexturesFlags;
    503     return canContainAnyTextures ?
    504         D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    505 }
    506 
    507 static ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags)
    508 {
    509     const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;
    510     const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;
    511     const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;
    512 
    513     const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);
    514     if (allowedGroupCount != 1)
    515         return ResourceClass::Unknown;
    516 
    517     if (allowRtDsTextures)
    518         return ResourceClass::RT_DS_Texture;
    519     if (allowNonRtDsTextures)
    520         return ResourceClass::Non_RT_DS_Texture;
    521     return ResourceClass::Buffer;
    522 }
    523 
    524 static bool IsHeapTypeStandard(D3D12_HEAP_TYPE type)
    525 {
    526     return type == D3D12_HEAP_TYPE_DEFAULT ||
    527         type == D3D12_HEAP_TYPE_UPLOAD ||
    528         type == D3D12_HEAP_TYPE_READBACK;
    529 }
    530 
    531 static D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type)
    532 {
    533     D3D12MA_ASSERT(IsHeapTypeStandard(type));
    534     D3D12_HEAP_PROPERTIES result = {};
    535     result.Type = type;
    536     return result;
    537 }
    538 
    539 static bool IsFormatCompressed(DXGI_FORMAT format)
    540 {
    541     switch (format)
    542     {
    543     case DXGI_FORMAT_BC1_TYPELESS:
    544     case DXGI_FORMAT_BC1_UNORM:
    545     case DXGI_FORMAT_BC1_UNORM_SRGB:
    546     case DXGI_FORMAT_BC2_TYPELESS:
    547     case DXGI_FORMAT_BC2_UNORM:
    548     case DXGI_FORMAT_BC2_UNORM_SRGB:
    549     case DXGI_FORMAT_BC3_TYPELESS:
    550     case DXGI_FORMAT_BC3_UNORM:
    551     case DXGI_FORMAT_BC3_UNORM_SRGB:
    552     case DXGI_FORMAT_BC4_TYPELESS:
    553     case DXGI_FORMAT_BC4_UNORM:
    554     case DXGI_FORMAT_BC4_SNORM:
    555     case DXGI_FORMAT_BC5_TYPELESS:
    556     case DXGI_FORMAT_BC5_UNORM:
    557     case DXGI_FORMAT_BC5_SNORM:
    558     case DXGI_FORMAT_BC6H_TYPELESS:
    559     case DXGI_FORMAT_BC6H_UF16:
    560     case DXGI_FORMAT_BC6H_SF16:
    561     case DXGI_FORMAT_BC7_TYPELESS:
    562     case DXGI_FORMAT_BC7_UNORM:
    563     case DXGI_FORMAT_BC7_UNORM_SRGB:
    564         return true;
    565     default:
    566         return false;
    567     }
    568 }
    569 
    570 // Only some formats are supported. For others it returns 0.
    571 static UINT GetBitsPerPixel(DXGI_FORMAT format)
    572 {
    573     switch (format)
    574     {
    575     case DXGI_FORMAT_R32G32B32A32_TYPELESS:
    576     case DXGI_FORMAT_R32G32B32A32_FLOAT:
    577     case DXGI_FORMAT_R32G32B32A32_UINT:
    578     case DXGI_FORMAT_R32G32B32A32_SINT:
    579         return 128;
    580     case DXGI_FORMAT_R32G32B32_TYPELESS:
    581     case DXGI_FORMAT_R32G32B32_FLOAT:
    582     case DXGI_FORMAT_R32G32B32_UINT:
    583     case DXGI_FORMAT_R32G32B32_SINT:
    584         return 96;
    585     case DXGI_FORMAT_R16G16B16A16_TYPELESS:
    586     case DXGI_FORMAT_R16G16B16A16_FLOAT:
    587     case DXGI_FORMAT_R16G16B16A16_UNORM:
    588     case DXGI_FORMAT_R16G16B16A16_UINT:
    589     case DXGI_FORMAT_R16G16B16A16_SNORM:
    590     case DXGI_FORMAT_R16G16B16A16_SINT:
    591         return 64;
    592     case DXGI_FORMAT_R32G32_TYPELESS:
    593     case DXGI_FORMAT_R32G32_FLOAT:
    594     case DXGI_FORMAT_R32G32_UINT:
    595     case DXGI_FORMAT_R32G32_SINT:
    596         return 64;
    597     case DXGI_FORMAT_R32G8X24_TYPELESS:
    598     case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
    599     case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
    600     case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
    601         return 64;
    602     case DXGI_FORMAT_R10G10B10A2_TYPELESS:
    603     case DXGI_FORMAT_R10G10B10A2_UNORM:
    604     case DXGI_FORMAT_R10G10B10A2_UINT:
    605     case DXGI_FORMAT_R11G11B10_FLOAT:
    606         return 32;
    607     case DXGI_FORMAT_R8G8B8A8_TYPELESS:
    608     case DXGI_FORMAT_R8G8B8A8_UNORM:
    609     case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
    610     case DXGI_FORMAT_R8G8B8A8_UINT:
    611     case DXGI_FORMAT_R8G8B8A8_SNORM:
    612     case DXGI_FORMAT_R8G8B8A8_SINT:
    613         return 32;
    614     case DXGI_FORMAT_R16G16_TYPELESS:
    615     case DXGI_FORMAT_R16G16_FLOAT:
    616     case DXGI_FORMAT_R16G16_UNORM:
    617     case DXGI_FORMAT_R16G16_UINT:
    618     case DXGI_FORMAT_R16G16_SNORM:
    619     case DXGI_FORMAT_R16G16_SINT:
    620         return 32;
    621     case DXGI_FORMAT_R32_TYPELESS:
    622     case DXGI_FORMAT_D32_FLOAT:
    623     case DXGI_FORMAT_R32_FLOAT:
    624     case DXGI_FORMAT_R32_UINT:
    625     case DXGI_FORMAT_R32_SINT:
    626         return 32;
    627     case DXGI_FORMAT_R24G8_TYPELESS:
    628     case DXGI_FORMAT_D24_UNORM_S8_UINT:
    629     case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
    630     case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
    631         return 32;
    632     case DXGI_FORMAT_R8G8_TYPELESS:
    633     case DXGI_FORMAT_R8G8_UNORM:
    634     case DXGI_FORMAT_R8G8_UINT:
    635     case DXGI_FORMAT_R8G8_SNORM:
    636     case DXGI_FORMAT_R8G8_SINT:
    637         return 16;
    638     case DXGI_FORMAT_R16_TYPELESS:
    639     case DXGI_FORMAT_R16_FLOAT:
    640     case DXGI_FORMAT_D16_UNORM:
    641     case DXGI_FORMAT_R16_UNORM:
    642     case DXGI_FORMAT_R16_UINT:
    643     case DXGI_FORMAT_R16_SNORM:
    644     case DXGI_FORMAT_R16_SINT:
    645         return 16;
    646     case DXGI_FORMAT_R8_TYPELESS:
    647     case DXGI_FORMAT_R8_UNORM:
    648     case DXGI_FORMAT_R8_UINT:
    649     case DXGI_FORMAT_R8_SNORM:
    650     case DXGI_FORMAT_R8_SINT:
    651     case DXGI_FORMAT_A8_UNORM:
    652         return 8;
    653     case DXGI_FORMAT_BC1_TYPELESS:
    654     case DXGI_FORMAT_BC1_UNORM:
    655     case DXGI_FORMAT_BC1_UNORM_SRGB:
    656         return 4;
    657     case DXGI_FORMAT_BC2_TYPELESS:
    658     case DXGI_FORMAT_BC2_UNORM:
    659     case DXGI_FORMAT_BC2_UNORM_SRGB:
    660         return 8;
    661     case DXGI_FORMAT_BC3_TYPELESS:
    662     case DXGI_FORMAT_BC3_UNORM:
    663     case DXGI_FORMAT_BC3_UNORM_SRGB:
    664         return 8;
    665     case DXGI_FORMAT_BC4_TYPELESS:
    666     case DXGI_FORMAT_BC4_UNORM:
    667     case DXGI_FORMAT_BC4_SNORM:
    668         return 4;
    669     case DXGI_FORMAT_BC5_TYPELESS:
    670     case DXGI_FORMAT_BC5_UNORM:
    671     case DXGI_FORMAT_BC5_SNORM:
    672         return 8;
    673     case DXGI_FORMAT_BC6H_TYPELESS:
    674     case DXGI_FORMAT_BC6H_UF16:
    675     case DXGI_FORMAT_BC6H_SF16:
    676         return 8;
    677     case DXGI_FORMAT_BC7_TYPELESS:
    678     case DXGI_FORMAT_BC7_UNORM:
    679     case DXGI_FORMAT_BC7_UNORM_SRGB:
    680         return 8;
    681     default:
    682         return 0;
    683     }
    684 }
    685     
    686 template<typename D3D12_RESOURCE_DESC_T>
    687 static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc)
    688 {
    689     if (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
    690         return ResourceClass::Buffer;
    691     // Else: it's surely a texture.
    692     const bool isRenderTargetOrDepthStencil =
    693         (resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
    694     return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;
    695 }
    696     
    697 // This algorithm is overly conservative.
    698 template<typename D3D12_RESOURCE_DESC_T>
    699 static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)
    700 {
    701     if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
    702         return false;
    703     if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)
    704         return false;
    705     if (resourceDesc.SampleDesc.Count > 1)
    706         return false;
    707     if (resourceDesc.DepthOrArraySize != 1)
    708         return false;
    709 
    710     UINT sizeX = (UINT)resourceDesc.Width;
    711     UINT sizeY = resourceDesc.Height;
    712     UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);
    713     if (bitsPerPixel == 0)
    714         return false;
    715 
    716     if (IsFormatCompressed(resourceDesc.Format))
    717     {
    718         sizeX = DivideRoundingUp(sizeX, 4u);
    719         sizeY = DivideRoundingUp(sizeY, 4u);
    720         bitsPerPixel *= 16;
    721     }
    722 
    723     UINT tileSizeX = 0, tileSizeY = 0;
    724     switch (bitsPerPixel)
    725     {
    726     case   8: tileSizeX = 64; tileSizeY = 64; break;
    727     case  16: tileSizeX = 64; tileSizeY = 32; break;
    728     case  32: tileSizeX = 32; tileSizeY = 32; break;
    729     case  64: tileSizeX = 32; tileSizeY = 16; break;
    730     case 128: tileSizeX = 16; tileSizeY = 16; break;
    731     default: return false;
    732     }
    733 
    734     const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY);
    735     return tileCount <= 16;
    736 }
    737     
    738 static bool ValidateAllocateMemoryParameters(
    739     const ALLOCATION_DESC* pAllocDesc,
    740     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
    741     Allocation** ppAllocation)
    742 {
    743     return pAllocDesc &&
    744         pAllocInfo &&
    745         ppAllocation &&
    746         (pAllocInfo->Alignment == 0 ||
    747             pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||
    748             pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) &&
    749         pAllocInfo->SizeInBytes != 0 &&
    750         pAllocInfo->SizeInBytes % (64ull * 1024) == 0;
    751 }
    752 
    753 #endif // _D3D12MA_FUNCTIONS
    754     
    755 #ifndef _D3D12MA_STATISTICS_FUNCTIONS
    756 
    757 static void ClearStatistics(Statistics& outStats)
    758 {
    759     outStats.BlockCount = 0;
    760     outStats.AllocationCount = 0;
    761     outStats.BlockBytes = 0;
    762     outStats.AllocationBytes = 0;
    763 }
    764 
    765 static void ClearDetailedStatistics(DetailedStatistics& outStats)
    766 {
    767     ClearStatistics(outStats.Stats);
    768     outStats.UnusedRangeCount = 0;
    769     outStats.AllocationSizeMin = UINT64_MAX;
    770     outStats.AllocationSizeMax = 0;
    771     outStats.UnusedRangeSizeMin = UINT64_MAX;
    772     outStats.UnusedRangeSizeMax = 0;
    773 }
    774 
    775 static void AddStatistics(Statistics& inoutStats, const Statistics& src)
    776 {
    777     inoutStats.BlockCount += src.BlockCount;
    778     inoutStats.AllocationCount += src.AllocationCount;
    779     inoutStats.BlockBytes += src.BlockBytes;
    780     inoutStats.AllocationBytes += src.AllocationBytes;
    781 }
    782 
    783 static void AddDetailedStatistics(DetailedStatistics& inoutStats, const DetailedStatistics& src)
    784 {
    785     AddStatistics(inoutStats.Stats, src.Stats);
    786     inoutStats.UnusedRangeCount += src.UnusedRangeCount;
    787     inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, src.AllocationSizeMin);
    788     inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, src.AllocationSizeMax);
    789     inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, src.UnusedRangeSizeMin);
    790     inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, src.UnusedRangeSizeMax);
    791 }
    792 
    793 static void AddDetailedStatisticsAllocation(DetailedStatistics& inoutStats, UINT64 size)
    794 {
    795     inoutStats.Stats.AllocationCount++;
    796     inoutStats.Stats.AllocationBytes += size;
    797     inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, size);
    798     inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, size);
    799 }
    800 
    801 static void AddDetailedStatisticsUnusedRange(DetailedStatistics& inoutStats, UINT64 size)
    802 {
    803     inoutStats.UnusedRangeCount++;
    804     inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, size);
    805     inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, size);
    806 }
    807     
    808 #endif // _D3D12MA_STATISTICS_FUNCTIONS
    809 
    810 
    811 #ifndef _D3D12MA_MUTEX
    812 
    813 #ifndef D3D12MA_MUTEX
    814     class Mutex
    815     {
    816     public:
    817         void Lock() { m_Mutex.lock(); }
    818         void Unlock() { m_Mutex.unlock(); }
    819     
    820     private:
    821         std::mutex m_Mutex;
    822     };
    823     #define D3D12MA_MUTEX Mutex
    824 #endif
    825 
    826 #ifndef D3D12MA_RW_MUTEX
    827 #ifdef _WIN32
    828     class RWMutex
    829     {
    830     public:
    831         RWMutex() { InitializeSRWLock(&m_Lock); }
    832         void LockRead() { AcquireSRWLockShared(&m_Lock); }
    833         void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
    834         void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
    835         void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
    836     
    837     private:
    838         SRWLOCK m_Lock;
    839     };
    840 #else // #ifdef _WIN32
    841     class RWMutex
    842     {
    843     public:
    844         RWMutex() {}
    845         void LockRead() { m_Mutex.lock_shared(); }
    846         void UnlockRead() { m_Mutex.unlock_shared(); }
    847         void LockWrite() { m_Mutex.lock(); }
    848         void UnlockWrite() { m_Mutex.unlock(); }
    849     
    850     private:
    851         std::shared_timed_mutex m_Mutex;
    852     };
    853 #endif // #ifdef _WIN32
    854     #define D3D12MA_RW_MUTEX RWMutex
    855 #endif // #ifndef D3D12MA_RW_MUTEX
    856 
    857 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
    858 struct MutexLock
    859 {
    860     D3D12MA_CLASS_NO_COPY(MutexLock);
    861 public:
    862     MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :
    863         m_pMutex(useMutex ? &mutex : NULL)
    864     {
    865         if (m_pMutex) m_pMutex->Lock(); 
    866     }
    867     ~MutexLock() { if (m_pMutex) m_pMutex->Unlock(); }
    868 
    869 private:
    870     D3D12MA_MUTEX* m_pMutex;
    871 };
    872 
    873 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
    874 struct MutexLockRead
    875 {
    876     D3D12MA_CLASS_NO_COPY(MutexLockRead);
    877 public:
    878     MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex)
    879         :  m_pMutex(useMutex ? &mutex : NULL)
    880     {
    881         if(m_pMutex)
    882         {
    883             m_pMutex->LockRead();
    884         }
    885     }
    886     ~MutexLockRead() { if (m_pMutex) m_pMutex->UnlockRead(); }
    887 
    888 private:
    889     D3D12MA_RW_MUTEX* m_pMutex;
    890 };
    891 
    892 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
    893 struct MutexLockWrite
    894 {
    895     D3D12MA_CLASS_NO_COPY(MutexLockWrite);
    896 public:
    897     MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex)
    898         : m_pMutex(useMutex ? &mutex : NULL)
    899     {
    900         if (m_pMutex) m_pMutex->LockWrite(); 
    901     }
    902     ~MutexLockWrite() { if (m_pMutex) m_pMutex->UnlockWrite(); }
    903 
    904 private:
    905     D3D12MA_RW_MUTEX* m_pMutex;
    906 };
    907 
    908 #if D3D12MA_DEBUG_GLOBAL_MUTEX
    909     static D3D12MA_MUTEX g_DebugGlobalMutex;
    910     #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);
    911 #else
    912     #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
    913 #endif
    914 #endif // _D3D12MA_MUTEX
    915 
    916 #ifndef _D3D12MA_VECTOR
    917 /*
    918 Dynamically resizing continuous array. Class with interface similar to std::vector.
    919 T must be POD because constructors and destructors are not called and memcpy is
    920 used for these objects.
    921 */
    922 template<typename T>
    923 class Vector
    924 {
    925 public:
    926     using value_type = T;
    927     using iterator = T*;
    928     using const_iterator = const T*;
    929 
    930     // allocationCallbacks externally owned, must outlive this object.
    931     Vector(const ALLOCATION_CALLBACKS& allocationCallbacks);
    932     Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks);
    933     Vector(const Vector<T>& src);
    934     ~Vector();
    935 
    936     const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }
    937     bool empty() const { return m_Count == 0; }
    938     size_t size() const { return m_Count; }
    939     T* data() { return m_pArray; }
    940     const T* data() const { return m_pArray; }
    941     void clear(bool freeMemory = false) { resize(0, freeMemory); }
    942 
    943     iterator begin() { return m_pArray; }
    944     iterator end() { return m_pArray + m_Count; }
    945     const_iterator cbegin() const { return m_pArray; }
    946     const_iterator cend() const { return m_pArray + m_Count; }
    947     const_iterator begin() const { return cbegin(); }
    948     const_iterator end() const { return cend(); }
    949 
    950     void push_front(const T& src) { insert(0, src); }
    951     void push_back(const T& src);
    952     void pop_front();
    953     void pop_back();
    954 
    955     T& front();
    956     T& back();
    957     const T& front() const;
    958     const T& back() const;
    959 
    960     void reserve(size_t newCapacity, bool freeMemory = false);
    961     void resize(size_t newCount, bool freeMemory = false);
    962     void insert(size_t index, const T& src);
    963     void remove(size_t index);
    964 
    965     template<typename CmpLess>
    966     size_t InsertSorted(const T& value, const CmpLess& cmp);
    967     template<typename CmpLess>
    968     bool RemoveSorted(const T& value, const CmpLess& cmp);
    969 
    970     Vector& operator=(const Vector<T>& rhs);
    971     T& operator[](size_t index);
    972     const T& operator[](size_t index) const;
    973 
    974 private:
    975     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
    976     T* m_pArray;
    977     size_t m_Count;
    978     size_t m_Capacity;
    979 };
    980 
    981 #ifndef _D3D12MA_VECTOR_FUNCTIONS
    982 template<typename T>
    983 Vector<T>::Vector(const ALLOCATION_CALLBACKS& allocationCallbacks)
    984     : m_AllocationCallbacks(allocationCallbacks),
    985     m_pArray(NULL),
    986     m_Count(0),
    987     m_Capacity(0) {}
    988 
    989 template<typename T>
    990 Vector<T>::Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks)
    991     : m_AllocationCallbacks(allocationCallbacks),
    992     m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),
    993     m_Count(count),
    994     m_Capacity(count) {}
    995 
    996 template<typename T>
    997 Vector<T>::Vector(const Vector<T>& src)
    998     : m_AllocationCallbacks(src.m_AllocationCallbacks),
    999     m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),
   1000     m_Count(src.m_Count),
   1001     m_Capacity(src.m_Count)
   1002 {
   1003     if (m_Count > 0)
   1004     {
   1005         memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
   1006     }
   1007 }
   1008 
   1009 template<typename T>
   1010 Vector<T>::~Vector()
   1011 {
   1012     Free(m_AllocationCallbacks, m_pArray);
   1013 }
   1014 
   1015 template<typename T>
   1016 void Vector<T>::push_back(const T& src)
   1017 {
   1018     const size_t newIndex = size();
   1019     resize(newIndex + 1);
   1020     m_pArray[newIndex] = src;
   1021 }
   1022 
   1023 template<typename T>
   1024 void Vector<T>::pop_front()
   1025 {
   1026     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1027     remove(0);
   1028 }
   1029 
   1030 template<typename T>
   1031 void Vector<T>::pop_back()
   1032 {
   1033     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1034     resize(size() - 1);
   1035 }
   1036 
   1037 template<typename T>
   1038 T& Vector<T>::front()
   1039 {
   1040     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1041     return m_pArray[0];
   1042 }
   1043 
   1044 template<typename T>
   1045 T& Vector<T>::back()
   1046 {
   1047     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1048     return m_pArray[m_Count - 1];
   1049 }
   1050 
   1051 template<typename T>
   1052 const T& Vector<T>::front() const
   1053 {
   1054     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1055     return m_pArray[0];
   1056 }
   1057 
   1058 template<typename T>
   1059 const T& Vector<T>::back() const
   1060 {
   1061     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   1062     return m_pArray[m_Count - 1];
   1063 }
   1064 
   1065 template<typename T>
   1066 void Vector<T>::reserve(size_t newCapacity, bool freeMemory)
   1067 {
   1068     newCapacity = D3D12MA_MAX(newCapacity, m_Count);
   1069 
   1070     if ((newCapacity < m_Capacity) && !freeMemory)
   1071     {
   1072         newCapacity = m_Capacity;
   1073     }
   1074 
   1075     if (newCapacity != m_Capacity)
   1076     {
   1077         T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
   1078         if (m_Count != 0)
   1079         {
   1080             memcpy(newArray, m_pArray, m_Count * sizeof(T));
   1081         }
   1082         Free(m_AllocationCallbacks, m_pArray);
   1083         m_Capacity = newCapacity;
   1084         m_pArray = newArray;
   1085     }
   1086 }
   1087 
   1088 template<typename T>
   1089 void Vector<T>::resize(size_t newCount, bool freeMemory)
   1090 {
   1091     size_t newCapacity = m_Capacity;
   1092     if (newCount > m_Capacity)
   1093     {
   1094         newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));
   1095     }
   1096     else if (freeMemory)
   1097     {
   1098         newCapacity = newCount;
   1099     }
   1100 
   1101     if (newCapacity != m_Capacity)
   1102     {
   1103         T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
   1104         const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);
   1105         if (elementsToCopy != 0)
   1106         {
   1107             memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
   1108         }
   1109         Free(m_AllocationCallbacks, m_pArray);
   1110         m_Capacity = newCapacity;
   1111         m_pArray = newArray;
   1112     }
   1113 
   1114     m_Count = newCount;
   1115 }
   1116 
   1117 template<typename T>
   1118 void Vector<T>::insert(size_t index, const T& src)
   1119 {
   1120     D3D12MA_HEAVY_ASSERT(index <= m_Count);
   1121     const size_t oldCount = size();
   1122     resize(oldCount + 1);
   1123     if (index < oldCount)
   1124     {
   1125         memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
   1126     }
   1127     m_pArray[index] = src;
   1128 }
   1129 
   1130 template<typename T>
   1131 void Vector<T>::remove(size_t index)
   1132 {
   1133     D3D12MA_HEAVY_ASSERT(index < m_Count);
   1134     const size_t oldCount = size();
   1135     if (index < oldCount - 1)
   1136     {
   1137         memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
   1138     }
   1139     resize(oldCount - 1);
   1140 }
   1141 
   1142 template<typename T> template<typename CmpLess>
   1143 size_t Vector<T>::InsertSorted(const T& value, const CmpLess& cmp)
   1144 {
   1145     const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(
   1146         m_pArray,
   1147         m_pArray + m_Count,
   1148         value,
   1149         cmp) - m_pArray;
   1150     insert(indexToInsert, value);
   1151     return indexToInsert;
   1152 }
   1153 
   1154 template<typename T> template<typename CmpLess>
   1155 bool Vector<T>::RemoveSorted(const T& value, const CmpLess& cmp)
   1156 {
   1157     const iterator it = BinaryFindFirstNotLess(
   1158         m_pArray,
   1159         m_pArray + m_Count,
   1160         value,
   1161         cmp);
   1162     if ((it != end()) && !cmp(*it, value) && !cmp(value, *it))
   1163     {
   1164         size_t indexToRemove = it - begin();
   1165         remove(indexToRemove);
   1166         return true;
   1167     }
   1168     return false;
   1169 }
   1170 
   1171 template<typename T>
   1172 Vector<T>& Vector<T>::operator=(const Vector<T>& rhs)
   1173 {
   1174     if (&rhs != this)
   1175     {
   1176         resize(rhs.m_Count);
   1177         if (m_Count != 0)
   1178         {
   1179             memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
   1180         }
   1181     }
   1182     return *this;
   1183 }
   1184 
   1185 template<typename T>
   1186 T& Vector<T>::operator[](size_t index)
   1187 {
   1188     D3D12MA_HEAVY_ASSERT(index < m_Count);
   1189     return m_pArray[index];
   1190 }
   1191 
   1192 template<typename T>
   1193 const T& Vector<T>::operator[](size_t index) const
   1194 {
   1195     D3D12MA_HEAVY_ASSERT(index < m_Count);
   1196     return m_pArray[index];
   1197 }
   1198 #endif // _D3D12MA_VECTOR_FUNCTIONS
   1199 #endif // _D3D12MA_VECTOR
   1200 
   1201 #ifndef _D3D12MA_STRING_BUILDER
   1202 class StringBuilder
   1203 {
   1204 public:
   1205     StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) {}
   1206 
   1207     size_t GetLength() const { return m_Data.size(); }
   1208     LPCWSTR GetData() const { return m_Data.data(); }
   1209 
   1210     void Add(WCHAR ch) { m_Data.push_back(ch); }
   1211     void Add(LPCWSTR str);
   1212     void AddNewLine() { Add(L'\n'); }
   1213     void AddNumber(UINT num);
   1214     void AddNumber(UINT64 num);
   1215     void AddPointer(const void* ptr);
   1216 
   1217 private:
   1218     Vector<WCHAR> m_Data;
   1219 };
   1220 
   1221 #ifndef _D3D12MA_STRING_BUILDER_FUNCTIONS
   1222 void StringBuilder::Add(LPCWSTR str)
   1223 {
   1224     const size_t len = wcslen(str);
   1225     if (len > 0)
   1226     {
   1227         const size_t oldCount = m_Data.size();
   1228         m_Data.resize(oldCount + len);
   1229         memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));
   1230     }
   1231 }
   1232 
   1233 void StringBuilder::AddNumber(UINT num)
   1234 {
   1235     WCHAR buf[11];
   1236     buf[10] = L'\0';
   1237     WCHAR *p = &buf[10];
   1238     do
   1239     {
   1240         *--p = L'0' + (num % 10);
   1241         num /= 10;
   1242     }
   1243     while (num);
   1244     Add(p);
   1245 }
   1246 
   1247 void StringBuilder::AddNumber(UINT64 num)
   1248 {
   1249     WCHAR buf[21];
   1250     buf[20] = L'\0';
   1251     WCHAR *p = &buf[20];
   1252     do
   1253     {
   1254         *--p = L'0' + (num % 10);
   1255         num /= 10;
   1256     }
   1257     while (num);
   1258     Add(p);
   1259 }
   1260 
   1261 void StringBuilder::AddPointer(const void* ptr)
   1262 {
   1263     WCHAR buf[21];
   1264     uintptr_t num = (uintptr_t)ptr;
   1265     buf[20] = L'\0';
   1266     WCHAR *p = &buf[20];
   1267     do
   1268     {
   1269         *--p = HexDigitToChar((UINT8)(num & 0xF));
   1270         num >>= 4;
   1271     }
   1272     while (num);
   1273     Add(p);
   1274 }
   1275 
   1276 #endif // _D3D12MA_STRING_BUILDER_FUNCTIONS
   1277 #endif // _D3D12MA_STRING_BUILDER
   1278 
   1279 #ifndef _D3D12MA_JSON_WRITER
   1280 /*
   1281 Allows to conveniently build a correct JSON document to be written to the
   1282 StringBuilder passed to the constructor.
   1283 */
   1284 class JsonWriter
   1285 {
   1286 public:
   1287     // stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object.
   1288     JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);
   1289     ~JsonWriter();
   1290 
   1291     // Begins object by writing "{".
   1292     // Inside an object, you must call pairs of WriteString and a value, e.g.:
   1293     // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();
   1294     // Will write: { "A": 1, "B": 2 }
   1295     void BeginObject(bool singleLine = false);
   1296     // Ends object by writing "}".
   1297     void EndObject();
   1298 
   1299     // Begins array by writing "[".
   1300     // Inside an array, you can write a sequence of any values.
   1301     void BeginArray(bool singleLine = false);
   1302     // Ends array by writing "[".
   1303     void EndArray();
   1304 
   1305     // Writes a string value inside "".
   1306     // pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped.
   1307     void WriteString(LPCWSTR pStr);
   1308 
   1309     // Begins writing a string value.
   1310     // Call BeginString, ContinueString, ContinueString, ..., EndString instead of
   1311     // WriteString to conveniently build the string content incrementally, made of
   1312     // parts including numbers.
   1313     void BeginString(LPCWSTR pStr = NULL);
   1314     // Posts next part of an open string.
   1315     void ContinueString(LPCWSTR pStr);
   1316     // Posts next part of an open string. The number is converted to decimal characters.
   1317     void ContinueString(UINT num);
   1318     void ContinueString(UINT64 num);
   1319     void ContinueString_Pointer(const void* ptr);
   1320     // Posts next part of an open string. Pointer value is converted to characters
   1321     // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
   1322     // void ContinueString_Pointer(const void* ptr);
   1323     // Ends writing a string value by writing '"'.
   1324     void EndString(LPCWSTR pStr = NULL);
   1325 
   1326     // Writes a number value.
   1327     void WriteNumber(UINT num);
   1328     void WriteNumber(UINT64 num);
   1329     // Writes a boolean value - false or true.
   1330     void WriteBool(bool b);
   1331     // Writes a null value.
   1332     void WriteNull();
   1333 
   1334     void AddAllocationToObject(const Allocation& alloc);
   1335     void AddDetailedStatisticsInfoObject(const DetailedStatistics& stats);
   1336 
   1337 private:
   1338     static const WCHAR* const INDENT;
   1339 
   1340     enum CollectionType
   1341     {
   1342         COLLECTION_TYPE_OBJECT,
   1343         COLLECTION_TYPE_ARRAY,
   1344     };
   1345     struct StackItem
   1346     {
   1347         CollectionType type;
   1348         UINT valueCount;
   1349         bool singleLineMode;
   1350     };
   1351 
   1352     StringBuilder& m_SB;
   1353     Vector<StackItem> m_Stack;
   1354     bool m_InsideString;
   1355 
   1356     void BeginValue(bool isString);
   1357     void WriteIndent(bool oneLess = false);
   1358 };
   1359 
   1360 #ifndef _D3D12MA_JSON_WRITER_FUNCTIONS
   1361 const WCHAR* const JsonWriter::INDENT = L"  ";
   1362 
   1363 JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder)
   1364     : m_SB(stringBuilder),
   1365     m_Stack(allocationCallbacks),
   1366     m_InsideString(false) {}
   1367 
   1368 JsonWriter::~JsonWriter()
   1369 {
   1370     D3D12MA_ASSERT(!m_InsideString);
   1371     D3D12MA_ASSERT(m_Stack.empty());
   1372 }
   1373 
   1374 void JsonWriter::BeginObject(bool singleLine)
   1375 {
   1376     D3D12MA_ASSERT(!m_InsideString);
   1377 
   1378     BeginValue(false);
   1379     m_SB.Add(L'{');
   1380 
   1381     StackItem stackItem;
   1382     stackItem.type = COLLECTION_TYPE_OBJECT;
   1383     stackItem.valueCount = 0;
   1384     stackItem.singleLineMode = singleLine;
   1385     m_Stack.push_back(stackItem);
   1386 }
   1387 
   1388 void JsonWriter::EndObject()
   1389 {
   1390     D3D12MA_ASSERT(!m_InsideString);
   1391     D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
   1392     D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);
   1393 
   1394     WriteIndent(true);
   1395     m_SB.Add(L'}');
   1396 
   1397     m_Stack.pop_back();
   1398 }
   1399 
   1400 void JsonWriter::BeginArray(bool singleLine)
   1401 {
   1402     D3D12MA_ASSERT(!m_InsideString);
   1403 
   1404     BeginValue(false);
   1405     m_SB.Add(L'[');
   1406 
   1407     StackItem stackItem;
   1408     stackItem.type = COLLECTION_TYPE_ARRAY;
   1409     stackItem.valueCount = 0;
   1410     stackItem.singleLineMode = singleLine;
   1411     m_Stack.push_back(stackItem);
   1412 }
   1413 
   1414 void JsonWriter::EndArray()
   1415 {
   1416     D3D12MA_ASSERT(!m_InsideString);
   1417     D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
   1418 
   1419     WriteIndent(true);
   1420     m_SB.Add(L']');
   1421 
   1422     m_Stack.pop_back();
   1423 }
   1424 
   1425 void JsonWriter::WriteString(LPCWSTR pStr)
   1426 {
   1427     BeginString(pStr);
   1428     EndString();
   1429 }
   1430 
   1431 void JsonWriter::BeginString(LPCWSTR pStr)
   1432 {
   1433     D3D12MA_ASSERT(!m_InsideString);
   1434 
   1435     BeginValue(true);
   1436     m_InsideString = true;
   1437     m_SB.Add(L'"');
   1438     if (pStr != NULL)
   1439     {
   1440         ContinueString(pStr);
   1441     }
   1442 }
   1443 
   1444 void JsonWriter::ContinueString(LPCWSTR pStr)
   1445 {
   1446     D3D12MA_ASSERT(m_InsideString);
   1447     D3D12MA_ASSERT(pStr);
   1448 
   1449     for (const WCHAR *p = pStr; *p; ++p)
   1450     {
   1451         // the strings we encode are assumed to be in UTF-16LE format, the native
   1452         // windows wide character Unicode format. In this encoding Unicode code
   1453         // points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,
   1454         // and everything else takes more than two bytes. We will reject any
   1455         // multi wchar character encodings for simplicity.
   1456         UINT val = (UINT)*p;
   1457         D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&
   1458             "Character not currently supported.");
   1459         switch (*p)
   1460         {
   1461         case L'"':  m_SB.Add(L'\\'); m_SB.Add(L'"');  break;
   1462         case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;
   1463         case L'/':  m_SB.Add(L'\\'); m_SB.Add(L'/');  break;
   1464         case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b');  break;
   1465         case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f');  break;
   1466         case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n');  break;
   1467         case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r');  break;
   1468         case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't');  break;
   1469         default:
   1470             // conservatively use encoding \uXXXX for any Unicode character
   1471             // requiring more than one byte.
   1472             if (32 <= val && val < 256)
   1473                 m_SB.Add(*p);
   1474             else
   1475             {
   1476                 m_SB.Add(L'\\');
   1477                 m_SB.Add(L'u');
   1478                 for (UINT i = 0; i < 4; ++i)
   1479                 {
   1480                     UINT hexDigit = (val & 0xF000) >> 12;
   1481                     val <<= 4;
   1482                     if (hexDigit < 10)
   1483                         m_SB.Add(L'0' + (WCHAR)hexDigit);
   1484                     else
   1485                         m_SB.Add(L'A' + (WCHAR)hexDigit);
   1486                 }
   1487             }
   1488             break;
   1489         }
   1490     }
   1491 }
   1492 
   1493 void JsonWriter::ContinueString(UINT num)
   1494 {
   1495     D3D12MA_ASSERT(m_InsideString);
   1496     m_SB.AddNumber(num);
   1497 }
   1498 
   1499 void JsonWriter::ContinueString(UINT64 num)
   1500 {
   1501     D3D12MA_ASSERT(m_InsideString);
   1502     m_SB.AddNumber(num);
   1503 }
   1504 
   1505 void JsonWriter::ContinueString_Pointer(const void* ptr)
   1506 {
   1507     D3D12MA_ASSERT(m_InsideString);
   1508     m_SB.AddPointer(ptr);
   1509 }
   1510 
   1511 void JsonWriter::EndString(LPCWSTR pStr)
   1512 {
   1513     D3D12MA_ASSERT(m_InsideString);
   1514 
   1515     if (pStr)
   1516         ContinueString(pStr);
   1517     m_SB.Add(L'"');
   1518     m_InsideString = false;
   1519 }
   1520 
   1521 void JsonWriter::WriteNumber(UINT num)
   1522 {
   1523     D3D12MA_ASSERT(!m_InsideString);
   1524     BeginValue(false);
   1525     m_SB.AddNumber(num);
   1526 }
   1527 
   1528 void JsonWriter::WriteNumber(UINT64 num)
   1529 {
   1530     D3D12MA_ASSERT(!m_InsideString);
   1531     BeginValue(false);
   1532     m_SB.AddNumber(num);
   1533 }
   1534 
   1535 void JsonWriter::WriteBool(bool b)
   1536 {
   1537     D3D12MA_ASSERT(!m_InsideString);
   1538     BeginValue(false);
   1539     if (b)
   1540         m_SB.Add(L"true");
   1541     else
   1542         m_SB.Add(L"false");
   1543 }
   1544 
   1545 void JsonWriter::WriteNull()
   1546 {
   1547     D3D12MA_ASSERT(!m_InsideString);
   1548     BeginValue(false);
   1549     m_SB.Add(L"null");
   1550 }
   1551 
   1552 void JsonWriter::AddAllocationToObject(const Allocation& alloc)
   1553 {
   1554     WriteString(L"Type");
   1555     switch (alloc.m_PackedData.GetResourceDimension()) {
   1556     case D3D12_RESOURCE_DIMENSION_UNKNOWN:
   1557         WriteString(L"UNKNOWN");
   1558         break;
   1559     case D3D12_RESOURCE_DIMENSION_BUFFER:
   1560         WriteString(L"BUFFER");
   1561         break;
   1562     case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
   1563         WriteString(L"TEXTURE1D");
   1564         break;
   1565     case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
   1566         WriteString(L"TEXTURE2D");
   1567         break;
   1568     case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
   1569         WriteString(L"TEXTURE3D");
   1570         break;
   1571     default: D3D12MA_ASSERT(0); break;
   1572     }
   1573 
   1574     WriteString(L"Size");
   1575     WriteNumber(alloc.GetSize());
   1576     WriteString(L"Usage");
   1577     WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());
   1578 
   1579     void* privateData = alloc.GetPrivateData();
   1580     if (privateData)
   1581     {
   1582         WriteString(L"CustomData");
   1583         BeginString();
   1584         ContinueString_Pointer(privateData);
   1585         EndString();
   1586     }
   1587 
   1588     LPCWSTR name = alloc.GetName();
   1589     if (name != NULL)
   1590     {
   1591         WriteString(L"Name");
   1592         WriteString(name);
   1593     }
   1594     if (alloc.m_PackedData.GetTextureLayout())
   1595     {
   1596         WriteString(L"Layout");
   1597         WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());
   1598     }
   1599 }
   1600 
   1601 void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats)
   1602 {
   1603     BeginObject();
   1604 
   1605     WriteString(L"BlockCount");
   1606     WriteNumber(stats.Stats.BlockCount);
   1607     WriteString(L"BlockBytes");
   1608     WriteNumber(stats.Stats.BlockBytes);
   1609     WriteString(L"AllocationCount");
   1610     WriteNumber(stats.Stats.AllocationCount);
   1611     WriteString(L"AllocationBytes");
   1612     WriteNumber(stats.Stats.AllocationBytes);
   1613     WriteString(L"UnusedRangeCount");
   1614     WriteNumber(stats.UnusedRangeCount);
   1615 
   1616     if (stats.Stats.AllocationCount > 1)
   1617     {
   1618         WriteString(L"AllocationSizeMin");
   1619         WriteNumber(stats.AllocationSizeMin);
   1620         WriteString(L"AllocationSizeMax");
   1621         WriteNumber(stats.AllocationSizeMax);
   1622     }
   1623     if (stats.UnusedRangeCount > 1)
   1624     {
   1625         WriteString(L"UnusedRangeSizeMin");
   1626         WriteNumber(stats.UnusedRangeSizeMin);
   1627         WriteString(L"UnusedRangeSizeMax");
   1628         WriteNumber(stats.UnusedRangeSizeMax);
   1629     }
   1630     EndObject();
   1631 }
   1632 
   1633 void JsonWriter::BeginValue(bool isString)
   1634 {
   1635     if (!m_Stack.empty())
   1636     {
   1637         StackItem& currItem = m_Stack.back();
   1638         if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)
   1639         {
   1640             D3D12MA_ASSERT(isString);
   1641         }
   1642 
   1643         if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)
   1644         {
   1645             m_SB.Add(L':'); m_SB.Add(L' ');
   1646         }
   1647         else if (currItem.valueCount > 0)
   1648         {
   1649             m_SB.Add(L','); m_SB.Add(L' ');
   1650             WriteIndent();
   1651         }
   1652         else
   1653         {
   1654             WriteIndent();
   1655         }
   1656         ++currItem.valueCount;
   1657     }
   1658 }
   1659 
   1660 void JsonWriter::WriteIndent(bool oneLess)
   1661 {
   1662     if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
   1663     {
   1664         m_SB.AddNewLine();
   1665 
   1666         size_t count = m_Stack.size();
   1667         if (count > 0 && oneLess)
   1668         {
   1669             --count;
   1670         }
   1671         for (size_t i = 0; i < count; ++i)
   1672         {
   1673             m_SB.Add(INDENT);
   1674         }
   1675     }
   1676 }
   1677 #endif // _D3D12MA_JSON_WRITER_FUNCTIONS
   1678 #endif // _D3D12MA_JSON_WRITER
   1679 
   1680 #ifndef _D3D12MA_POOL_ALLOCATOR
   1681 /*
   1682 Allocator for objects of type T using a list of arrays (pools) to speed up
   1683 allocation. Number of elements that can be allocated is not bounded because
   1684 allocator can create multiple blocks.
   1685 T should be POD because constructor and destructor is not called in Alloc or
   1686 Free.
   1687 */
   1688 template<typename T>
   1689 class PoolAllocator
   1690 {
   1691     D3D12MA_CLASS_NO_COPY(PoolAllocator)
   1692 public:
   1693     // allocationCallbacks externally owned, must outlive this object.
   1694     PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);
   1695     ~PoolAllocator() { Clear(); }
   1696 
   1697     void Clear();
   1698     template<typename... Types>
   1699     T* Alloc(Types... args);
   1700     void Free(T* ptr);
   1701 
   1702 private:
   1703     union Item
   1704     {
   1705         UINT NextFreeIndex; // UINT32_MAX means end of list.
   1706         alignas(T) char Value[sizeof(T)];
   1707     };
   1708 
   1709     struct ItemBlock
   1710     {
   1711         Item* pItems;
   1712         UINT Capacity;
   1713         UINT FirstFreeIndex;
   1714     };
   1715 
   1716     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
   1717     const UINT m_FirstBlockCapacity;
   1718     Vector<ItemBlock> m_ItemBlocks;
   1719 
   1720     ItemBlock& CreateNewBlock();
   1721 };
   1722 
   1723 #ifndef _D3D12MA_POOL_ALLOCATOR_FUNCTIONS
   1724 template<typename T>
   1725 PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity)
   1726     : m_AllocationCallbacks(allocationCallbacks),
   1727     m_FirstBlockCapacity(firstBlockCapacity),
   1728     m_ItemBlocks(allocationCallbacks)
   1729 {
   1730     D3D12MA_ASSERT(m_FirstBlockCapacity > 1);
   1731 }
   1732 
   1733 template<typename T>
   1734 void PoolAllocator<T>::Clear()
   1735 {
   1736     for(size_t i = m_ItemBlocks.size(); i--; )
   1737     {
   1738         D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
   1739     }
   1740     m_ItemBlocks.clear(true);
   1741 }
   1742 
   1743 template<typename T> template<typename... Types>
   1744 T* PoolAllocator<T>::Alloc(Types... args)
   1745 {
   1746     for(size_t i = m_ItemBlocks.size(); i--; )
   1747     {
   1748         ItemBlock& block = m_ItemBlocks[i];
   1749         // This block has some free items: Use first one.
   1750         if(block.FirstFreeIndex != UINT32_MAX)
   1751         {
   1752             Item* const pItem = &block.pItems[block.FirstFreeIndex];
   1753             block.FirstFreeIndex = pItem->NextFreeIndex;
   1754             T* result = (T*)&pItem->Value;
   1755             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
   1756             return result;
   1757         }
   1758     }
   1759 
   1760     // No block has free item: Create new one and use it.
   1761     ItemBlock& newBlock = CreateNewBlock();
   1762     Item* const pItem = &newBlock.pItems[0];
   1763     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
   1764     T* result = (T*)pItem->Value;
   1765     new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
   1766     return result;
   1767 }
   1768 
   1769 template<typename T>
   1770 void PoolAllocator<T>::Free(T* ptr)
   1771 {
   1772     // Search all memory blocks to find ptr.
   1773     for(size_t i = m_ItemBlocks.size(); i--; )
   1774     {
   1775         ItemBlock& block = m_ItemBlocks[i];
   1776 
   1777         Item* pItemPtr;
   1778         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
   1779 
   1780         // Check if pItemPtr is in address range of this block.
   1781         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
   1782         {
   1783             ptr->~T(); // Explicit destructor call.
   1784             const UINT index = static_cast<UINT>(pItemPtr - block.pItems);
   1785             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
   1786             block.FirstFreeIndex = index;
   1787             return;
   1788         }
   1789     }
   1790     D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
   1791 }
   1792 
   1793 template<typename T>
   1794 typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()
   1795 {
   1796     const UINT newBlockCapacity = m_ItemBlocks.empty() ?
   1797         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
   1798 
   1799     const ItemBlock newBlock = {
   1800         D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),
   1801         newBlockCapacity,
   1802         0 };
   1803 
   1804     m_ItemBlocks.push_back(newBlock);
   1805 
   1806     // Setup singly-linked list of all free items in this block.
   1807     for(UINT i = 0; i < newBlockCapacity - 1; ++i)
   1808     {
   1809         newBlock.pItems[i].NextFreeIndex = i + 1;
   1810     }
   1811     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
   1812     return m_ItemBlocks.back();
   1813 }
   1814 #endif // _D3D12MA_POOL_ALLOCATOR_FUNCTIONS
   1815 #endif // _D3D12MA_POOL_ALLOCATOR
   1816 
   1817 #ifndef _D3D12MA_LIST
   1818 /*
   1819 Doubly linked list, with elements allocated out of PoolAllocator.
   1820 Has custom interface, as well as STL-style interface, including iterator and
   1821 const_iterator.
   1822 */
   1823 template<typename T>
   1824 class List
   1825 {
   1826     D3D12MA_CLASS_NO_COPY(List)
   1827 public:
   1828     struct Item
   1829     {
   1830         Item* pPrev;
   1831         Item* pNext;
   1832         T Value;
   1833     };
   1834 
   1835     class reverse_iterator;
   1836     class const_reverse_iterator;
   1837     class iterator
   1838     {
   1839         friend class List<T>;
   1840         friend class const_iterator;
   1841 
   1842     public:
   1843         iterator() = default;
   1844         iterator(const reverse_iterator& src)
   1845             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1846 
   1847         T& operator*() const;
   1848         T* operator->() const;
   1849 
   1850         iterator& operator++();
   1851         iterator& operator--();
   1852         iterator operator++(int);
   1853         iterator operator--(int);
   1854 
   1855         bool operator==(const iterator& rhs) const;
   1856         bool operator!=(const iterator& rhs) const;
   1857 
   1858     private:
   1859         List<T>* m_pList = NULL;
   1860         Item* m_pItem = NULL;
   1861 
   1862         iterator(List<T>* pList, Item* pItem) : m_pList(pList), m_pItem(pItem) {}
   1863     };
   1864 
   1865     class reverse_iterator
   1866     {
   1867         friend class List<T>;
   1868         friend class const_reverse_iterator;
   1869 
   1870     public:
   1871         reverse_iterator() = default;
   1872         reverse_iterator(const iterator& src)
   1873             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1874 
   1875         T& operator*() const;
   1876         T* operator->() const;
   1877 
   1878         reverse_iterator& operator++();
   1879         reverse_iterator& operator--();
   1880         reverse_iterator operator++(int);
   1881         reverse_iterator operator--(int);
   1882 
   1883         bool operator==(const reverse_iterator& rhs) const;
   1884         bool operator!=(const reverse_iterator& rhs) const;
   1885 
   1886     private:
   1887         List<T>* m_pList = NULL;
   1888         Item* m_pItem = NULL;
   1889 
   1890         reverse_iterator(List<T>* pList, Item* pItem)
   1891             : m_pList(pList), m_pItem(pItem) {}
   1892     };
   1893 
   1894     class const_iterator
   1895     {
   1896         friend class List<T>;
   1897 
   1898     public:
   1899         const_iterator() = default;
   1900         const_iterator(const iterator& src)
   1901             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1902         const_iterator(const reverse_iterator& src)
   1903             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1904         const_iterator(const const_reverse_iterator& src)
   1905             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1906 
   1907         iterator dropConst() const;
   1908         const T& operator*() const;
   1909         const T* operator->() const;
   1910 
   1911         const_iterator& operator++();
   1912         const_iterator& operator--();
   1913         const_iterator operator++(int);
   1914         const_iterator operator--(int);
   1915 
   1916         bool operator==(const const_iterator& rhs) const;
   1917         bool operator!=(const const_iterator& rhs) const;
   1918 
   1919     private:
   1920         const List<T>* m_pList = NULL;
   1921         const Item* m_pItem = NULL;
   1922 
   1923         const_iterator(const List<T>* pList, const Item* pItem)
   1924             : m_pList(pList), m_pItem(pItem) {}
   1925     };
   1926 
   1927     class const_reverse_iterator
   1928     {
   1929         friend class List<T>;
   1930 
   1931     public:
   1932         const_reverse_iterator() = default;
   1933         const_reverse_iterator(const iterator& src)
   1934             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1935         const_reverse_iterator(const reverse_iterator& src)
   1936             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1937         const_reverse_iterator(const const_iterator& src)
   1938             : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
   1939 
   1940         reverse_iterator dropConst() const;
   1941         const T& operator*() const;
   1942         const T* operator->() const;
   1943 
   1944         const_reverse_iterator& operator++();
   1945         const_reverse_iterator& operator--();
   1946         const_reverse_iterator operator++(int);
   1947         const_reverse_iterator operator--(int);
   1948 
   1949         bool operator==(const const_reverse_iterator& rhs) const;
   1950         bool operator!=(const const_reverse_iterator& rhs) const;
   1951 
   1952     private:
   1953         const List<T>* m_pList = NULL;
   1954         const Item* m_pItem = NULL;
   1955 
   1956         const_reverse_iterator(const List<T>* pList, const Item* pItem)
   1957             : m_pList(pList), m_pItem(pItem) {}
   1958     };
   1959 
   1960     // allocationCallbacks externally owned, must outlive this object.
   1961     List(const ALLOCATION_CALLBACKS& allocationCallbacks);
   1962     // Intentionally not calling Clear, because that would be unnecessary
   1963     // computations to return all items to m_ItemAllocator as free.
   1964     ~List() = default;
   1965     
   1966     size_t GetCount() const { return m_Count; }
   1967     bool IsEmpty() const { return m_Count == 0; }
   1968 
   1969     Item* Front() { return m_pFront; }
   1970     const Item* Front() const { return m_pFront; }
   1971     Item* Back() { return m_pBack; }
   1972     const Item* Back() const { return m_pBack; }
   1973 
   1974     bool empty() const { return IsEmpty(); }
   1975     size_t size() const { return GetCount(); }
   1976     void push_back(const T& value) { PushBack(value); }
   1977     iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }
   1978     void clear() { Clear(); }
   1979     void erase(iterator it) { Remove(it.m_pItem); }
   1980 
   1981     iterator begin() { return iterator(this, Front()); }
   1982     iterator end() { return iterator(this, NULL); }
   1983     reverse_iterator rbegin() { return reverse_iterator(this, Back()); }
   1984     reverse_iterator rend() { return reverse_iterator(this, NULL); }
   1985 
   1986     const_iterator cbegin() const { return const_iterator(this, Front()); }
   1987     const_iterator cend() const { return const_iterator(this, NULL); }
   1988     const_iterator begin() const { return cbegin(); }
   1989     const_iterator end() const { return cend(); }
   1990 
   1991     const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }
   1992     const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }
   1993     const_reverse_iterator rbegin() const { return crbegin(); }
   1994     const_reverse_iterator rend() const { return crend(); }
   1995 
   1996     Item* PushBack();
   1997     Item* PushFront();
   1998     Item* PushBack(const T& value);
   1999     Item* PushFront(const T& value);
   2000     void PopBack();
   2001     void PopFront();
   2002 
   2003     // Item can be null - it means PushBack.
   2004     Item* InsertBefore(Item* pItem);
   2005     // Item can be null - it means PushFront.
   2006     Item* InsertAfter(Item* pItem);
   2007     Item* InsertBefore(Item* pItem, const T& value);
   2008     Item* InsertAfter(Item* pItem, const T& value);
   2009 
   2010     void Clear();
   2011     void Remove(Item* pItem);
   2012 
   2013 private:
   2014     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
   2015     PoolAllocator<Item> m_ItemAllocator;
   2016     Item* m_pFront;
   2017     Item* m_pBack;
   2018     size_t m_Count;
   2019 };
   2020 
   2021 #ifndef _D3D12MA_LIST_ITERATOR_FUNCTIONS
   2022 template<typename T>
   2023 T& List<T>::iterator::operator*() const
   2024 {
   2025     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2026     return m_pItem->Value;
   2027 }
   2028 
   2029 template<typename T>
   2030 T* List<T>::iterator::operator->() const
   2031 {
   2032     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2033     return &m_pItem->Value;
   2034 }
   2035 
   2036 template<typename T>
   2037 typename List<T>::iterator& List<T>::iterator::operator++()
   2038 {
   2039     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2040     m_pItem = m_pItem->pNext;
   2041     return *this;
   2042 }
   2043 
   2044 template<typename T>
   2045 typename List<T>::iterator& List<T>::iterator::operator--()
   2046 {
   2047     if (m_pItem != NULL)
   2048     {
   2049         m_pItem = m_pItem->pPrev;
   2050     }
   2051     else
   2052     {
   2053         D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
   2054         m_pItem = m_pList->Back();
   2055     }
   2056     return *this;
   2057 }
   2058 
   2059 template<typename T>
   2060 typename List<T>::iterator List<T>::iterator::operator++(int)
   2061 {
   2062     iterator result = *this;
   2063     ++* this;
   2064     return result;
   2065 }
   2066 
   2067 template<typename T>
   2068 typename List<T>::iterator List<T>::iterator::operator--(int)
   2069 {
   2070     iterator result = *this;
   2071     --* this;
   2072     return result;
   2073 }
   2074 
   2075 template<typename T>
   2076 bool List<T>::iterator::operator==(const iterator& rhs) const
   2077 {
   2078     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2079     return m_pItem == rhs.m_pItem;
   2080 }
   2081 
   2082 template<typename T>
   2083 bool List<T>::iterator::operator!=(const iterator& rhs) const
   2084 {
   2085     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2086     return m_pItem != rhs.m_pItem;
   2087 }
   2088 #endif // _D3D12MA_LIST_ITERATOR_FUNCTIONS
   2089 
   2090 #ifndef _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS
   2091 template<typename T>
   2092 T& List<T>::reverse_iterator::operator*() const
   2093 {
   2094     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2095     return m_pItem->Value;
   2096 }
   2097 
   2098 template<typename T>
   2099 T* List<T>::reverse_iterator::operator->() const
   2100 {
   2101     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2102     return &m_pItem->Value;
   2103 }
   2104 
   2105 template<typename T>
   2106 typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator++()
   2107 {
   2108     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2109     m_pItem = m_pItem->pPrev;
   2110     return *this;
   2111 }
   2112 
   2113 template<typename T>
   2114 typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator--()
   2115 {
   2116     if (m_pItem != NULL)
   2117     {
   2118         m_pItem = m_pItem->pNext;
   2119     }
   2120     else
   2121     {
   2122         D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
   2123         m_pItem = m_pList->Front();
   2124     }
   2125     return *this;
   2126 }
   2127 
   2128 template<typename T>
   2129 typename List<T>::reverse_iterator List<T>::reverse_iterator::operator++(int)
   2130 {
   2131     reverse_iterator result = *this;
   2132     ++* this;
   2133     return result;
   2134 }
   2135 
   2136 template<typename T>
   2137 typename List<T>::reverse_iterator List<T>::reverse_iterator::operator--(int)
   2138 {
   2139     reverse_iterator result = *this;
   2140     --* this;
   2141     return result;
   2142 }
   2143 
   2144 template<typename T>
   2145 bool List<T>::reverse_iterator::operator==(const reverse_iterator& rhs) const
   2146 {
   2147     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2148     return m_pItem == rhs.m_pItem;
   2149 }
   2150 
   2151 template<typename T>
   2152 bool List<T>::reverse_iterator::operator!=(const reverse_iterator& rhs) const
   2153 {
   2154     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2155     return m_pItem != rhs.m_pItem;
   2156 }
   2157 #endif // _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS
   2158 
   2159 #ifndef _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS
   2160 template<typename T>
   2161 typename List<T>::iterator List<T>::const_iterator::dropConst() const
   2162 {
   2163     return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));
   2164 }
   2165 
   2166 template<typename T>
   2167 const T& List<T>::const_iterator::operator*() const
   2168 {
   2169     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2170     return m_pItem->Value;
   2171 }
   2172 
   2173 template<typename T>
   2174 const T* List<T>::const_iterator::operator->() const
   2175 {
   2176     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2177     return &m_pItem->Value;
   2178 }
   2179 
   2180 template<typename T>
   2181 typename List<T>::const_iterator& List<T>::const_iterator::operator++()
   2182 {
   2183     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2184     m_pItem = m_pItem->pNext;
   2185     return *this;
   2186 }
   2187 
   2188 template<typename T>
   2189 typename List<T>::const_iterator& List<T>::const_iterator::operator--()
   2190 {
   2191     if (m_pItem != NULL)
   2192     {
   2193         m_pItem = m_pItem->pPrev;
   2194     }
   2195     else
   2196     {
   2197         D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
   2198         m_pItem = m_pList->Back();
   2199     }
   2200     return *this;
   2201 }
   2202 
   2203 template<typename T>
   2204 typename List<T>::const_iterator List<T>::const_iterator::operator++(int)
   2205 {
   2206     const_iterator result = *this;
   2207     ++* this;
   2208     return result;
   2209 }
   2210 
   2211 template<typename T>
   2212 typename List<T>::const_iterator List<T>::const_iterator::operator--(int)
   2213 {
   2214     const_iterator result = *this;
   2215     --* this;
   2216     return result;
   2217 }
   2218 
   2219 template<typename T>
   2220 bool List<T>::const_iterator::operator==(const const_iterator& rhs) const
   2221 {
   2222     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2223     return m_pItem == rhs.m_pItem;
   2224 }
   2225 
   2226 template<typename T>
   2227 bool List<T>::const_iterator::operator!=(const const_iterator& rhs) const
   2228 {
   2229     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2230     return m_pItem != rhs.m_pItem;
   2231 }
   2232 #endif // _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS
   2233 
   2234 #ifndef _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS
   2235 template<typename T>
   2236 typename List<T>::reverse_iterator List<T>::const_reverse_iterator::dropConst() const
   2237 {
   2238     return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));
   2239 }
   2240 
   2241 template<typename T>
   2242 const T& List<T>::const_reverse_iterator::operator*() const
   2243 {
   2244     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2245     return m_pItem->Value;
   2246 }
   2247 
   2248 template<typename T>
   2249 const T* List<T>::const_reverse_iterator::operator->() const
   2250 {
   2251     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2252     return &m_pItem->Value;
   2253 }
   2254 
   2255 template<typename T>
   2256 typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator++()
   2257 {
   2258     D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
   2259     m_pItem = m_pItem->pPrev;
   2260     return *this;
   2261 }
   2262 
   2263 template<typename T>
   2264 typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator--()
   2265 {
   2266     if (m_pItem != NULL)
   2267     {
   2268         m_pItem = m_pItem->pNext;
   2269     }
   2270     else
   2271     {
   2272         D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
   2273         m_pItem = m_pList->Front();
   2274     }
   2275     return *this;
   2276 }
   2277 
   2278 template<typename T>
   2279 typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator++(int)
   2280 {
   2281     const_reverse_iterator result = *this;
   2282     ++* this;
   2283     return result;
   2284 }
   2285 
   2286 template<typename T>
   2287 typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator--(int)
   2288 {
   2289     const_reverse_iterator result = *this;
   2290     --* this;
   2291     return result;
   2292 }
   2293 
   2294 template<typename T>
   2295 bool List<T>::const_reverse_iterator::operator==(const const_reverse_iterator& rhs) const
   2296 {
   2297     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2298     return m_pItem == rhs.m_pItem;
   2299 }
   2300 
   2301 template<typename T>
   2302 bool List<T>::const_reverse_iterator::operator!=(const const_reverse_iterator& rhs) const
   2303 {
   2304     D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
   2305     return m_pItem != rhs.m_pItem;
   2306 }
   2307 #endif // _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS
   2308 
   2309 #ifndef _D3D12MA_LIST_FUNCTIONS
   2310 template<typename T>
   2311 List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks)
   2312     : m_AllocationCallbacks(allocationCallbacks),
   2313     m_ItemAllocator(allocationCallbacks, 128),
   2314     m_pFront(NULL),
   2315     m_pBack(NULL),
   2316     m_Count(0) {}
   2317 
   2318 template<typename T>
   2319 void List<T>::Clear()
   2320 {
   2321     if(!IsEmpty())
   2322     {
   2323         Item* pItem = m_pBack;
   2324         while(pItem != NULL)
   2325         {
   2326             Item* const pPrevItem = pItem->pPrev;
   2327             m_ItemAllocator.Free(pItem);
   2328             pItem = pPrevItem;
   2329         }
   2330         m_pFront = NULL;
   2331         m_pBack = NULL;
   2332         m_Count = 0;
   2333     }
   2334 }
   2335 
   2336 template<typename T>
   2337 typename List<T>::Item* List<T>::PushBack()
   2338 {
   2339     Item* const pNewItem = m_ItemAllocator.Alloc();
   2340     pNewItem->pNext = NULL;
   2341     if(IsEmpty())
   2342     {
   2343         pNewItem->pPrev = NULL;
   2344         m_pFront = pNewItem;
   2345         m_pBack = pNewItem;
   2346         m_Count = 1;
   2347     }
   2348     else
   2349     {
   2350         pNewItem->pPrev = m_pBack;
   2351         m_pBack->pNext = pNewItem;
   2352         m_pBack = pNewItem;
   2353         ++m_Count;
   2354     }
   2355     return pNewItem;
   2356 }
   2357 
   2358 template<typename T>
   2359 typename List<T>::Item* List<T>::PushFront()
   2360 {
   2361     Item* const pNewItem = m_ItemAllocator.Alloc();
   2362     pNewItem->pPrev = NULL;
   2363     if(IsEmpty())
   2364     {
   2365         pNewItem->pNext = NULL;
   2366         m_pFront = pNewItem;
   2367         m_pBack = pNewItem;
   2368         m_Count = 1;
   2369     }
   2370     else
   2371     {
   2372         pNewItem->pNext = m_pFront;
   2373         m_pFront->pPrev = pNewItem;
   2374         m_pFront = pNewItem;
   2375         ++m_Count;
   2376     }
   2377     return pNewItem;
   2378 }
   2379 
   2380 template<typename T>
   2381 typename List<T>::Item* List<T>::PushBack(const T& value)
   2382 {
   2383     Item* const pNewItem = PushBack();
   2384     pNewItem->Value = value;
   2385     return pNewItem;
   2386 }
   2387 
   2388 template<typename T>
   2389 typename List<T>::Item* List<T>::PushFront(const T& value)
   2390 {
   2391     Item* const pNewItem = PushFront();
   2392     pNewItem->Value = value;
   2393     return pNewItem;
   2394 }
   2395 
   2396 template<typename T>
   2397 void List<T>::PopBack()
   2398 {
   2399     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   2400     Item* const pBackItem = m_pBack;
   2401     Item* const pPrevItem = pBackItem->pPrev;
   2402     if(pPrevItem != NULL)
   2403     {
   2404         pPrevItem->pNext = NULL;
   2405     }
   2406     m_pBack = pPrevItem;
   2407     m_ItemAllocator.Free(pBackItem);
   2408     --m_Count;
   2409 }
   2410 
   2411 template<typename T>
   2412 void List<T>::PopFront()
   2413 {
   2414     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   2415     Item* const pFrontItem = m_pFront;
   2416     Item* const pNextItem = pFrontItem->pNext;
   2417     if(pNextItem != NULL)
   2418     {
   2419         pNextItem->pPrev = NULL;
   2420     }
   2421     m_pFront = pNextItem;
   2422     m_ItemAllocator.Free(pFrontItem);
   2423     --m_Count;
   2424 }
   2425 
   2426 template<typename T>
   2427 void List<T>::Remove(Item* pItem)
   2428 {
   2429     D3D12MA_HEAVY_ASSERT(pItem != NULL);
   2430     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   2431 
   2432     if(pItem->pPrev != NULL)
   2433     {
   2434         pItem->pPrev->pNext = pItem->pNext;
   2435     }
   2436     else
   2437     {
   2438         D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
   2439         m_pFront = pItem->pNext;
   2440     }
   2441 
   2442     if(pItem->pNext != NULL)
   2443     {
   2444         pItem->pNext->pPrev = pItem->pPrev;
   2445     }
   2446     else
   2447     {
   2448         D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
   2449         m_pBack = pItem->pPrev;
   2450     }
   2451 
   2452     m_ItemAllocator.Free(pItem);
   2453     --m_Count;
   2454 }
   2455 
   2456 template<typename T>
   2457 typename List<T>::Item* List<T>::InsertBefore(Item* pItem)
   2458 {
   2459     if(pItem != NULL)
   2460     {
   2461         Item* const prevItem = pItem->pPrev;
   2462         Item* const newItem = m_ItemAllocator.Alloc();
   2463         newItem->pPrev = prevItem;
   2464         newItem->pNext = pItem;
   2465         pItem->pPrev = newItem;
   2466         if(prevItem != NULL)
   2467         {
   2468             prevItem->pNext = newItem;
   2469         }
   2470         else
   2471         {
   2472             D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
   2473             m_pFront = newItem;
   2474         }
   2475         ++m_Count;
   2476         return newItem;
   2477     }
   2478     else
   2479     {
   2480         return PushBack();
   2481     }
   2482 }
   2483 
   2484 template<typename T>
   2485 typename List<T>::Item* List<T>::InsertAfter(Item* pItem)
   2486 {
   2487     if(pItem != NULL)
   2488     {
   2489         Item* const nextItem = pItem->pNext;
   2490         Item* const newItem = m_ItemAllocator.Alloc();
   2491         newItem->pNext = nextItem;
   2492         newItem->pPrev = pItem;
   2493         pItem->pNext = newItem;
   2494         if(nextItem != NULL)
   2495         {
   2496             nextItem->pPrev = newItem;
   2497         }
   2498         else
   2499         {
   2500             D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
   2501             m_pBack = newItem;
   2502         }
   2503         ++m_Count;
   2504         return newItem;
   2505     }
   2506     else
   2507         return PushFront();
   2508 }
   2509 
   2510 template<typename T>
   2511 typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)
   2512 {
   2513     Item* const newItem = InsertBefore(pItem);
   2514     newItem->Value = value;
   2515     return newItem;
   2516 }
   2517 
   2518 template<typename T>
   2519 typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)
   2520 {
   2521     Item* const newItem = InsertAfter(pItem);
   2522     newItem->Value = value;
   2523     return newItem;
   2524 }
   2525 #endif // _D3D12MA_LIST_FUNCTIONS
   2526 #endif // _D3D12MA_LIST
   2527 
   2528 #ifndef _D3D12MA_INTRUSIVE_LINKED_LIST
   2529 /*
   2530 Expected interface of ItemTypeTraits:
   2531 struct MyItemTypeTraits
   2532 {
   2533     using ItemType = MyItem;
   2534     static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
   2535     static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
   2536     static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
   2537     static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
   2538 };
   2539 */
   2540 template<typename ItemTypeTraits>
   2541 class IntrusiveLinkedList
   2542 {
   2543 public:
   2544     using ItemType = typename ItemTypeTraits::ItemType;
   2545     static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
   2546     static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
   2547 
   2548     // Movable, not copyable.
   2549     IntrusiveLinkedList() = default;
   2550     IntrusiveLinkedList(const IntrusiveLinkedList&) = delete;
   2551     IntrusiveLinkedList(IntrusiveLinkedList&& src);
   2552     IntrusiveLinkedList& operator=(const IntrusiveLinkedList&) = delete;
   2553     IntrusiveLinkedList& operator=(IntrusiveLinkedList&& src);
   2554     ~IntrusiveLinkedList() { D3D12MA_HEAVY_ASSERT(IsEmpty()); }
   2555 
   2556     size_t GetCount() const { return m_Count; }
   2557     bool IsEmpty() const { return m_Count == 0; }
   2558 
   2559     ItemType* Front() { return m_Front; }
   2560     ItemType* Back() { return m_Back; }
   2561     const ItemType* Front() const { return m_Front; }
   2562     const ItemType* Back() const { return m_Back; }
   2563 
   2564     void PushBack(ItemType* item);
   2565     void PushFront(ItemType* item);
   2566     ItemType* PopBack();
   2567     ItemType* PopFront();
   2568 
   2569     // MyItem can be null - it means PushBack.
   2570     void InsertBefore(ItemType* existingItem, ItemType* newItem);
   2571     // MyItem can be null - it means PushFront.
   2572     void InsertAfter(ItemType* existingItem, ItemType* newItem);
   2573 
   2574     void Remove(ItemType* item);
   2575     void RemoveAll();
   2576 
   2577 private:
   2578     ItemType* m_Front = NULL;
   2579     ItemType* m_Back = NULL;
   2580     size_t m_Count = 0;
   2581 };
   2582 
   2583 #ifndef _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS
   2584 template<typename ItemTypeTraits>
   2585 IntrusiveLinkedList<ItemTypeTraits>::IntrusiveLinkedList(IntrusiveLinkedList&& src)
   2586     : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
   2587 {
   2588     src.m_Front = src.m_Back = NULL;
   2589     src.m_Count = 0;
   2590 }
   2591 
   2592 template<typename ItemTypeTraits>
   2593 IntrusiveLinkedList<ItemTypeTraits>& IntrusiveLinkedList<ItemTypeTraits>::operator=(IntrusiveLinkedList&& src)
   2594 {
   2595     if (&src != this)
   2596     {
   2597         D3D12MA_HEAVY_ASSERT(IsEmpty());
   2598         m_Front = src.m_Front;
   2599         m_Back = src.m_Back;
   2600         m_Count = src.m_Count;
   2601         src.m_Front = src.m_Back = NULL;
   2602         src.m_Count = 0;
   2603     }
   2604     return *this;
   2605 }
   2606 
   2607 template<typename ItemTypeTraits>
   2608 void IntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)
   2609 {
   2610     D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);
   2611     if (IsEmpty())
   2612     {
   2613         m_Front = item;
   2614         m_Back = item;
   2615         m_Count = 1;
   2616     }
   2617     else
   2618     {
   2619         ItemTypeTraits::AccessPrev(item) = m_Back;
   2620         ItemTypeTraits::AccessNext(m_Back) = item;
   2621         m_Back = item;
   2622         ++m_Count;
   2623     }
   2624 }
   2625 
   2626 template<typename ItemTypeTraits>
   2627 void IntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)
   2628 {
   2629     D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);
   2630     if (IsEmpty())
   2631     {
   2632         m_Front = item;
   2633         m_Back = item;
   2634         m_Count = 1;
   2635     }
   2636     else
   2637     {
   2638         ItemTypeTraits::AccessNext(item) = m_Front;
   2639         ItemTypeTraits::AccessPrev(m_Front) = item;
   2640         m_Front = item;
   2641         ++m_Count;
   2642     }
   2643 }
   2644 
   2645 template<typename ItemTypeTraits>
   2646 typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopBack()
   2647 {
   2648     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   2649     ItemType* const backItem = m_Back;
   2650     ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
   2651     if (prevItem != NULL)
   2652     {
   2653         ItemTypeTraits::AccessNext(prevItem) = NULL;
   2654     }
   2655     m_Back = prevItem;
   2656     --m_Count;
   2657     ItemTypeTraits::AccessPrev(backItem) = NULL;
   2658     ItemTypeTraits::AccessNext(backItem) = NULL;
   2659     return backItem;
   2660 }
   2661 
   2662 template<typename ItemTypeTraits>
   2663 typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopFront()
   2664 {
   2665     D3D12MA_HEAVY_ASSERT(m_Count > 0);
   2666     ItemType* const frontItem = m_Front;
   2667     ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
   2668     if (nextItem != NULL)
   2669     {
   2670         ItemTypeTraits::AccessPrev(nextItem) = NULL;
   2671     }
   2672     m_Front = nextItem;
   2673     --m_Count;
   2674     ItemTypeTraits::AccessPrev(frontItem) = NULL;
   2675     ItemTypeTraits::AccessNext(frontItem) = NULL;
   2676     return frontItem;
   2677 }
   2678 
   2679 template<typename ItemTypeTraits>
   2680 void IntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)
   2681 {
   2682     D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);
   2683     if (existingItem != NULL)
   2684     {
   2685         ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
   2686         ItemTypeTraits::AccessPrev(newItem) = prevItem;
   2687         ItemTypeTraits::AccessNext(newItem) = existingItem;
   2688         ItemTypeTraits::AccessPrev(existingItem) = newItem;
   2689         if (prevItem != NULL)
   2690         {
   2691             ItemTypeTraits::AccessNext(prevItem) = newItem;
   2692         }
   2693         else
   2694         {
   2695             D3D12MA_HEAVY_ASSERT(m_Front == existingItem);
   2696             m_Front = newItem;
   2697         }
   2698         ++m_Count;
   2699     }
   2700     else
   2701         PushBack(newItem);
   2702 }
   2703 
   2704 template<typename ItemTypeTraits>
   2705 void IntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)
   2706 {
   2707     D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);
   2708     if (existingItem != NULL)
   2709     {
   2710         ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
   2711         ItemTypeTraits::AccessNext(newItem) = nextItem;
   2712         ItemTypeTraits::AccessPrev(newItem) = existingItem;
   2713         ItemTypeTraits::AccessNext(existingItem) = newItem;
   2714         if (nextItem != NULL)
   2715         {
   2716             ItemTypeTraits::AccessPrev(nextItem) = newItem;
   2717         }
   2718         else
   2719         {
   2720             D3D12MA_HEAVY_ASSERT(m_Back == existingItem);
   2721             m_Back = newItem;
   2722         }
   2723         ++m_Count;
   2724     }
   2725     else
   2726         return PushFront(newItem);
   2727 }
   2728 
   2729 template<typename ItemTypeTraits>
   2730 void IntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)
   2731 {
   2732     D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0);
   2733     if (ItemTypeTraits::GetPrev(item) != NULL)
   2734     {
   2735         ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
   2736     }
   2737     else
   2738     {
   2739         D3D12MA_HEAVY_ASSERT(m_Front == item);
   2740         m_Front = ItemTypeTraits::GetNext(item);
   2741     }
   2742 
   2743     if (ItemTypeTraits::GetNext(item) != NULL)
   2744     {
   2745         ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
   2746     }
   2747     else
   2748     {
   2749         D3D12MA_HEAVY_ASSERT(m_Back == item);
   2750         m_Back = ItemTypeTraits::GetPrev(item);
   2751     }
   2752     ItemTypeTraits::AccessPrev(item) = NULL;
   2753     ItemTypeTraits::AccessNext(item) = NULL;
   2754     --m_Count;
   2755 }
   2756 
   2757 template<typename ItemTypeTraits>
   2758 void IntrusiveLinkedList<ItemTypeTraits>::RemoveAll()
   2759 {
   2760     if (!IsEmpty())
   2761     {
   2762         ItemType* item = m_Back;
   2763         while (item != NULL)
   2764         {
   2765             ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
   2766             ItemTypeTraits::AccessPrev(item) = NULL;
   2767             ItemTypeTraits::AccessNext(item) = NULL;
   2768             item = prevItem;
   2769         }
   2770         m_Front = NULL;
   2771         m_Back = NULL;
   2772         m_Count = 0;
   2773     }
   2774 }
   2775 #endif // _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS
   2776 #endif // _D3D12MA_INTRUSIVE_LINKED_LIST
   2777 
   2778 #ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR
   2779 /*
   2780 Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.
   2781 */
   2782 class AllocationObjectAllocator
   2783 {
   2784     D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);
   2785 public:
   2786     AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks)
   2787         : m_Allocator(allocationCallbacks, 1024) {}
   2788 
   2789     template<typename... Types>
   2790     Allocation* Allocate(Types... args);
   2791     void Free(Allocation* alloc);
   2792 
   2793 private:
   2794     D3D12MA_MUTEX m_Mutex;
   2795     PoolAllocator<Allocation> m_Allocator;
   2796 };
   2797 
   2798 #ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS
   2799 template<typename... Types>
   2800 Allocation* AllocationObjectAllocator::Allocate(Types... args)
   2801 {
   2802     MutexLock mutexLock(m_Mutex);
   2803     return m_Allocator.Alloc(std::forward<Types>(args)...);
   2804 }
   2805 
   2806 void AllocationObjectAllocator::Free(Allocation* alloc)
   2807 {
   2808     MutexLock mutexLock(m_Mutex);
   2809     m_Allocator.Free(alloc);
   2810 }
   2811 #endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS
   2812 #endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR
   2813 
   2814 #ifndef _D3D12MA_SUBALLOCATION
   2815 /*
   2816 Represents a region of NormalBlock that is either assigned and returned as
   2817 allocated memory block or free.
   2818 */
   2819 struct Suballocation
   2820 {
   2821     UINT64 offset;
   2822     UINT64 size;
   2823     void* privateData;
   2824     SuballocationType type;
   2825 };
   2826 using SuballocationList = List<Suballocation>;
   2827 
   2828 // Comparator for offsets.
   2829 struct SuballocationOffsetLess
   2830 {
   2831     bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
   2832     {
   2833         return lhs.offset < rhs.offset;
   2834     }
   2835 };
   2836 
   2837 struct SuballocationOffsetGreater
   2838 {
   2839     bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
   2840     {
   2841         return lhs.offset > rhs.offset;
   2842     }
   2843 };
   2844 
   2845 struct SuballocationItemSizeLess
   2846 {
   2847     bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const
   2848     {
   2849         return lhs->size < rhs->size;
   2850     }
   2851     bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const
   2852     {
   2853         return lhs->size < rhsSize;
   2854     }
   2855 };
   2856 #endif // _D3D12MA_SUBALLOCATION
   2857 
   2858 #ifndef _D3D12MA_ALLOCATION_REQUEST
   2859 /*
   2860 Parameters of planned allocation inside a NormalBlock.
   2861 */
   2862 struct AllocationRequest
   2863 {
   2864     AllocHandle allocHandle;
   2865     UINT64 size;
   2866     UINT64 algorithmData;
   2867     UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.
   2868     UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
   2869     SuballocationList::iterator item;
   2870     BOOL zeroInitialized = FALSE; // TODO Implement proper handling in TLSF and Linear, using ZeroInitializedRange class.
   2871 };
   2872 #endif // _D3D12MA_ALLOCATION_REQUEST
   2873 
   2874 #ifndef _D3D12MA_ZERO_INITIALIZED_RANGE
   2875 /*
   2876 Keeps track of the range of bytes that are surely initialized with zeros.
   2877 Everything outside of it is considered uninitialized memory that may contain
   2878 garbage data.
   2879 
   2880 The range is left-inclusive.
   2881 */
   2882 class ZeroInitializedRange
   2883 {
   2884 public:
   2885     void Reset(UINT64 size);
   2886     BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const;
   2887     void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd);
   2888 
   2889 private:
   2890     UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0;
   2891 };
   2892 
   2893 #ifndef _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS
   2894 void ZeroInitializedRange::Reset(UINT64 size)
   2895 {
   2896     D3D12MA_ASSERT(size > 0);
   2897     m_ZeroBeg = 0;
   2898     m_ZeroEnd = size;
   2899 }
   2900 
   2901 BOOL ZeroInitializedRange::IsRangeZeroInitialized(UINT64 beg, UINT64 end) const
   2902 {
   2903     D3D12MA_ASSERT(beg < end);
   2904     return m_ZeroBeg <= beg && end <= m_ZeroEnd;
   2905 }
   2906 
   2907 void ZeroInitializedRange::MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd)
   2908 {
   2909     D3D12MA_ASSERT(usedBeg < usedEnd);
   2910     // No new bytes marked.
   2911     if (usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg)
   2912     {
   2913         return;
   2914     }
   2915     // All bytes marked.
   2916     if (usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd)
   2917     {
   2918         m_ZeroBeg = m_ZeroEnd = 0;
   2919     }
   2920     // Some bytes marked.
   2921     else
   2922     {
   2923         const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0;
   2924         const UINT64 remainingZeroAfter = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0;
   2925         D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0);
   2926         if (remainingZeroBefore > remainingZeroAfter)
   2927         {
   2928             m_ZeroEnd = usedBeg;
   2929         }
   2930         else
   2931         {
   2932             m_ZeroBeg = usedEnd;
   2933         }
   2934     }
   2935 }
   2936 #endif // _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS
   2937 #endif // _D3D12MA_ZERO_INITIALIZED_RANGE
   2938 
   2939 #ifndef _D3D12MA_BLOCK_METADATA
   2940 /*
   2941 Data structure used for bookkeeping of allocations and unused ranges of memory
   2942 in a single ID3D12Heap memory block.
   2943 */
   2944 class BlockMetadata
   2945 {
   2946 public:
   2947     BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
   2948     virtual ~BlockMetadata() = default;
   2949 
   2950     virtual void Init(UINT64 size) { m_Size = size; }
   2951     // Validates all data structures inside this object. If not valid, returns false.
   2952     virtual bool Validate() const = 0;
   2953     UINT64 GetSize() const { return m_Size; }
   2954     bool IsVirtual() const { return m_IsVirtual; }
   2955     virtual size_t GetAllocationCount() const = 0;
   2956     virtual size_t GetFreeRegionsCount() const = 0;
   2957     virtual UINT64 GetSumFreeSize() const = 0;
   2958     virtual UINT64 GetAllocationOffset(AllocHandle allocHandle) const = 0;
   2959     // Returns true if this block is empty - contains only single free suballocation.
   2960     virtual bool IsEmpty() const = 0;
   2961 
   2962     virtual void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;
   2963 
   2964     // Tries to find a place for suballocation with given parameters inside this block.
   2965     // If succeeded, fills pAllocationRequest and returns true.
   2966     // If failed, returns false.
   2967     virtual bool CreateAllocationRequest(
   2968         UINT64 allocSize,
   2969         UINT64 allocAlignment,
   2970         bool upperAddress,
   2971         UINT32 strategy,
   2972         AllocationRequest* pAllocationRequest) = 0;
   2973 
   2974     // Makes actual allocation based on request. Request must already be checked and valid.
   2975     virtual void Alloc(
   2976         const AllocationRequest& request,
   2977         UINT64 allocSize,
   2978         void* PrivateData) = 0;
   2979 
   2980     virtual void Free(AllocHandle allocHandle) = 0;
   2981     // Frees all allocations.
   2982     // Careful! Don't call it if there are Allocation objects owned by pPrivateData of of cleared allocations!
   2983     virtual void Clear() = 0;
   2984 
   2985     virtual AllocHandle GetAllocationListBegin() const = 0;
   2986     virtual AllocHandle GetNextAllocation(AllocHandle prevAlloc) const = 0;
   2987     virtual UINT64 GetNextFreeRegionSize(AllocHandle alloc) const = 0;
   2988     virtual void* GetAllocationPrivateData(AllocHandle allocHandle) const = 0;
   2989     virtual void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) = 0;
   2990 
   2991     virtual void AddStatistics(Statistics& inoutStats) const = 0;
   2992     virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0;
   2993     virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;
   2994     virtual void DebugLogAllAllocations() const = 0;
   2995 
   2996 protected:
   2997     const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; }
   2998     UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; }
   2999 
   3000     void DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const;
   3001     void PrintDetailedMap_Begin(JsonWriter& json,
   3002         UINT64 unusedBytes,
   3003         size_t allocationCount,
   3004         size_t unusedRangeCount) const;
   3005     void PrintDetailedMap_Allocation(JsonWriter& json,
   3006         UINT64 offset, UINT64 size, void* privateData) const;
   3007     void PrintDetailedMap_UnusedRange(JsonWriter& json,
   3008         UINT64 offset, UINT64 size) const;
   3009     void PrintDetailedMap_End(JsonWriter& json) const;
   3010 
   3011 private:
   3012     UINT64 m_Size;
   3013     bool m_IsVirtual;
   3014     const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;
   3015 
   3016     D3D12MA_CLASS_NO_COPY(BlockMetadata);
   3017 };
   3018 
   3019 #ifndef _D3D12MA_BLOCK_METADATA_FUNCTIONS
   3020 BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)
   3021     : m_Size(0),
   3022     m_IsVirtual(isVirtual),
   3023     m_pAllocationCallbacks(allocationCallbacks)
   3024 {
   3025     D3D12MA_ASSERT(allocationCallbacks);
   3026 }
   3027 
   3028 void BlockMetadata::DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const
   3029 {
   3030     if (IsVirtual())
   3031     {
   3032         D3D12MA_DEBUG_LOG(L"UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p", offset, size, privateData);
   3033     }
   3034     else
   3035     {
   3036         D3D12MA_ASSERT(privateData != NULL);
   3037         Allocation* allocation = reinterpret_cast<Allocation*>(privateData);
   3038 
   3039         privateData = allocation->GetPrivateData();
   3040         LPCWSTR name = allocation->GetName();
   3041 
   3042         D3D12MA_DEBUG_LOG(L"UNFREED ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p; Name: %s",
   3043             offset, size, privateData, name ? name : L"D3D12MA_Empty");
   3044     }
   3045 }
   3046 
   3047 void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json,
   3048     UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
   3049 {
   3050     json.WriteString(L"TotalBytes");
   3051     json.WriteNumber(GetSize());
   3052 
   3053     json.WriteString(L"UnusedBytes");
   3054     json.WriteNumber(unusedBytes);
   3055 
   3056     json.WriteString(L"Allocations");
   3057     json.WriteNumber((UINT64)allocationCount);
   3058 
   3059     json.WriteString(L"UnusedRanges");
   3060     json.WriteNumber((UINT64)unusedRangeCount);
   3061 
   3062     json.WriteString(L"Suballocations");
   3063     json.BeginArray();
   3064 }
   3065 
   3066 void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json,
   3067     UINT64 offset, UINT64 size, void* privateData) const
   3068 {
   3069     json.BeginObject(true);
   3070 
   3071     json.WriteString(L"Offset");
   3072     json.WriteNumber(offset);
   3073 
   3074     if (IsVirtual())
   3075     {
   3076         json.WriteString(L"Size");
   3077         json.WriteNumber(size);
   3078         if (privateData)
   3079         {
   3080             json.WriteString(L"CustomData");
   3081             json.WriteNumber((uintptr_t)privateData);
   3082         }
   3083     }
   3084     else
   3085     {
   3086         const Allocation* const alloc = (const Allocation*)privateData;
   3087         D3D12MA_ASSERT(alloc);
   3088         json.AddAllocationToObject(*alloc);
   3089     }
   3090     json.EndObject();
   3091 }
   3092 
   3093 void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json,
   3094     UINT64 offset, UINT64 size) const
   3095 {
   3096     json.BeginObject(true);
   3097 
   3098     json.WriteString(L"Offset");
   3099     json.WriteNumber(offset);
   3100 
   3101     json.WriteString(L"Type");
   3102     json.WriteString(L"FREE");
   3103 
   3104     json.WriteString(L"Size");
   3105     json.WriteNumber(size);
   3106 
   3107     json.EndObject();
   3108 }
   3109 
   3110 void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const
   3111 {
   3112     json.EndArray();
   3113 }
   3114 #endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS
   3115 #endif // _D3D12MA_BLOCK_METADATA
   3116 
   3117 #if 0
   3118 #ifndef _D3D12MA_BLOCK_METADATA_GENERIC
   3119 class BlockMetadata_Generic : public BlockMetadata
   3120 {
   3121 public:
   3122     BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
   3123     virtual ~BlockMetadata_Generic() = default;
   3124 
   3125     size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }
   3126     UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }
   3127     UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; }
   3128 
   3129     void Init(UINT64 size) override;
   3130     bool Validate() const override;
   3131     bool IsEmpty() const override;
   3132     void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;
   3133 
   3134     bool CreateAllocationRequest(
   3135         UINT64 allocSize,
   3136         UINT64 allocAlignment,
   3137         bool upperAddress,
   3138         AllocationRequest* pAllocationRequest) override;
   3139 
   3140     void Alloc(
   3141         const AllocationRequest& request,
   3142         UINT64 allocSize,
   3143         void* privateData) override;
   3144 
   3145     void Free(AllocHandle allocHandle) override;
   3146     void Clear() override;
   3147 
   3148     void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;
   3149 
   3150     void AddStatistics(Statistics& inoutStats) const override;
   3151     void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;
   3152     void WriteAllocationInfoToJson(JsonWriter& json) const override;
   3153 
   3154 private:
   3155     UINT m_FreeCount;
   3156     UINT64 m_SumFreeSize;
   3157     SuballocationList m_Suballocations;
   3158     // Suballocations that are free and have size greater than certain threshold.
   3159     // Sorted by size, ascending.
   3160     Vector<SuballocationList::iterator> m_FreeSuballocationsBySize;
   3161     ZeroInitializedRange m_ZeroInitializedRange;
   3162 
   3163     SuballocationList::const_iterator FindAtOffset(UINT64 offset) const;
   3164     bool ValidateFreeSuballocationList() const;
   3165 
   3166     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
   3167     // If yes, fills pOffset and returns true. If no, returns false.
   3168     bool CheckAllocation(
   3169         UINT64 allocSize,
   3170         UINT64 allocAlignment,
   3171         SuballocationList::const_iterator suballocItem,
   3172         AllocHandle* pAllocHandle,
   3173         UINT64* pSumFreeSize,
   3174         UINT64* pSumItemSize,
   3175         BOOL *pZeroInitialized) const;
   3176     // Given free suballocation, it merges it with following one, which must also be free.
   3177     void MergeFreeWithNext(SuballocationList::iterator item);
   3178     // Releases given suballocation, making it free.
   3179     // Merges it with adjacent free suballocations if applicable.
   3180     // Returns iterator to new free suballocation at this place.
   3181     SuballocationList::iterator FreeSuballocation(SuballocationList::iterator suballocItem);
   3182     // Given free suballocation, it inserts it into sorted list of
   3183     // m_FreeSuballocationsBySize if it's suitable.
   3184     void RegisterFreeSuballocation(SuballocationList::iterator item);
   3185     // Given free suballocation, it removes it from sorted list of
   3186     // m_FreeSuballocationsBySize if it's suitable.
   3187     void UnregisterFreeSuballocation(SuballocationList::iterator item);
   3188 
   3189     D3D12MA_CLASS_NO_COPY(BlockMetadata_Generic)
   3190 };
   3191 
   3192 #ifndef _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS
   3193 BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)
   3194     : BlockMetadata(allocationCallbacks, isVirtual),
   3195     m_FreeCount(0),
   3196     m_SumFreeSize(0),
   3197     m_Suballocations(*allocationCallbacks),
   3198     m_FreeSuballocationsBySize(*allocationCallbacks)
   3199 {
   3200     D3D12MA_ASSERT(allocationCallbacks);
   3201 }
   3202 
   3203 void BlockMetadata_Generic::Init(UINT64 size)
   3204 {
   3205     BlockMetadata::Init(size);
   3206     m_ZeroInitializedRange.Reset(size);
   3207 
   3208     m_FreeCount = 1;
   3209     m_SumFreeSize = size;
   3210 
   3211     Suballocation suballoc = {};
   3212     suballoc.offset = 0;
   3213     suballoc.size = size;
   3214     suballoc.type = SUBALLOCATION_TYPE_FREE;
   3215     suballoc.privateData = NULL;
   3216 
   3217     D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
   3218     m_Suballocations.push_back(suballoc);
   3219     SuballocationList::iterator suballocItem = m_Suballocations.end();
   3220     --suballocItem;
   3221     m_FreeSuballocationsBySize.push_back(suballocItem);
   3222 }
   3223 
   3224 bool BlockMetadata_Generic::Validate() const
   3225 {
   3226     D3D12MA_VALIDATE(!m_Suballocations.empty());
   3227 
   3228     // Expected offset of new suballocation as calculated from previous ones.
   3229     UINT64 calculatedOffset = 0;
   3230     // Expected number of free suballocations as calculated from traversing their list.
   3231     UINT calculatedFreeCount = 0;
   3232     // Expected sum size of free suballocations as calculated from traversing their list.
   3233     UINT64 calculatedSumFreeSize = 0;
   3234     // Expected number of free suballocations that should be registered in
   3235     // m_FreeSuballocationsBySize calculated from traversing their list.
   3236     size_t freeSuballocationsToRegister = 0;
   3237     // True if previous visited suballocation was free.
   3238     bool prevFree = false;
   3239 
   3240     for (const auto& subAlloc : m_Suballocations)
   3241     {
   3242         // Actual offset of this suballocation doesn't match expected one.
   3243         D3D12MA_VALIDATE(subAlloc.offset == calculatedOffset);
   3244 
   3245         const bool currFree = (subAlloc.type == SUBALLOCATION_TYPE_FREE);
   3246         // Two adjacent free suballocations are invalid. They should be merged.
   3247         D3D12MA_VALIDATE(!prevFree || !currFree);
   3248 
   3249         const Allocation* const alloc = (Allocation*)subAlloc.privateData;
   3250         if (!IsVirtual())
   3251         {
   3252             D3D12MA_VALIDATE(currFree == (alloc == NULL));
   3253         }
   3254 
   3255         if (currFree)
   3256         {
   3257             calculatedSumFreeSize += subAlloc.size;
   3258             ++calculatedFreeCount;
   3259             if (subAlloc.size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
   3260             {
   3261                 ++freeSuballocationsToRegister;
   3262             }
   3263 
   3264             // Margin required between allocations - every free space must be at least that large.
   3265             D3D12MA_VALIDATE(subAlloc.size >= GetDebugMargin());
   3266         }
   3267         else
   3268         {
   3269             if (!IsVirtual())
   3270             {
   3271                 D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset);
   3272                 D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size);
   3273             }
   3274 
   3275             // Margin required between allocations - previous allocation must be free.
   3276             D3D12MA_VALIDATE(GetDebugMargin() == 0 || prevFree);
   3277         }
   3278 
   3279         calculatedOffset += subAlloc.size;
   3280         prevFree = currFree;
   3281     }
   3282 
   3283     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
   3284     // match expected one.
   3285     D3D12MA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
   3286 
   3287     UINT64 lastSize = 0;
   3288     for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
   3289     {
   3290         SuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
   3291 
   3292         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
   3293         D3D12MA_VALIDATE(suballocItem->type == SUBALLOCATION_TYPE_FREE);
   3294         // They must be sorted by size ascending.
   3295         D3D12MA_VALIDATE(suballocItem->size >= lastSize);
   3296 
   3297         lastSize = suballocItem->size;
   3298     }
   3299 
   3300     // Check if totals match calculacted values.
   3301     D3D12MA_VALIDATE(ValidateFreeSuballocationList());
   3302     D3D12MA_VALIDATE(calculatedOffset == GetSize());
   3303     D3D12MA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
   3304     D3D12MA_VALIDATE(calculatedFreeCount == m_FreeCount);
   3305 
   3306     return true;
   3307 }
   3308 
   3309 bool BlockMetadata_Generic::IsEmpty() const
   3310 {
   3311     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
   3312 }
   3313 
   3314 void BlockMetadata_Generic::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const
   3315 {
   3316     Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();
   3317     outInfo.Offset = suballoc.offset;
   3318     outInfo.Size = suballoc.size;
   3319     outInfo.pPrivateData = suballoc.privateData;
   3320 }
   3321 
   3322 bool BlockMetadata_Generic::CreateAllocationRequest(
   3323     UINT64 allocSize,
   3324     UINT64 allocAlignment,
   3325     bool upperAddress,
   3326     AllocationRequest* pAllocationRequest)
   3327 {
   3328     D3D12MA_ASSERT(allocSize > 0);
   3329     D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");
   3330     D3D12MA_ASSERT(pAllocationRequest != NULL);
   3331     D3D12MA_HEAVY_ASSERT(Validate());
   3332 
   3333     // There is not enough total free space in this block to fullfill the request: Early return.
   3334     if (m_SumFreeSize < allocSize + GetDebugMargin())
   3335     {
   3336         return false;
   3337     }
   3338 
   3339     // New algorithm, efficiently searching freeSuballocationsBySize.
   3340     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
   3341     if (freeSuballocCount > 0)
   3342     {
   3343         // Find first free suballocation with size not less than allocSize + GetDebugMargin().
   3344         SuballocationList::iterator* const it = BinaryFindFirstNotLess(
   3345             m_FreeSuballocationsBySize.data(),
   3346             m_FreeSuballocationsBySize.data() + freeSuballocCount,
   3347             allocSize + GetDebugMargin(),
   3348             SuballocationItemSizeLess());
   3349         size_t index = it - m_FreeSuballocationsBySize.data();
   3350         for (; index < freeSuballocCount; ++index)
   3351         {
   3352             if (CheckAllocation(
   3353                 allocSize,
   3354                 allocAlignment,
   3355                 m_FreeSuballocationsBySize[index],
   3356                 &pAllocationRequest->allocHandle,
   3357                 &pAllocationRequest->sumFreeSize,
   3358                 &pAllocationRequest->sumItemSize,
   3359                 &pAllocationRequest->zeroInitialized))
   3360             {
   3361                 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
   3362                 return true;
   3363             }
   3364         }
   3365     }
   3366 
   3367     return false;
   3368 }
   3369 
   3370 void BlockMetadata_Generic::Alloc(
   3371     const AllocationRequest& request,
   3372     UINT64 allocSize,
   3373     void* privateData)
   3374 {
   3375     D3D12MA_ASSERT(request.item != m_Suballocations.end());
   3376     Suballocation& suballoc = *request.item;
   3377     // Given suballocation is a free block.
   3378     D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);
   3379     // Given offset is inside this suballocation.
   3380     UINT64 offset = (UINT64)request.allocHandle - 1;
   3381     D3D12MA_ASSERT(offset >= suballoc.offset);
   3382     const UINT64 paddingBegin = offset - suballoc.offset;
   3383     D3D12MA_ASSERT(suballoc.size >= paddingBegin + allocSize);
   3384     const UINT64 paddingEnd = suballoc.size - paddingBegin - allocSize;
   3385 
   3386     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
   3387     // it to become used.
   3388     UnregisterFreeSuballocation(request.item);
   3389 
   3390     suballoc.offset = offset;
   3391     suballoc.size = allocSize;
   3392     suballoc.type = SUBALLOCATION_TYPE_ALLOCATION;
   3393     suballoc.privateData = privateData;
   3394 
   3395     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
   3396     if (paddingEnd)
   3397     {
   3398         Suballocation paddingSuballoc = {};
   3399         paddingSuballoc.offset = offset + allocSize;
   3400         paddingSuballoc.size = paddingEnd;
   3401         paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;
   3402         SuballocationList::iterator next = request.item;
   3403         ++next;
   3404         const SuballocationList::iterator paddingEndItem =
   3405             m_Suballocations.insert(next, paddingSuballoc);
   3406         RegisterFreeSuballocation(paddingEndItem);
   3407     }
   3408 
   3409     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
   3410     if (paddingBegin)
   3411     {
   3412         Suballocation paddingSuballoc = {};
   3413         paddingSuballoc.offset = offset - paddingBegin;
   3414         paddingSuballoc.size = paddingBegin;
   3415         paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;
   3416         const SuballocationList::iterator paddingBeginItem =
   3417             m_Suballocations.insert(request.item, paddingSuballoc);
   3418         RegisterFreeSuballocation(paddingBeginItem);
   3419     }
   3420 
   3421     // Update totals.
   3422     m_FreeCount = m_FreeCount - 1;
   3423     if (paddingBegin > 0)
   3424     {
   3425         ++m_FreeCount;
   3426     }
   3427     if (paddingEnd > 0)
   3428     {
   3429         ++m_FreeCount;
   3430     }
   3431     m_SumFreeSize -= allocSize;
   3432 
   3433     m_ZeroInitializedRange.MarkRangeAsUsed(offset, offset + allocSize);
   3434 }
   3435 
   3436 void BlockMetadata_Generic::Free(AllocHandle allocHandle)
   3437 {
   3438     FreeSuballocation(FindAtOffset((UINT64)allocHandle - 1).dropConst());
   3439 }
   3440 
   3441 void BlockMetadata_Generic::Clear()
   3442 {
   3443     m_FreeCount = 1;
   3444     m_SumFreeSize = GetSize();
   3445 
   3446     m_Suballocations.clear();
   3447     Suballocation suballoc = {};
   3448     suballoc.offset = 0;
   3449     suballoc.size = GetSize();
   3450     suballoc.type = SUBALLOCATION_TYPE_FREE;
   3451     m_Suballocations.push_back(suballoc);
   3452 
   3453     m_FreeSuballocationsBySize.clear();
   3454     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
   3455 }
   3456 
   3457 SuballocationList::const_iterator BlockMetadata_Generic::FindAtOffset(UINT64 offset) const
   3458 {
   3459     const UINT64 last = m_Suballocations.crbegin()->offset;
   3460     if (last == offset)
   3461         return m_Suballocations.crbegin();
   3462     const UINT64 first = m_Suballocations.cbegin()->offset;
   3463     if (first == offset)
   3464         return m_Suballocations.cbegin();
   3465 
   3466     const size_t suballocCount = m_Suballocations.size();
   3467     const UINT64 step = (last - first + m_Suballocations.cbegin()->size) / suballocCount;
   3468     auto findSuballocation = [&](auto begin, auto end) -> SuballocationList::const_iterator
   3469     {
   3470         for (auto suballocItem = begin;
   3471             suballocItem != end;
   3472             ++suballocItem)
   3473         {
   3474             const Suballocation& suballoc = *suballocItem;
   3475             if (suballoc.offset == offset)
   3476                 return suballocItem;
   3477         }
   3478         D3D12MA_ASSERT(false && "Not found!");
   3479         return m_Suballocations.end();
   3480     };
   3481     // If requested offset is closer to the end of range, search from the end
   3482     if ((offset - first) > suballocCount * step / 2)
   3483     {
   3484         return findSuballocation(m_Suballocations.crbegin(), m_Suballocations.crend());
   3485     }
   3486     return findSuballocation(m_Suballocations.cbegin(), m_Suballocations.cend());
   3487 }
   3488 
   3489 bool BlockMetadata_Generic::ValidateFreeSuballocationList() const
   3490 {
   3491     UINT64 lastSize = 0;
   3492     for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
   3493     {
   3494         const SuballocationList::iterator it = m_FreeSuballocationsBySize[i];
   3495 
   3496         D3D12MA_VALIDATE(it->type == SUBALLOCATION_TYPE_FREE);
   3497         D3D12MA_VALIDATE(it->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
   3498         D3D12MA_VALIDATE(it->size >= lastSize);
   3499         lastSize = it->size;
   3500     }
   3501     return true;
   3502 }
   3503 
   3504 bool BlockMetadata_Generic::CheckAllocation(
   3505     UINT64 allocSize,
   3506     UINT64 allocAlignment,
   3507     SuballocationList::const_iterator suballocItem,
   3508     AllocHandle* pAllocHandle,
   3509     UINT64* pSumFreeSize,
   3510     UINT64* pSumItemSize,
   3511     BOOL* pZeroInitialized) const
   3512 {
   3513     D3D12MA_ASSERT(allocSize > 0);
   3514     D3D12MA_ASSERT(suballocItem != m_Suballocations.cend());
   3515     D3D12MA_ASSERT(pAllocHandle != NULL && pZeroInitialized != NULL);
   3516 
   3517     *pSumFreeSize = 0;
   3518     *pSumItemSize = 0;
   3519     *pZeroInitialized = FALSE;
   3520 
   3521     const Suballocation& suballoc = *suballocItem;
   3522     D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);
   3523 
   3524     *pSumFreeSize = suballoc.size;
   3525 
   3526     // Size of this suballocation is too small for this request: Early return.
   3527     if (suballoc.size < allocSize)
   3528     {
   3529         return false;
   3530     }
   3531 
   3532     // Start from offset equal to beginning of this suballocation and debug margin of previous allocation if present.
   3533     UINT64 offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());
   3534 
   3535     // Apply alignment.
   3536     offset = AlignUp(offset, allocAlignment);
   3537 
   3538     // Calculate padding at the beginning based on current offset.
   3539     const UINT64 paddingBegin = offset - suballoc.offset;
   3540 
   3541     // Fail if requested size plus margin after is bigger than size of this suballocation.
   3542     if (paddingBegin + allocSize + GetDebugMargin() > suballoc.size)
   3543     {
   3544         return false;
   3545     }
   3546 
   3547     // All tests passed: Success. Offset is already filled.
   3548     *pZeroInitialized = m_ZeroInitializedRange.IsRangeZeroInitialized(offset, offset + allocSize);
   3549     *pAllocHandle = (AllocHandle)(offset + 1);
   3550     return true;
   3551 }
   3552 
   3553 void BlockMetadata_Generic::MergeFreeWithNext(SuballocationList::iterator item)
   3554 {
   3555     D3D12MA_ASSERT(item != m_Suballocations.end());
   3556     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
   3557 
   3558     SuballocationList::iterator nextItem = item;
   3559     ++nextItem;
   3560     D3D12MA_ASSERT(nextItem != m_Suballocations.end());
   3561     D3D12MA_ASSERT(nextItem->type == SUBALLOCATION_TYPE_FREE);
   3562 
   3563     item->size += nextItem->size;
   3564     --m_FreeCount;
   3565     m_Suballocations.erase(nextItem);
   3566 }
   3567 
   3568 SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(SuballocationList::iterator suballocItem)
   3569 {
   3570     // Change this suballocation to be marked as free.
   3571     Suballocation& suballoc = *suballocItem;
   3572     suballoc.type = SUBALLOCATION_TYPE_FREE;
   3573     suballoc.privateData = NULL;
   3574 
   3575     // Update totals.
   3576     ++m_FreeCount;
   3577     m_SumFreeSize += suballoc.size;
   3578 
   3579     // Merge with previous and/or next suballocation if it's also free.
   3580     bool mergeWithNext = false;
   3581     bool mergeWithPrev = false;
   3582 
   3583     SuballocationList::iterator nextItem = suballocItem;
   3584     ++nextItem;
   3585     if ((nextItem != m_Suballocations.end()) && (nextItem->type == SUBALLOCATION_TYPE_FREE))
   3586     {
   3587         mergeWithNext = true;
   3588     }
   3589 
   3590     SuballocationList::iterator prevItem = suballocItem;
   3591     if (suballocItem != m_Suballocations.begin())
   3592     {
   3593         --prevItem;
   3594         if (prevItem->type == SUBALLOCATION_TYPE_FREE)
   3595         {
   3596             mergeWithPrev = true;
   3597         }
   3598     }
   3599 
   3600     if (mergeWithNext)
   3601     {
   3602         UnregisterFreeSuballocation(nextItem);
   3603         MergeFreeWithNext(suballocItem);
   3604     }
   3605 
   3606     if (mergeWithPrev)
   3607     {
   3608         UnregisterFreeSuballocation(prevItem);
   3609         MergeFreeWithNext(prevItem);
   3610         RegisterFreeSuballocation(prevItem);
   3611         return prevItem;
   3612     }
   3613     else
   3614     {
   3615         RegisterFreeSuballocation(suballocItem);
   3616         return suballocItem;
   3617     }
   3618 }
   3619 
   3620 void BlockMetadata_Generic::RegisterFreeSuballocation(SuballocationList::iterator item)
   3621 {
   3622     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
   3623     D3D12MA_ASSERT(item->size > 0);
   3624 
   3625     // You may want to enable this validation at the beginning or at the end of
   3626     // this function, depending on what do you want to check.
   3627     D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
   3628 
   3629     if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
   3630     {
   3631         if (m_FreeSuballocationsBySize.empty())
   3632         {
   3633             m_FreeSuballocationsBySize.push_back(item);
   3634         }
   3635         else
   3636         {
   3637             m_FreeSuballocationsBySize.InsertSorted(item, SuballocationItemSizeLess());
   3638         }
   3639     }
   3640 
   3641     //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
   3642 }
   3643 
   3644 void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::iterator item)
   3645 {
   3646     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
   3647     D3D12MA_ASSERT(item->size > 0);
   3648 
   3649     // You may want to enable this validation at the beginning or at the end of
   3650     // this function, depending on what do you want to check.
   3651     D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
   3652 
   3653     if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
   3654     {
   3655         SuballocationList::iterator* const it = BinaryFindFirstNotLess(
   3656             m_FreeSuballocationsBySize.data(),
   3657             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
   3658             item,
   3659             SuballocationItemSizeLess());
   3660         for (size_t index = it - m_FreeSuballocationsBySize.data();
   3661             index < m_FreeSuballocationsBySize.size();
   3662             ++index)
   3663         {
   3664             if (m_FreeSuballocationsBySize[index] == item)
   3665             {
   3666                 m_FreeSuballocationsBySize.remove(index);
   3667                 return;
   3668             }
   3669             D3D12MA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
   3670         }
   3671         D3D12MA_ASSERT(0 && "Not found.");
   3672     }
   3673 
   3674     //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
   3675 }
   3676 
   3677 void BlockMetadata_Generic::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)
   3678 {
   3679     Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();
   3680     suballoc.privateData = privateData;
   3681 }
   3682 
   3683 void BlockMetadata_Generic::AddStatistics(Statistics& inoutStats) const
   3684 {
   3685     inoutStats.BlockCount++;
   3686     inoutStats.AllocationCount += (UINT)m_Suballocations.size() - m_FreeCount;
   3687     inoutStats.BlockBytes += GetSize();
   3688     inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;
   3689 }
   3690 
   3691 void BlockMetadata_Generic::AddDetailedStatistics(DetailedStatistics& inoutStats) const
   3692 {
   3693     inoutStats.Stats.BlockCount++;
   3694     inoutStats.Stats.BlockBytes += GetSize();
   3695 
   3696     for (const auto& suballoc : m_Suballocations)
   3697     {
   3698         if (suballoc.type == SUBALLOCATION_TYPE_FREE)
   3699             AddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);
   3700         else
   3701             AddDetailedStatisticsAllocation(inoutStats, suballoc.size);
   3702     }
   3703 }
   3704 
   3705 void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const
   3706 {
   3707     PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_FreeCount);
   3708     for (const auto& suballoc : m_Suballocations)
   3709     {
   3710         if (suballoc.type == SUBALLOCATION_TYPE_FREE)
   3711             PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
   3712         else
   3713             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);
   3714     }
   3715     PrintDetailedMap_End(json);
   3716 }
   3717 #endif // _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS
   3718 #endif // _D3D12MA_BLOCK_METADATA_GENERIC
   3719 #endif // #if 0
   3720 
   3721 #ifndef _D3D12MA_BLOCK_METADATA_LINEAR
   3722 class BlockMetadata_Linear : public BlockMetadata
   3723 {
   3724 public:
   3725     BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
   3726     virtual ~BlockMetadata_Linear() = default;
   3727 
   3728     UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }
   3729     bool IsEmpty() const override { return GetAllocationCount() == 0; }
   3730     UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; };
   3731 
   3732     void Init(UINT64 size) override;
   3733     bool Validate() const override;
   3734     size_t GetAllocationCount() const override;
   3735     size_t GetFreeRegionsCount() const override;
   3736     void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;
   3737 
   3738     bool CreateAllocationRequest(
   3739         UINT64 allocSize,
   3740         UINT64 allocAlignment,
   3741         bool upperAddress,
   3742         UINT32 strategy,
   3743         AllocationRequest* pAllocationRequest) override;
   3744 
   3745     void Alloc(
   3746         const AllocationRequest& request,
   3747         UINT64 allocSize,
   3748         void* privateData) override;
   3749 
   3750     void Free(AllocHandle allocHandle) override;
   3751     void Clear() override;
   3752 
   3753     AllocHandle GetAllocationListBegin() const override;
   3754     AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;
   3755     UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;
   3756     void* GetAllocationPrivateData(AllocHandle allocHandle) const override;
   3757     void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;
   3758 
   3759     void AddStatistics(Statistics& inoutStats) const override;
   3760     void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;
   3761     void WriteAllocationInfoToJson(JsonWriter& json) const override;
   3762     void DebugLogAllAllocations() const override;
   3763 
   3764 private:
   3765     /*
   3766     There are two suballocation vectors, used in ping-pong way.
   3767     The one with index m_1stVectorIndex is called 1st.
   3768     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
   3769     2nd can be non-empty only when 1st is not empty.
   3770     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
   3771     */
   3772     typedef Vector<Suballocation> SuballocationVectorType;
   3773 
   3774     enum ALLOC_REQUEST_TYPE
   3775     {
   3776         ALLOC_REQUEST_UPPER_ADDRESS,
   3777         ALLOC_REQUEST_END_OF_1ST,
   3778         ALLOC_REQUEST_END_OF_2ND,
   3779     };
   3780 
   3781     enum SECOND_VECTOR_MODE
   3782     {
   3783         SECOND_VECTOR_EMPTY,
   3784         /*
   3785         Suballocations in 2nd vector are created later than the ones in 1st, but they
   3786         all have smaller offset.
   3787         */
   3788         SECOND_VECTOR_RING_BUFFER,
   3789         /*
   3790         Suballocations in 2nd vector are upper side of double stack.
   3791         They all have offsets higher than those in 1st vector.
   3792         Top of this stack means smaller offsets, but higher indices in this vector.
   3793         */
   3794         SECOND_VECTOR_DOUBLE_STACK,
   3795     };
   3796 
   3797     UINT64 m_SumFreeSize;
   3798     SuballocationVectorType m_Suballocations0, m_Suballocations1;
   3799     UINT32 m_1stVectorIndex;
   3800     SECOND_VECTOR_MODE m_2ndVectorMode;
   3801     // Number of items in 1st vector with hAllocation = null at the beginning.
   3802     size_t m_1stNullItemsBeginCount;
   3803     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
   3804     size_t m_1stNullItemsMiddleCount;
   3805     // Number of items in 2nd vector with hAllocation = null.
   3806     size_t m_2ndNullItemsCount;
   3807 
   3808     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
   3809     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
   3810     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
   3811     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
   3812 
   3813     Suballocation& FindSuballocation(UINT64 offset) const;
   3814     bool ShouldCompact1st() const;
   3815     void CleanupAfterFree();
   3816 
   3817     bool CreateAllocationRequest_LowerAddress(
   3818         UINT64 allocSize,
   3819         UINT64 allocAlignment,
   3820         AllocationRequest* pAllocationRequest);
   3821     bool CreateAllocationRequest_UpperAddress(
   3822         UINT64 allocSize,
   3823         UINT64 allocAlignment,
   3824         AllocationRequest* pAllocationRequest);
   3825 
   3826     D3D12MA_CLASS_NO_COPY(BlockMetadata_Linear)
   3827 };
   3828 
   3829 #ifndef _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS
   3830 BlockMetadata_Linear::BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)
   3831     : BlockMetadata(allocationCallbacks, isVirtual),
   3832     m_SumFreeSize(0),
   3833     m_Suballocations0(*allocationCallbacks),
   3834     m_Suballocations1(*allocationCallbacks),
   3835     m_1stVectorIndex(0),
   3836     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
   3837     m_1stNullItemsBeginCount(0),
   3838     m_1stNullItemsMiddleCount(0),
   3839     m_2ndNullItemsCount(0)
   3840 {
   3841     D3D12MA_ASSERT(allocationCallbacks);
   3842 }
   3843 
   3844 void BlockMetadata_Linear::Init(UINT64 size)
   3845 {
   3846     BlockMetadata::Init(size);
   3847     m_SumFreeSize = size;
   3848 }
   3849 
   3850 bool BlockMetadata_Linear::Validate() const
   3851 {
   3852     D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());
   3853     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   3854     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   3855 
   3856     D3D12MA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
   3857     D3D12MA_VALIDATE(!suballocations1st.empty() ||
   3858         suballocations2nd.empty() ||
   3859         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
   3860 
   3861     if (!suballocations1st.empty())
   3862     {
   3863         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
   3864         D3D12MA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != SUBALLOCATION_TYPE_FREE);
   3865         // Null item at the end should be just pop_back().
   3866         D3D12MA_VALIDATE(suballocations1st.back().type != SUBALLOCATION_TYPE_FREE);
   3867     }
   3868     if (!suballocations2nd.empty())
   3869     {
   3870         // Null item at the end should be just pop_back().
   3871         D3D12MA_VALIDATE(suballocations2nd.back().type != SUBALLOCATION_TYPE_FREE);
   3872     }
   3873 
   3874     D3D12MA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
   3875     D3D12MA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
   3876 
   3877     UINT64 sumUsedSize = 0;
   3878     const size_t suballoc1stCount = suballocations1st.size();
   3879     UINT64 offset = 0;
   3880 
   3881     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   3882     {
   3883         const size_t suballoc2ndCount = suballocations2nd.size();
   3884         size_t nullItem2ndCount = 0;
   3885         for (size_t i = 0; i < suballoc2ndCount; ++i)
   3886         {
   3887             const Suballocation& suballoc = suballocations2nd[i];
   3888             const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);
   3889 
   3890             const Allocation* alloc = (Allocation*)suballoc.privateData;
   3891             if (!IsVirtual())
   3892             {
   3893                 D3D12MA_VALIDATE(currFree == (alloc == NULL));
   3894             }
   3895             D3D12MA_VALIDATE(suballoc.offset >= offset);
   3896 
   3897             if (!currFree)
   3898             {
   3899                 if (!IsVirtual())
   3900                 {
   3901                     D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);
   3902                     D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);
   3903                 }
   3904                 sumUsedSize += suballoc.size;
   3905             }
   3906             else
   3907             {
   3908                 ++nullItem2ndCount;
   3909             }
   3910 
   3911             offset = suballoc.offset + suballoc.size + GetDebugMargin();
   3912         }
   3913 
   3914         D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
   3915     }
   3916 
   3917     for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
   3918     {
   3919         const Suballocation& suballoc = suballocations1st[i];
   3920         D3D12MA_VALIDATE(suballoc.type == SUBALLOCATION_TYPE_FREE &&
   3921             suballoc.privateData == NULL);
   3922     }
   3923 
   3924     size_t nullItem1stCount = m_1stNullItemsBeginCount;
   3925 
   3926     for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
   3927     {
   3928         const Suballocation& suballoc = suballocations1st[i];
   3929         const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);
   3930 
   3931         const Allocation* alloc = (Allocation*)suballoc.privateData;
   3932         if (!IsVirtual())
   3933         {
   3934             D3D12MA_VALIDATE(currFree == (alloc == NULL));
   3935         }
   3936         D3D12MA_VALIDATE(suballoc.offset >= offset);
   3937         D3D12MA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
   3938 
   3939         if (!currFree)
   3940         {
   3941             if (!IsVirtual())
   3942             {
   3943                 D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);
   3944                 D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);
   3945             }
   3946             sumUsedSize += suballoc.size;
   3947         }
   3948         else
   3949         {
   3950             ++nullItem1stCount;
   3951         }
   3952 
   3953         offset = suballoc.offset + suballoc.size + GetDebugMargin();
   3954     }
   3955     D3D12MA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
   3956 
   3957     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   3958     {
   3959         const size_t suballoc2ndCount = suballocations2nd.size();
   3960         size_t nullItem2ndCount = 0;
   3961         for (size_t i = suballoc2ndCount; i--; )
   3962         {
   3963             const Suballocation& suballoc = suballocations2nd[i];
   3964             const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);
   3965 
   3966             const Allocation* alloc = (Allocation*)suballoc.privateData;
   3967             if (!IsVirtual())
   3968             {
   3969                 D3D12MA_VALIDATE(currFree == (alloc == NULL));
   3970             }
   3971             D3D12MA_VALIDATE(suballoc.offset >= offset);
   3972 
   3973             if (!currFree)
   3974             {
   3975                 if (!IsVirtual())
   3976                 {
   3977                     D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);
   3978                     D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);
   3979                 }
   3980                 sumUsedSize += suballoc.size;
   3981             }
   3982             else
   3983             {
   3984                 ++nullItem2ndCount;
   3985             }
   3986 
   3987             offset = suballoc.offset + suballoc.size + GetDebugMargin();
   3988         }
   3989 
   3990         D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
   3991     }
   3992 
   3993     D3D12MA_VALIDATE(offset <= GetSize());
   3994     D3D12MA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
   3995 
   3996     return true;
   3997 }
   3998 
   3999 size_t BlockMetadata_Linear::GetAllocationCount() const
   4000 {
   4001     return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
   4002         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
   4003 }
   4004 
   4005 size_t BlockMetadata_Linear::GetFreeRegionsCount() const
   4006 {
   4007     // Function only used for defragmentation, which is disabled for this algorithm
   4008     D3D12MA_ASSERT(0);
   4009     return SIZE_MAX;
   4010 }
   4011 
   4012 void BlockMetadata_Linear::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const
   4013 {
   4014     const Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);
   4015     outInfo.Offset = suballoc.offset;
   4016     outInfo.Size = suballoc.size;
   4017     outInfo.pPrivateData = suballoc.privateData;
   4018 }
   4019 
   4020 bool BlockMetadata_Linear::CreateAllocationRequest(
   4021     UINT64 allocSize,
   4022     UINT64 allocAlignment,
   4023     bool upperAddress,
   4024     UINT32 strategy,
   4025     AllocationRequest* pAllocationRequest)
   4026 {
   4027     D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
   4028     D3D12MA_ASSERT(pAllocationRequest != NULL);
   4029     D3D12MA_HEAVY_ASSERT(Validate());
   4030     pAllocationRequest->size = allocSize;
   4031     return upperAddress ?
   4032         CreateAllocationRequest_UpperAddress(
   4033             allocSize, allocAlignment, pAllocationRequest) :
   4034         CreateAllocationRequest_LowerAddress(
   4035             allocSize, allocAlignment, pAllocationRequest);
   4036 }
   4037 
   4038 void BlockMetadata_Linear::Alloc(
   4039     const AllocationRequest& request,
   4040     UINT64 allocSize,
   4041     void* privateData)
   4042 {
   4043     UINT64 offset = (UINT64)request.allocHandle - 1;
   4044     const Suballocation newSuballoc = { offset, request.size, privateData, SUBALLOCATION_TYPE_ALLOCATION };
   4045 
   4046     switch (request.algorithmData)
   4047     {
   4048     case ALLOC_REQUEST_UPPER_ADDRESS:
   4049     {
   4050         D3D12MA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
   4051             "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
   4052         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4053         suballocations2nd.push_back(newSuballoc);
   4054         m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
   4055         break;
   4056     }
   4057     case ALLOC_REQUEST_END_OF_1ST:
   4058     {
   4059         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4060 
   4061         D3D12MA_ASSERT(suballocations1st.empty() ||
   4062             offset >= suballocations1st.back().offset + suballocations1st.back().size);
   4063         // Check if it fits before the end of the block.
   4064         D3D12MA_ASSERT(offset + request.size <= GetSize());
   4065 
   4066         suballocations1st.push_back(newSuballoc);
   4067         break;
   4068     }
   4069     case ALLOC_REQUEST_END_OF_2ND:
   4070     {
   4071         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4072         // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
   4073         D3D12MA_ASSERT(!suballocations1st.empty() &&
   4074             offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
   4075         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4076 
   4077         switch (m_2ndVectorMode)
   4078         {
   4079         case SECOND_VECTOR_EMPTY:
   4080             // First allocation from second part ring buffer.
   4081             D3D12MA_ASSERT(suballocations2nd.empty());
   4082             m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
   4083             break;
   4084         case SECOND_VECTOR_RING_BUFFER:
   4085             // 2-part ring buffer is already started.
   4086             D3D12MA_ASSERT(!suballocations2nd.empty());
   4087             break;
   4088         case SECOND_VECTOR_DOUBLE_STACK:
   4089             D3D12MA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
   4090             break;
   4091         default:
   4092             D3D12MA_ASSERT(0);
   4093         }
   4094 
   4095         suballocations2nd.push_back(newSuballoc);
   4096         break;
   4097     }
   4098     default:
   4099         D3D12MA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
   4100     }
   4101     m_SumFreeSize -= newSuballoc.size;
   4102 }
   4103 
   4104 void BlockMetadata_Linear::Free(AllocHandle allocHandle)
   4105 {
   4106     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4107     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4108     UINT64 offset = (UINT64)allocHandle - 1;
   4109 
   4110     if (!suballocations1st.empty())
   4111     {
   4112         // First allocation: Mark it as next empty at the beginning.
   4113         Suballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
   4114         if (firstSuballoc.offset == offset)
   4115         {
   4116             firstSuballoc.type = SUBALLOCATION_TYPE_FREE;
   4117             firstSuballoc.privateData = NULL;
   4118             m_SumFreeSize += firstSuballoc.size;
   4119             ++m_1stNullItemsBeginCount;
   4120             CleanupAfterFree();
   4121             return;
   4122         }
   4123     }
   4124 
   4125     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
   4126     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
   4127         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   4128     {
   4129         Suballocation& lastSuballoc = suballocations2nd.back();
   4130         if (lastSuballoc.offset == offset)
   4131         {
   4132             m_SumFreeSize += lastSuballoc.size;
   4133             suballocations2nd.pop_back();
   4134             CleanupAfterFree();
   4135             return;
   4136         }
   4137     }
   4138     // Last allocation in 1st vector.
   4139     else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
   4140     {
   4141         Suballocation& lastSuballoc = suballocations1st.back();
   4142         if (lastSuballoc.offset == offset)
   4143         {
   4144             m_SumFreeSize += lastSuballoc.size;
   4145             suballocations1st.pop_back();
   4146             CleanupAfterFree();
   4147             return;
   4148         }
   4149     }
   4150 
   4151     Suballocation refSuballoc;
   4152     refSuballoc.offset = offset;
   4153     // Rest of members stays uninitialized intentionally for better performance.
   4154 
   4155     // Item from the middle of 1st vector.
   4156     {
   4157         const SuballocationVectorType::iterator it = BinaryFindSorted(
   4158             suballocations1st.begin() + m_1stNullItemsBeginCount,
   4159             suballocations1st.end(),
   4160             refSuballoc,
   4161             SuballocationOffsetLess());
   4162         if (it != suballocations1st.end())
   4163         {
   4164             it->type = SUBALLOCATION_TYPE_FREE;
   4165             it->privateData = NULL;
   4166             ++m_1stNullItemsMiddleCount;
   4167             m_SumFreeSize += it->size;
   4168             CleanupAfterFree();
   4169             return;
   4170         }
   4171     }
   4172 
   4173     if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
   4174     {
   4175         // Item from the middle of 2nd vector.
   4176         const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
   4177             BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :
   4178             BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());
   4179         if (it != suballocations2nd.end())
   4180         {
   4181             it->type = SUBALLOCATION_TYPE_FREE;
   4182             it->privateData = NULL;
   4183             ++m_2ndNullItemsCount;
   4184             m_SumFreeSize += it->size;
   4185             CleanupAfterFree();
   4186             return;
   4187         }
   4188     }
   4189 
   4190     D3D12MA_ASSERT(0 && "Allocation to free not found in linear allocator!");
   4191 }
   4192 
   4193 void BlockMetadata_Linear::Clear()
   4194 {
   4195     m_SumFreeSize = GetSize();
   4196     m_Suballocations0.clear();
   4197     m_Suballocations1.clear();
   4198     // Leaving m_1stVectorIndex unchanged - it doesn't matter.
   4199     m_2ndVectorMode = SECOND_VECTOR_EMPTY;
   4200     m_1stNullItemsBeginCount = 0;
   4201     m_1stNullItemsMiddleCount = 0;
   4202     m_2ndNullItemsCount = 0;
   4203 }
   4204 
   4205 AllocHandle BlockMetadata_Linear::GetAllocationListBegin() const
   4206 {
   4207     // Function only used for defragmentation, which is disabled for this algorithm
   4208     D3D12MA_ASSERT(0);
   4209     return (AllocHandle)0;
   4210 }
   4211 
   4212 AllocHandle BlockMetadata_Linear::GetNextAllocation(AllocHandle prevAlloc) const
   4213 {
   4214     // Function only used for defragmentation, which is disabled for this algorithm
   4215     D3D12MA_ASSERT(0);
   4216     return (AllocHandle)0;
   4217 }
   4218 
   4219 UINT64 BlockMetadata_Linear::GetNextFreeRegionSize(AllocHandle alloc) const
   4220 {
   4221     // Function only used for defragmentation, which is disabled for this algorithm
   4222     D3D12MA_ASSERT(0);
   4223     return 0;
   4224 }
   4225 
   4226 void* BlockMetadata_Linear::GetAllocationPrivateData(AllocHandle allocHandle) const
   4227 {
   4228     return FindSuballocation((UINT64)allocHandle - 1).privateData;
   4229 }
   4230 
   4231 void BlockMetadata_Linear::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)
   4232 {
   4233     Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);
   4234     suballoc.privateData = privateData;
   4235 }
   4236 
   4237 void BlockMetadata_Linear::AddStatistics(Statistics& inoutStats) const
   4238 {
   4239     inoutStats.BlockCount++;
   4240     inoutStats.AllocationCount += (UINT)GetAllocationCount();
   4241     inoutStats.BlockBytes += GetSize();
   4242     inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;
   4243 }
   4244 
   4245 void BlockMetadata_Linear::AddDetailedStatistics(DetailedStatistics& inoutStats) const
   4246 {
   4247     inoutStats.Stats.BlockCount++;
   4248     inoutStats.Stats.BlockBytes += GetSize();
   4249 
   4250     const UINT64 size = GetSize();
   4251     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4252     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4253     const size_t suballoc1stCount = suballocations1st.size();
   4254     const size_t suballoc2ndCount = suballocations2nd.size();
   4255 
   4256     UINT64 lastOffset = 0;
   4257     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4258     {
   4259         const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
   4260         size_t nextAlloc2ndIndex = 0;
   4261         while (lastOffset < freeSpace2ndTo1stEnd)
   4262         {
   4263             // Find next non-null allocation or move nextAllocIndex to the end.
   4264             while (nextAlloc2ndIndex < suballoc2ndCount &&
   4265                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4266             {
   4267                 ++nextAlloc2ndIndex;
   4268             }
   4269 
   4270             // Found non-null allocation.
   4271             if (nextAlloc2ndIndex < suballoc2ndCount)
   4272             {
   4273                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4274 
   4275                 // 1. Process free space before this allocation.
   4276                 if (lastOffset < suballoc.offset)
   4277                 {
   4278                     // There is free space from lastOffset to suballoc.offset.
   4279                     const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4280                     AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4281                 }
   4282 
   4283                 // 2. Process this allocation.
   4284                 // There is allocation with suballoc.offset, suballoc.size.
   4285                 AddDetailedStatisticsAllocation(inoutStats, suballoc.size);
   4286 
   4287                 // 3. Prepare for next iteration.
   4288                 lastOffset = suballoc.offset + suballoc.size;
   4289                 ++nextAlloc2ndIndex;
   4290             }
   4291             // We are at the end.
   4292             else
   4293             {
   4294                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
   4295                 if (lastOffset < freeSpace2ndTo1stEnd)
   4296                 {
   4297                     const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
   4298                     AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4299                 }
   4300 
   4301                 // End of loop.
   4302                 lastOffset = freeSpace2ndTo1stEnd;
   4303             }
   4304         }
   4305     }
   4306 
   4307     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
   4308     const UINT64 freeSpace1stTo2ndEnd =
   4309         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
   4310     while (lastOffset < freeSpace1stTo2ndEnd)
   4311     {
   4312         // Find next non-null allocation or move nextAllocIndex to the end.
   4313         while (nextAlloc1stIndex < suballoc1stCount &&
   4314             suballocations1st[nextAlloc1stIndex].privateData == NULL)
   4315         {
   4316             ++nextAlloc1stIndex;
   4317         }
   4318 
   4319         // Found non-null allocation.
   4320         if (nextAlloc1stIndex < suballoc1stCount)
   4321         {
   4322             const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];
   4323 
   4324             // 1. Process free space before this allocation.
   4325             if (lastOffset < suballoc.offset)
   4326             {
   4327                 // There is free space from lastOffset to suballoc.offset.
   4328                 const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4329                 AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4330             }
   4331 
   4332             // 2. Process this allocation.
   4333             // There is allocation with suballoc.offset, suballoc.size.
   4334             AddDetailedStatisticsAllocation(inoutStats, suballoc.size);
   4335 
   4336             // 3. Prepare for next iteration.
   4337             lastOffset = suballoc.offset + suballoc.size;
   4338             ++nextAlloc1stIndex;
   4339         }
   4340         // We are at the end.
   4341         else
   4342         {
   4343             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
   4344             if (lastOffset < freeSpace1stTo2ndEnd)
   4345             {
   4346                 const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
   4347                 AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4348             }
   4349 
   4350             // End of loop.
   4351             lastOffset = freeSpace1stTo2ndEnd;
   4352         }
   4353     }
   4354 
   4355     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   4356     {
   4357         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
   4358         while (lastOffset < size)
   4359         {
   4360             // Find next non-null allocation or move nextAllocIndex to the end.
   4361             while (nextAlloc2ndIndex != SIZE_MAX &&
   4362                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4363             {
   4364                 --nextAlloc2ndIndex;
   4365             }
   4366 
   4367             // Found non-null allocation.
   4368             if (nextAlloc2ndIndex != SIZE_MAX)
   4369             {
   4370                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4371 
   4372                 // 1. Process free space before this allocation.
   4373                 if (lastOffset < suballoc.offset)
   4374                 {
   4375                     // There is free space from lastOffset to suballoc.offset.
   4376                     const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4377                     AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4378                 }
   4379 
   4380                 // 2. Process this allocation.
   4381                 // There is allocation with suballoc.offset, suballoc.size.
   4382                 AddDetailedStatisticsAllocation(inoutStats, suballoc.size);
   4383 
   4384                 // 3. Prepare for next iteration.
   4385                 lastOffset = suballoc.offset + suballoc.size;
   4386                 --nextAlloc2ndIndex;
   4387             }
   4388             // We are at the end.
   4389             else
   4390             {
   4391                 // There is free space from lastOffset to size.
   4392                 if (lastOffset < size)
   4393                 {
   4394                     const UINT64 unusedRangeSize = size - lastOffset;
   4395                     AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
   4396                 }
   4397 
   4398                 // End of loop.
   4399                 lastOffset = size;
   4400             }
   4401         }
   4402     }
   4403 }
   4404 
   4405 void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const
   4406 {
   4407     const UINT64 size = GetSize();
   4408     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4409     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4410     const size_t suballoc1stCount = suballocations1st.size();
   4411     const size_t suballoc2ndCount = suballocations2nd.size();
   4412 
   4413     // FIRST PASS
   4414 
   4415     size_t unusedRangeCount = 0;
   4416     UINT64 usedBytes = 0;
   4417 
   4418     UINT64 lastOffset = 0;
   4419 
   4420     size_t alloc2ndCount = 0;
   4421     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4422     {
   4423         const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
   4424         size_t nextAlloc2ndIndex = 0;
   4425         while (lastOffset < freeSpace2ndTo1stEnd)
   4426         {
   4427             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
   4428             while (nextAlloc2ndIndex < suballoc2ndCount &&
   4429                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4430             {
   4431                 ++nextAlloc2ndIndex;
   4432             }
   4433 
   4434             // Found non-null allocation.
   4435             if (nextAlloc2ndIndex < suballoc2ndCount)
   4436             {
   4437                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4438 
   4439                 // 1. Process free space before this allocation.
   4440                 if (lastOffset < suballoc.offset)
   4441                 {
   4442                     // There is free space from lastOffset to suballoc.offset.
   4443                     ++unusedRangeCount;
   4444                 }
   4445 
   4446                 // 2. Process this allocation.
   4447                 // There is allocation with suballoc.offset, suballoc.size.
   4448                 ++alloc2ndCount;
   4449                 usedBytes += suballoc.size;
   4450 
   4451                 // 3. Prepare for next iteration.
   4452                 lastOffset = suballoc.offset + suballoc.size;
   4453                 ++nextAlloc2ndIndex;
   4454             }
   4455             // We are at the end.
   4456             else
   4457             {
   4458                 if (lastOffset < freeSpace2ndTo1stEnd)
   4459                 {
   4460                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
   4461                     ++unusedRangeCount;
   4462                 }
   4463 
   4464                 // End of loop.
   4465                 lastOffset = freeSpace2ndTo1stEnd;
   4466             }
   4467         }
   4468     }
   4469 
   4470     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
   4471     size_t alloc1stCount = 0;
   4472     const UINT64 freeSpace1stTo2ndEnd =
   4473         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
   4474     while (lastOffset < freeSpace1stTo2ndEnd)
   4475     {
   4476         // Find next non-null allocation or move nextAllocIndex to the end.
   4477         while (nextAlloc1stIndex < suballoc1stCount &&
   4478             suballocations1st[nextAlloc1stIndex].privateData == NULL)
   4479         {
   4480             ++nextAlloc1stIndex;
   4481         }
   4482 
   4483         // Found non-null allocation.
   4484         if (nextAlloc1stIndex < suballoc1stCount)
   4485         {
   4486             const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];
   4487 
   4488             // 1. Process free space before this allocation.
   4489             if (lastOffset < suballoc.offset)
   4490             {
   4491                 // There is free space from lastOffset to suballoc.offset.
   4492                 ++unusedRangeCount;
   4493             }
   4494 
   4495             // 2. Process this allocation.
   4496             // There is allocation with suballoc.offset, suballoc.size.
   4497             ++alloc1stCount;
   4498             usedBytes += suballoc.size;
   4499 
   4500             // 3. Prepare for next iteration.
   4501             lastOffset = suballoc.offset + suballoc.size;
   4502             ++nextAlloc1stIndex;
   4503         }
   4504         // We are at the end.
   4505         else
   4506         {
   4507             if (lastOffset < size)
   4508             {
   4509                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
   4510                 ++unusedRangeCount;
   4511             }
   4512 
   4513             // End of loop.
   4514             lastOffset = freeSpace1stTo2ndEnd;
   4515         }
   4516     }
   4517 
   4518     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   4519     {
   4520         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
   4521         while (lastOffset < size)
   4522         {
   4523             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
   4524             while (nextAlloc2ndIndex != SIZE_MAX &&
   4525                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4526             {
   4527                 --nextAlloc2ndIndex;
   4528             }
   4529 
   4530             // Found non-null allocation.
   4531             if (nextAlloc2ndIndex != SIZE_MAX)
   4532             {
   4533                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4534 
   4535                 // 1. Process free space before this allocation.
   4536                 if (lastOffset < suballoc.offset)
   4537                 {
   4538                     // There is free space from lastOffset to suballoc.offset.
   4539                     ++unusedRangeCount;
   4540                 }
   4541 
   4542                 // 2. Process this allocation.
   4543                 // There is allocation with suballoc.offset, suballoc.size.
   4544                 ++alloc2ndCount;
   4545                 usedBytes += suballoc.size;
   4546 
   4547                 // 3. Prepare for next iteration.
   4548                 lastOffset = suballoc.offset + suballoc.size;
   4549                 --nextAlloc2ndIndex;
   4550             }
   4551             // We are at the end.
   4552             else
   4553             {
   4554                 if (lastOffset < size)
   4555                 {
   4556                     // There is free space from lastOffset to size.
   4557                     ++unusedRangeCount;
   4558                 }
   4559 
   4560                 // End of loop.
   4561                 lastOffset = size;
   4562             }
   4563         }
   4564     }
   4565 
   4566     const UINT64 unusedBytes = size - usedBytes;
   4567     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
   4568 
   4569     // SECOND PASS
   4570     lastOffset = 0;
   4571     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4572     {
   4573         const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
   4574         size_t nextAlloc2ndIndex = 0;
   4575         while (lastOffset < freeSpace2ndTo1stEnd)
   4576         {
   4577             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
   4578             while (nextAlloc2ndIndex < suballoc2ndCount &&
   4579                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4580             {
   4581                 ++nextAlloc2ndIndex;
   4582             }
   4583 
   4584             // Found non-null allocation.
   4585             if (nextAlloc2ndIndex < suballoc2ndCount)
   4586             {
   4587                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4588 
   4589                 // 1. Process free space before this allocation.
   4590                 if (lastOffset < suballoc.offset)
   4591                 {
   4592                     // There is free space from lastOffset to suballoc.offset.
   4593                     const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4594                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4595                 }
   4596 
   4597                 // 2. Process this allocation.
   4598                 // There is allocation with suballoc.offset, suballoc.size.
   4599                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);
   4600 
   4601                 // 3. Prepare for next iteration.
   4602                 lastOffset = suballoc.offset + suballoc.size;
   4603                 ++nextAlloc2ndIndex;
   4604             }
   4605             // We are at the end.
   4606             else
   4607             {
   4608                 if (lastOffset < freeSpace2ndTo1stEnd)
   4609                 {
   4610                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
   4611                     const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
   4612                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4613                 }
   4614 
   4615                 // End of loop.
   4616                 lastOffset = freeSpace2ndTo1stEnd;
   4617             }
   4618         }
   4619     }
   4620 
   4621     nextAlloc1stIndex = m_1stNullItemsBeginCount;
   4622     while (lastOffset < freeSpace1stTo2ndEnd)
   4623     {
   4624         // Find next non-null allocation or move nextAllocIndex to the end.
   4625         while (nextAlloc1stIndex < suballoc1stCount &&
   4626             suballocations1st[nextAlloc1stIndex].privateData == NULL)
   4627         {
   4628             ++nextAlloc1stIndex;
   4629         }
   4630 
   4631         // Found non-null allocation.
   4632         if (nextAlloc1stIndex < suballoc1stCount)
   4633         {
   4634             const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];
   4635 
   4636             // 1. Process free space before this allocation.
   4637             if (lastOffset < suballoc.offset)
   4638             {
   4639                 // There is free space from lastOffset to suballoc.offset.
   4640                 const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4641                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4642             }
   4643 
   4644             // 2. Process this allocation.
   4645             // There is allocation with suballoc.offset, suballoc.size.
   4646             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);
   4647 
   4648             // 3. Prepare for next iteration.
   4649             lastOffset = suballoc.offset + suballoc.size;
   4650             ++nextAlloc1stIndex;
   4651         }
   4652         // We are at the end.
   4653         else
   4654         {
   4655             if (lastOffset < freeSpace1stTo2ndEnd)
   4656             {
   4657                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
   4658                 const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
   4659                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4660             }
   4661 
   4662             // End of loop.
   4663             lastOffset = freeSpace1stTo2ndEnd;
   4664         }
   4665     }
   4666 
   4667     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   4668     {
   4669         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
   4670         while (lastOffset < size)
   4671         {
   4672             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
   4673             while (nextAlloc2ndIndex != SIZE_MAX &&
   4674                 suballocations2nd[nextAlloc2ndIndex].privateData == NULL)
   4675             {
   4676                 --nextAlloc2ndIndex;
   4677             }
   4678 
   4679             // Found non-null allocation.
   4680             if (nextAlloc2ndIndex != SIZE_MAX)
   4681             {
   4682                 const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
   4683 
   4684                 // 1. Process free space before this allocation.
   4685                 if (lastOffset < suballoc.offset)
   4686                 {
   4687                     // There is free space from lastOffset to suballoc.offset.
   4688                     const UINT64 unusedRangeSize = suballoc.offset - lastOffset;
   4689                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4690                 }
   4691 
   4692                 // 2. Process this allocation.
   4693                 // There is allocation with suballoc.offset, suballoc.size.
   4694                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);
   4695 
   4696                 // 3. Prepare for next iteration.
   4697                 lastOffset = suballoc.offset + suballoc.size;
   4698                 --nextAlloc2ndIndex;
   4699             }
   4700             // We are at the end.
   4701             else
   4702             {
   4703                 if (lastOffset < size)
   4704                 {
   4705                     // There is free space from lastOffset to size.
   4706                     const UINT64 unusedRangeSize = size - lastOffset;
   4707                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
   4708                 }
   4709 
   4710                 // End of loop.
   4711                 lastOffset = size;
   4712             }
   4713         }
   4714     }
   4715 
   4716     PrintDetailedMap_End(json);
   4717 }
   4718 
   4719 void BlockMetadata_Linear::DebugLogAllAllocations() const
   4720 {
   4721     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4722     for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)
   4723         if (it->type != SUBALLOCATION_TYPE_FREE)
   4724             DebugLogAllocation(it->offset, it->size, it->privateData);
   4725 
   4726     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4727     for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)
   4728         if (it->type != SUBALLOCATION_TYPE_FREE)
   4729             DebugLogAllocation(it->offset, it->size, it->privateData);
   4730 }
   4731 
   4732 Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const
   4733 {
   4734     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4735     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4736 
   4737     Suballocation refSuballoc;
   4738     refSuballoc.offset = offset;
   4739     // Rest of members stays uninitialized intentionally for better performance.
   4740 
   4741     // Item from the 1st vector.
   4742     {
   4743         const SuballocationVectorType::const_iterator it = BinaryFindSorted(
   4744             suballocations1st.begin() + m_1stNullItemsBeginCount,
   4745             suballocations1st.end(),
   4746             refSuballoc,
   4747             SuballocationOffsetLess());
   4748         if (it != suballocations1st.end())
   4749         {
   4750             return const_cast<Suballocation&>(*it);
   4751         }
   4752     }
   4753 
   4754     if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
   4755     {
   4756         // Rest of members stays uninitialized intentionally for better performance.
   4757         const SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
   4758             BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :
   4759             BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());
   4760         if (it != suballocations2nd.end())
   4761         {
   4762             return const_cast<Suballocation&>(*it);
   4763         }
   4764     }
   4765 
   4766     D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!");
   4767     return const_cast<Suballocation&>(suballocations1st.back()); // Should never occur.
   4768 }
   4769 
   4770 bool BlockMetadata_Linear::ShouldCompact1st() const
   4771 {
   4772     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
   4773     const size_t suballocCount = AccessSuballocations1st().size();
   4774     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
   4775 }
   4776 
   4777 void BlockMetadata_Linear::CleanupAfterFree()
   4778 {
   4779     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4780     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4781 
   4782     if (IsEmpty())
   4783     {
   4784         suballocations1st.clear();
   4785         suballocations2nd.clear();
   4786         m_1stNullItemsBeginCount = 0;
   4787         m_1stNullItemsMiddleCount = 0;
   4788         m_2ndNullItemsCount = 0;
   4789         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
   4790     }
   4791     else
   4792     {
   4793         const size_t suballoc1stCount = suballocations1st.size();
   4794         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
   4795         D3D12MA_ASSERT(nullItem1stCount <= suballoc1stCount);
   4796 
   4797         // Find more null items at the beginning of 1st vector.
   4798         while (m_1stNullItemsBeginCount < suballoc1stCount &&
   4799             suballocations1st[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)
   4800         {
   4801             ++m_1stNullItemsBeginCount;
   4802             --m_1stNullItemsMiddleCount;
   4803         }
   4804 
   4805         // Find more null items at the end of 1st vector.
   4806         while (m_1stNullItemsMiddleCount > 0 &&
   4807             suballocations1st.back().type == SUBALLOCATION_TYPE_FREE)
   4808         {
   4809             --m_1stNullItemsMiddleCount;
   4810             suballocations1st.pop_back();
   4811         }
   4812 
   4813         // Find more null items at the end of 2nd vector.
   4814         while (m_2ndNullItemsCount > 0 &&
   4815             suballocations2nd.back().type == SUBALLOCATION_TYPE_FREE)
   4816         {
   4817             --m_2ndNullItemsCount;
   4818             suballocations2nd.pop_back();
   4819         }
   4820 
   4821         // Find more null items at the beginning of 2nd vector.
   4822         while (m_2ndNullItemsCount > 0 &&
   4823             suballocations2nd[0].type == SUBALLOCATION_TYPE_FREE)
   4824         {
   4825             --m_2ndNullItemsCount;
   4826             suballocations2nd.remove(0);
   4827         }
   4828 
   4829         if (ShouldCompact1st())
   4830         {
   4831             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
   4832             size_t srcIndex = m_1stNullItemsBeginCount;
   4833             for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
   4834             {
   4835                 while (suballocations1st[srcIndex].type == SUBALLOCATION_TYPE_FREE)
   4836                 {
   4837                     ++srcIndex;
   4838                 }
   4839                 if (dstIndex != srcIndex)
   4840                 {
   4841                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
   4842                 }
   4843                 ++srcIndex;
   4844             }
   4845             suballocations1st.resize(nonNullItemCount);
   4846             m_1stNullItemsBeginCount = 0;
   4847             m_1stNullItemsMiddleCount = 0;
   4848         }
   4849 
   4850         // 2nd vector became empty.
   4851         if (suballocations2nd.empty())
   4852         {
   4853             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
   4854         }
   4855 
   4856         // 1st vector became empty.
   4857         if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
   4858         {
   4859             suballocations1st.clear();
   4860             m_1stNullItemsBeginCount = 0;
   4861 
   4862             if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4863             {
   4864                 // Swap 1st with 2nd. Now 2nd is empty.
   4865                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
   4866                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
   4867                 while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
   4868                     suballocations2nd[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)
   4869                 {
   4870                     ++m_1stNullItemsBeginCount;
   4871                     --m_1stNullItemsMiddleCount;
   4872                 }
   4873                 m_2ndNullItemsCount = 0;
   4874                 m_1stVectorIndex ^= 1;
   4875             }
   4876         }
   4877     }
   4878 
   4879     D3D12MA_HEAVY_ASSERT(Validate());
   4880 }
   4881 
   4882 bool BlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
   4883     UINT64 allocSize,
   4884     UINT64 allocAlignment,
   4885     AllocationRequest* pAllocationRequest)
   4886 {
   4887     const UINT64 blockSize = GetSize();
   4888     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4889     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4890 
   4891     if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
   4892     {
   4893         // Try to allocate at the end of 1st vector.
   4894 
   4895         UINT64 resultBaseOffset = 0;
   4896         if (!suballocations1st.empty())
   4897         {
   4898             const Suballocation& lastSuballoc = suballocations1st.back();
   4899             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();
   4900         }
   4901 
   4902         // Start from offset equal to beginning of free space.
   4903         UINT64 resultOffset = resultBaseOffset;
   4904         // Apply alignment.
   4905         resultOffset = AlignUp(resultOffset, allocAlignment);
   4906 
   4907         const UINT64 freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
   4908             suballocations2nd.back().offset : blockSize;
   4909 
   4910         // There is enough free space at the end after alignment.
   4911         if (resultOffset + allocSize + GetDebugMargin() <= freeSpaceEnd)
   4912         {
   4913             // All tests passed: Success.
   4914             pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);
   4915             // pAllocationRequest->item, customData unused.
   4916             pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_1ST;
   4917             return true;
   4918         }
   4919     }
   4920 
   4921     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
   4922     // beginning of 1st vector as the end of free space.
   4923     if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4924     {
   4925         D3D12MA_ASSERT(!suballocations1st.empty());
   4926 
   4927         UINT64 resultBaseOffset = 0;
   4928         if (!suballocations2nd.empty())
   4929         {
   4930             const Suballocation& lastSuballoc = suballocations2nd.back();
   4931             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();
   4932         }
   4933 
   4934         // Start from offset equal to beginning of free space.
   4935         UINT64 resultOffset = resultBaseOffset;
   4936 
   4937         // Apply alignment.
   4938         resultOffset = AlignUp(resultOffset, allocAlignment);
   4939 
   4940         size_t index1st = m_1stNullItemsBeginCount;
   4941         // There is enough free space at the end after alignment.
   4942         if ((index1st == suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= blockSize) ||
   4943             (index1st < suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= suballocations1st[index1st].offset))
   4944         {
   4945             // All tests passed: Success.
   4946             pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);
   4947             pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_2ND;
   4948             // pAllocationRequest->item, customData unused.
   4949             return true;
   4950         }
   4951     }
   4952     return false;
   4953 }
   4954 
   4955 bool BlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
   4956     UINT64 allocSize,
   4957     UINT64 allocAlignment,
   4958     AllocationRequest* pAllocationRequest)
   4959 {
   4960     const UINT64 blockSize = GetSize();
   4961     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
   4962     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
   4963 
   4964     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
   4965     {
   4966         D3D12MA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
   4967         return false;
   4968     }
   4969 
   4970     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
   4971     if (allocSize > blockSize)
   4972     {
   4973         return false;
   4974     }
   4975     UINT64 resultBaseOffset = blockSize - allocSize;
   4976     if (!suballocations2nd.empty())
   4977     {
   4978         const Suballocation& lastSuballoc = suballocations2nd.back();
   4979         resultBaseOffset = lastSuballoc.offset - allocSize;
   4980         if (allocSize > lastSuballoc.offset)
   4981         {
   4982             return false;
   4983         }
   4984     }
   4985 
   4986     // Start from offset equal to end of free space.
   4987     UINT64 resultOffset = resultBaseOffset;
   4988     // Apply debugMargin at the end.
   4989     if (GetDebugMargin() > 0)
   4990     {
   4991         if (resultOffset < GetDebugMargin())
   4992         {
   4993             return false;
   4994         }
   4995         resultOffset -= GetDebugMargin();
   4996     }
   4997 
   4998     // Apply alignment.
   4999     resultOffset = AlignDown(resultOffset, allocAlignment);
   5000     // There is enough free space.
   5001     const UINT64 endOf1st = !suballocations1st.empty() ?
   5002         suballocations1st.back().offset + suballocations1st.back().size : 0;
   5003 
   5004     if (endOf1st + GetDebugMargin() <= resultOffset)
   5005     {
   5006         // All tests passed: Success.
   5007         pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);
   5008         // pAllocationRequest->item unused.
   5009         pAllocationRequest->algorithmData = ALLOC_REQUEST_UPPER_ADDRESS;
   5010         return true;
   5011     }
   5012     return false;
   5013 }
   5014 #endif // _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS
   5015 #endif // _D3D12MA_BLOCK_METADATA_LINEAR
   5016 
   5017 #ifndef _D3D12MA_BLOCK_METADATA_TLSF
   5018 class BlockMetadata_TLSF : public BlockMetadata
   5019 {
   5020 public:
   5021     BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
   5022     virtual ~BlockMetadata_TLSF();
   5023 
   5024     size_t GetAllocationCount() const override { return m_AllocCount; }
   5025     size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }
   5026     UINT64 GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }
   5027     bool IsEmpty() const override { return m_NullBlock->offset == 0; }
   5028     UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };
   5029 
   5030     void Init(UINT64 size) override;
   5031     bool Validate() const override;
   5032     void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;
   5033 
   5034     bool CreateAllocationRequest(
   5035         UINT64 allocSize,
   5036         UINT64 allocAlignment,
   5037         bool upperAddress,
   5038         UINT32 strategy,
   5039         AllocationRequest* pAllocationRequest) override;
   5040 
   5041     void Alloc(
   5042         const AllocationRequest& request,
   5043         UINT64 allocSize,
   5044         void* privateData) override;
   5045 
   5046     void Free(AllocHandle allocHandle) override;
   5047     void Clear() override;
   5048 
   5049     AllocHandle GetAllocationListBegin() const override;
   5050     AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;
   5051     UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;
   5052     void* GetAllocationPrivateData(AllocHandle allocHandle) const override;
   5053     void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;
   5054 
   5055     void AddStatistics(Statistics& inoutStats) const override;
   5056     void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;
   5057     void WriteAllocationInfoToJson(JsonWriter& json) const override;
   5058     void DebugLogAllAllocations() const override;
   5059 
   5060 private:
   5061     // According to original paper it should be preferable 4 or 5:
   5062     // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"
   5063     // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf
   5064     static const UINT8 SECOND_LEVEL_INDEX = 5;
   5065     static const UINT16 SMALL_BUFFER_SIZE = 256;
   5066     static const UINT INITIAL_BLOCK_ALLOC_COUNT = 16;
   5067     static const UINT8 MEMORY_CLASS_SHIFT = 7;
   5068     static const UINT8 MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;
   5069 
   5070     class Block
   5071     {
   5072     public:
   5073         UINT64 offset;
   5074         UINT64 size;
   5075         Block* prevPhysical;
   5076         Block* nextPhysical;
   5077 
   5078         void MarkFree() { prevFree = NULL; }
   5079         void MarkTaken() { prevFree = this; }
   5080         bool IsFree() const { return prevFree != this; }
   5081         void*& PrivateData() { D3D12MA_HEAVY_ASSERT(!IsFree()); return privateData; }
   5082         Block*& PrevFree() { return prevFree; }
   5083         Block*& NextFree() { D3D12MA_HEAVY_ASSERT(IsFree()); return nextFree; }
   5084 
   5085     private:
   5086         Block* prevFree; // Address of the same block here indicates that block is taken
   5087         union
   5088         {
   5089             Block* nextFree;
   5090             void* privateData;
   5091         };
   5092     };
   5093     
   5094     size_t m_AllocCount = 0;
   5095     // Total number of free blocks besides null block
   5096     size_t m_BlocksFreeCount = 0;
   5097     // Total size of free blocks excluding null block
   5098     UINT64 m_BlocksFreeSize = 0;
   5099     UINT32 m_IsFreeBitmap = 0;
   5100     UINT8 m_MemoryClasses = 0;
   5101     UINT32 m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];
   5102     UINT32 m_ListsCount = 0;
   5103     /*
   5104     * 0: 0-3 lists for small buffers
   5105     * 1+: 0-(2^SLI-1) lists for normal buffers
   5106     */
   5107     Block** m_FreeList = NULL;
   5108     PoolAllocator<Block> m_BlockAllocator;
   5109     Block* m_NullBlock = NULL;
   5110 
   5111     UINT8 SizeToMemoryClass(UINT64 size) const;
   5112     UINT16 SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const;
   5113     UINT32 GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const;
   5114     UINT32 GetListIndex(UINT64 size) const;
   5115 
   5116     void RemoveFreeBlock(Block* block);
   5117     void InsertFreeBlock(Block* block);
   5118     void MergeBlock(Block* block, Block* prev);
   5119 
   5120     Block* FindFreeBlock(UINT64 size, UINT32& listIndex) const;
   5121     bool CheckBlock(
   5122         Block& block,
   5123         UINT32 listIndex,
   5124         UINT64 allocSize,
   5125         UINT64 allocAlignment,
   5126         AllocationRequest* pAllocationRequest);
   5127 
   5128     D3D12MA_CLASS_NO_COPY(BlockMetadata_TLSF)
   5129 };
   5130 
   5131 #ifndef _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS
   5132 BlockMetadata_TLSF::BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)
   5133     : BlockMetadata(allocationCallbacks, isVirtual),
   5134     m_BlockAllocator(*allocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT)
   5135 {
   5136     D3D12MA_ASSERT(allocationCallbacks);
   5137 }
   5138 
   5139 BlockMetadata_TLSF::~BlockMetadata_TLSF()
   5140 {
   5141     D3D12MA_DELETE_ARRAY(*GetAllocs(), m_FreeList, m_ListsCount);
   5142 }
   5143 
   5144 void BlockMetadata_TLSF::Init(UINT64 size)
   5145 {
   5146     BlockMetadata::Init(size);
   5147 
   5148     m_NullBlock = m_BlockAllocator.Alloc();
   5149     m_NullBlock->size = size;
   5150     m_NullBlock->offset = 0;
   5151     m_NullBlock->prevPhysical = NULL;
   5152     m_NullBlock->nextPhysical = NULL;
   5153     m_NullBlock->MarkFree();
   5154     m_NullBlock->NextFree() = NULL;
   5155     m_NullBlock->PrevFree() = NULL;
   5156     UINT8 memoryClass = SizeToMemoryClass(size);
   5157     UINT16 sli = SizeToSecondIndex(size, memoryClass);
   5158     m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;
   5159     if (IsVirtual())
   5160         m_ListsCount += 1UL << SECOND_LEVEL_INDEX;
   5161     else
   5162         m_ListsCount += 4;
   5163 
   5164     m_MemoryClasses = memoryClass + 2;
   5165     memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(UINT32));
   5166 
   5167     m_FreeList = D3D12MA_NEW_ARRAY(*GetAllocs(), Block*, m_ListsCount);
   5168     memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
   5169 }
   5170 
   5171 bool BlockMetadata_TLSF::Validate() const
   5172 {
   5173     D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());
   5174 
   5175     UINT64 calculatedSize = m_NullBlock->size;
   5176     UINT64 calculatedFreeSize = m_NullBlock->size;
   5177     size_t allocCount = 0;
   5178     size_t freeCount = 0;
   5179 
   5180     // Check integrity of free lists
   5181     for (UINT32 list = 0; list < m_ListsCount; ++list)
   5182     {
   5183         Block* block = m_FreeList[list];
   5184         if (block != NULL)
   5185         {
   5186             D3D12MA_VALIDATE(block->IsFree());
   5187             D3D12MA_VALIDATE(block->PrevFree() == NULL);
   5188             while (block->NextFree())
   5189             {
   5190                 D3D12MA_VALIDATE(block->NextFree()->IsFree());
   5191                 D3D12MA_VALIDATE(block->NextFree()->PrevFree() == block);
   5192                 block = block->NextFree();
   5193             }
   5194         }
   5195     }
   5196 
   5197     D3D12MA_VALIDATE(m_NullBlock->nextPhysical == NULL);
   5198     if (m_NullBlock->prevPhysical)
   5199     {
   5200         D3D12MA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);
   5201     }
   5202 
   5203     // Check all blocks
   5204     UINT64 nextOffset = m_NullBlock->offset;
   5205     for (Block* prev = m_NullBlock->prevPhysical; prev != NULL; prev = prev->prevPhysical)
   5206     {
   5207         D3D12MA_VALIDATE(prev->offset + prev->size == nextOffset);
   5208         nextOffset = prev->offset;
   5209         calculatedSize += prev->size;
   5210 
   5211         UINT32 listIndex = GetListIndex(prev->size);
   5212         if (prev->IsFree())
   5213         {
   5214             ++freeCount;
   5215             // Check if free block belongs to free list
   5216             Block* freeBlock = m_FreeList[listIndex];
   5217             D3D12MA_VALIDATE(freeBlock != NULL);
   5218 
   5219             bool found = false;
   5220             do
   5221             {
   5222                 if (freeBlock == prev)
   5223                     found = true;
   5224 
   5225                 freeBlock = freeBlock->NextFree();
   5226             } while (!found && freeBlock != NULL);
   5227 
   5228             D3D12MA_VALIDATE(found);
   5229             calculatedFreeSize += prev->size;
   5230         }
   5231         else
   5232         {
   5233             ++allocCount;
   5234             // Check if taken block is not on a free list
   5235             Block* freeBlock = m_FreeList[listIndex];
   5236             while (freeBlock)
   5237             {
   5238                 D3D12MA_VALIDATE(freeBlock != prev);
   5239                 freeBlock = freeBlock->NextFree();
   5240             }
   5241         }
   5242 
   5243         if (prev->prevPhysical)
   5244         {
   5245             D3D12MA_VALIDATE(prev->prevPhysical->nextPhysical == prev);
   5246         }
   5247     }
   5248 
   5249     D3D12MA_VALIDATE(nextOffset == 0);
   5250     D3D12MA_VALIDATE(calculatedSize == GetSize());
   5251     D3D12MA_VALIDATE(calculatedFreeSize == GetSumFreeSize());
   5252     D3D12MA_VALIDATE(allocCount == m_AllocCount);
   5253     D3D12MA_VALIDATE(freeCount == m_BlocksFreeCount);
   5254 
   5255     return true;
   5256 }
   5257 
   5258 void BlockMetadata_TLSF::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const
   5259 {
   5260     Block* block = (Block*)allocHandle;
   5261     D3D12MA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");
   5262     outInfo.Offset = block->offset;
   5263     outInfo.Size = block->size;
   5264     outInfo.pPrivateData = block->PrivateData();
   5265 }
   5266 
   5267 bool BlockMetadata_TLSF::CreateAllocationRequest(
   5268     UINT64 allocSize,
   5269     UINT64 allocAlignment,
   5270     bool upperAddress,
   5271     UINT32 strategy,
   5272     AllocationRequest* pAllocationRequest)
   5273 {
   5274     D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
   5275     D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");
   5276     D3D12MA_ASSERT(pAllocationRequest != NULL);
   5277     D3D12MA_HEAVY_ASSERT(Validate());
   5278 
   5279     allocSize += GetDebugMargin();
   5280     // Quick check for too small pool
   5281     if (allocSize > GetSumFreeSize())
   5282         return false;
   5283 
   5284     // If no free blocks in pool then check only null block
   5285     if (m_BlocksFreeCount == 0)
   5286         return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest);
   5287 
   5288     // Round up to the next block
   5289     UINT64 sizeForNextList = allocSize;
   5290     UINT16 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);
   5291     if (allocSize > SMALL_BUFFER_SIZE)
   5292     {
   5293         sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX));
   5294     }
   5295     else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)
   5296         sizeForNextList = SMALL_BUFFER_SIZE + 1;
   5297     else
   5298         sizeForNextList += smallSizeStep;
   5299 
   5300     UINT32 nextListIndex = 0;
   5301     UINT32 prevListIndex = 0;
   5302     Block* nextListBlock = NULL;
   5303     Block* prevListBlock = NULL;
   5304 
   5305     // Check blocks according to strategies
   5306     if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_TIME)
   5307     {
   5308         // Quick check for larger block first
   5309         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
   5310         if (nextListBlock != NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))
   5311             return true;
   5312 
   5313         // If not fitted then null block
   5314         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))
   5315             return true;
   5316 
   5317         // Null block failed, search larger bucket
   5318         while (nextListBlock)
   5319         {
   5320             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))
   5321                 return true;
   5322             nextListBlock = nextListBlock->NextFree();
   5323         }
   5324 
   5325         // Failed again, check best fit bucket
   5326         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
   5327         while (prevListBlock)
   5328         {
   5329             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))
   5330                 return true;
   5331             prevListBlock = prevListBlock->NextFree();
   5332         }
   5333     }
   5334     else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_MEMORY)
   5335     {
   5336         // Check best fit bucket
   5337         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
   5338         while (prevListBlock)
   5339         {
   5340             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))
   5341                 return true;
   5342             prevListBlock = prevListBlock->NextFree();
   5343         }
   5344 
   5345         // If failed check null block
   5346         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))
   5347             return true;
   5348 
   5349         // Check larger bucket
   5350         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
   5351         while (nextListBlock)
   5352         {
   5353             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))
   5354                 return true;
   5355             nextListBlock = nextListBlock->NextFree();
   5356         }
   5357     }
   5358     else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_OFFSET)
   5359     {
   5360         // Perform search from the start
   5361         Vector<Block*> blockList(m_BlocksFreeCount, *GetAllocs());
   5362 
   5363         size_t i = m_BlocksFreeCount;
   5364         for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)
   5365         {
   5366             if (block->IsFree() && block->size >= allocSize)
   5367                 blockList[--i] = block;
   5368         }
   5369 
   5370         for (; i < m_BlocksFreeCount; ++i)
   5371         {
   5372             Block& block = *blockList[i];
   5373             if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, pAllocationRequest))
   5374                 return true;
   5375         }
   5376 
   5377         // If failed check null block
   5378         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))
   5379             return true;
   5380 
   5381         // Whole range searched, no more memory
   5382         return false;
   5383     }
   5384     else
   5385     {
   5386         // Check larger bucket
   5387         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
   5388         while (nextListBlock)
   5389         {
   5390             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))
   5391                 return true;
   5392             nextListBlock = nextListBlock->NextFree();
   5393         }
   5394 
   5395         // If failed check null block
   5396         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))
   5397             return true;
   5398 
   5399         // Check best fit bucket
   5400         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
   5401         while (prevListBlock)
   5402         {
   5403             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))
   5404                 return true;
   5405             prevListBlock = prevListBlock->NextFree();
   5406         }
   5407     }
   5408 
   5409     // Worst case, full search has to be done
   5410     while (++nextListIndex < m_ListsCount)
   5411     {
   5412         nextListBlock = m_FreeList[nextListIndex];
   5413         while (nextListBlock)
   5414         {
   5415             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))
   5416                 return true;
   5417             nextListBlock = nextListBlock->NextFree();
   5418         }
   5419     }
   5420 
   5421     // No more memory sadly
   5422     return false;
   5423 }
   5424 
   5425 void BlockMetadata_TLSF::Alloc(
   5426     const AllocationRequest& request,
   5427     UINT64 allocSize,
   5428     void* privateData)
   5429 {
   5430     // Get block and pop it from the free list
   5431     Block* currentBlock = (Block*)request.allocHandle;
   5432     UINT64 offset = request.algorithmData;
   5433     D3D12MA_ASSERT(currentBlock != NULL);
   5434     D3D12MA_ASSERT(currentBlock->offset <= offset);
   5435 
   5436     if (currentBlock != m_NullBlock)
   5437         RemoveFreeBlock(currentBlock);
   5438 
   5439     // Append missing alignment to prev block or create new one
   5440     UINT64 misssingAlignment = offset - currentBlock->offset;
   5441     if (misssingAlignment)
   5442     {
   5443         Block* prevBlock = currentBlock->prevPhysical;
   5444         D3D12MA_ASSERT(prevBlock != NULL && "There should be no missing alignment at offset 0!");
   5445 
   5446         if (prevBlock->IsFree() && prevBlock->size != GetDebugMargin())
   5447         {
   5448             UINT32 oldList = GetListIndex(prevBlock->size);
   5449             prevBlock->size += misssingAlignment;
   5450             // Check if new size crosses list bucket
   5451             if (oldList != GetListIndex(prevBlock->size))
   5452             {
   5453                 prevBlock->size -= misssingAlignment;
   5454                 RemoveFreeBlock(prevBlock);
   5455                 prevBlock->size += misssingAlignment;
   5456                 InsertFreeBlock(prevBlock);
   5457             }
   5458             else
   5459                 m_BlocksFreeSize += misssingAlignment;
   5460         }
   5461         else
   5462         {
   5463             Block* newBlock = m_BlockAllocator.Alloc();
   5464             currentBlock->prevPhysical = newBlock;
   5465             prevBlock->nextPhysical = newBlock;
   5466             newBlock->prevPhysical = prevBlock;
   5467             newBlock->nextPhysical = currentBlock;
   5468             newBlock->size = misssingAlignment;
   5469             newBlock->offset = currentBlock->offset;
   5470             newBlock->MarkTaken();
   5471 
   5472             InsertFreeBlock(newBlock);
   5473         }
   5474 
   5475         currentBlock->size -= misssingAlignment;
   5476         currentBlock->offset += misssingAlignment;
   5477     }
   5478 
   5479     UINT64 size = request.size + GetDebugMargin();
   5480     if (currentBlock->size == size)
   5481     {
   5482         if (currentBlock == m_NullBlock)
   5483         {
   5484             // Setup new null block
   5485             m_NullBlock = m_BlockAllocator.Alloc();
   5486             m_NullBlock->size = 0;
   5487             m_NullBlock->offset = currentBlock->offset + size;
   5488             m_NullBlock->prevPhysical = currentBlock;
   5489             m_NullBlock->nextPhysical = NULL;
   5490             m_NullBlock->MarkFree();
   5491             m_NullBlock->PrevFree() = NULL;
   5492             m_NullBlock->NextFree() = NULL;
   5493             currentBlock->nextPhysical = m_NullBlock;
   5494             currentBlock->MarkTaken();
   5495         }
   5496     }
   5497     else
   5498     {
   5499         D3D12MA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");
   5500 
   5501         // Create new free block
   5502         Block* newBlock = m_BlockAllocator.Alloc();
   5503         newBlock->size = currentBlock->size - size;
   5504         newBlock->offset = currentBlock->offset + size;
   5505         newBlock->prevPhysical = currentBlock;
   5506         newBlock->nextPhysical = currentBlock->nextPhysical;
   5507         currentBlock->nextPhysical = newBlock;
   5508         currentBlock->size = size;
   5509 
   5510         if (currentBlock == m_NullBlock)
   5511         {
   5512             m_NullBlock = newBlock;
   5513             m_NullBlock->MarkFree();
   5514             m_NullBlock->NextFree() = NULL;
   5515             m_NullBlock->PrevFree() = NULL;
   5516             currentBlock->MarkTaken();
   5517         }
   5518         else
   5519         {
   5520             newBlock->nextPhysical->prevPhysical = newBlock;
   5521             newBlock->MarkTaken();
   5522             InsertFreeBlock(newBlock);
   5523         }
   5524     }
   5525     currentBlock->PrivateData() = privateData;
   5526 
   5527     if (GetDebugMargin() > 0)
   5528     {
   5529         currentBlock->size -= GetDebugMargin();
   5530         Block* newBlock = m_BlockAllocator.Alloc();
   5531         newBlock->size = GetDebugMargin();
   5532         newBlock->offset = currentBlock->offset + currentBlock->size;
   5533         newBlock->prevPhysical = currentBlock;
   5534         newBlock->nextPhysical = currentBlock->nextPhysical;
   5535         newBlock->MarkTaken();
   5536         currentBlock->nextPhysical->prevPhysical = newBlock;
   5537         currentBlock->nextPhysical = newBlock;
   5538         InsertFreeBlock(newBlock);
   5539     }
   5540     ++m_AllocCount;
   5541 }
   5542 
   5543 void BlockMetadata_TLSF::Free(AllocHandle allocHandle)
   5544 {
   5545     Block* block = (Block*)allocHandle;
   5546     Block* next = block->nextPhysical;
   5547     D3D12MA_ASSERT(!block->IsFree() && "Block is already free!");
   5548 
   5549     --m_AllocCount;
   5550     if (GetDebugMargin() > 0)
   5551     {
   5552         RemoveFreeBlock(next);
   5553         MergeBlock(next, block);
   5554         block = next;
   5555         next = next->nextPhysical;
   5556     }
   5557 
   5558     // Try merging
   5559     Block* prev = block->prevPhysical;
   5560     if (prev != NULL && prev->IsFree() && prev->size != GetDebugMargin())
   5561     {
   5562         RemoveFreeBlock(prev);
   5563         MergeBlock(block, prev);
   5564     }
   5565 
   5566     if (!next->IsFree())
   5567         InsertFreeBlock(block);
   5568     else if (next == m_NullBlock)
   5569         MergeBlock(m_NullBlock, block);
   5570     else
   5571     {
   5572         RemoveFreeBlock(next);
   5573         MergeBlock(next, block);
   5574         InsertFreeBlock(next);
   5575     }
   5576 }
   5577 
   5578 void BlockMetadata_TLSF::Clear()
   5579 {
   5580     m_AllocCount = 0;
   5581     m_BlocksFreeCount = 0;
   5582     m_BlocksFreeSize = 0;
   5583     m_IsFreeBitmap = 0;
   5584     m_NullBlock->offset = 0;
   5585     m_NullBlock->size = GetSize();
   5586     Block* block = m_NullBlock->prevPhysical;
   5587     m_NullBlock->prevPhysical = NULL;
   5588     while (block)
   5589     {
   5590         Block* prev = block->prevPhysical;
   5591         m_BlockAllocator.Free(block);
   5592         block = prev;
   5593     }
   5594     memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
   5595     memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(UINT32));
   5596 }
   5597 
   5598 AllocHandle BlockMetadata_TLSF::GetAllocationListBegin() const
   5599 {
   5600     if (m_AllocCount == 0)
   5601         return (AllocHandle)0;
   5602 
   5603     for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)
   5604     {
   5605         if (!block->IsFree())
   5606             return (AllocHandle)block;
   5607     }
   5608     D3D12MA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");
   5609     return (AllocHandle)0;
   5610 }
   5611 
   5612 AllocHandle BlockMetadata_TLSF::GetNextAllocation(AllocHandle prevAlloc) const
   5613 {
   5614     Block* startBlock = (Block*)prevAlloc;
   5615     D3D12MA_ASSERT(!startBlock->IsFree() && "Incorrect block!");
   5616 
   5617     for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)
   5618     {
   5619         if (!block->IsFree())
   5620             return (AllocHandle)block;
   5621     }
   5622     return (AllocHandle)0;
   5623 }
   5624 
   5625 UINT64 BlockMetadata_TLSF::GetNextFreeRegionSize(AllocHandle alloc) const
   5626 {
   5627     Block* block = (Block*)alloc;
   5628     D3D12MA_ASSERT(!block->IsFree() && "Incorrect block!");
   5629 
   5630     if (block->prevPhysical)
   5631         return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;
   5632     return 0;
   5633 }
   5634 
   5635 void* BlockMetadata_TLSF::GetAllocationPrivateData(AllocHandle allocHandle) const
   5636 {
   5637     Block* block = (Block*)allocHandle;
   5638     D3D12MA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");
   5639     return block->PrivateData();
   5640 }
   5641 
   5642 void BlockMetadata_TLSF::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)
   5643 {
   5644     Block* block = (Block*)allocHandle;
   5645     D3D12MA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");
   5646     block->PrivateData() = privateData;
   5647 }
   5648 
   5649 void BlockMetadata_TLSF::AddStatistics(Statistics& inoutStats) const
   5650 {
   5651     inoutStats.BlockCount++;
   5652     inoutStats.AllocationCount += static_cast<UINT>(m_AllocCount);
   5653     inoutStats.BlockBytes += GetSize();
   5654     inoutStats.AllocationBytes += GetSize() - GetSumFreeSize();
   5655 }
   5656 
   5657 void BlockMetadata_TLSF::AddDetailedStatistics(DetailedStatistics& inoutStats) const
   5658 {
   5659     inoutStats.Stats.BlockCount++;
   5660     inoutStats.Stats.BlockBytes += GetSize();
   5661 
   5662     for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)
   5663     {
   5664         if (block->IsFree())
   5665             AddDetailedStatisticsUnusedRange(inoutStats, block->size);
   5666         else
   5667             AddDetailedStatisticsAllocation(inoutStats, block->size);
   5668     }
   5669 
   5670     if (m_NullBlock->size > 0)
   5671         AddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);
   5672 }
   5673 
   5674 void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const
   5675 {
   5676     size_t blockCount = m_AllocCount + m_BlocksFreeCount;
   5677     Vector<Block*> blockList(blockCount, *GetAllocs());
   5678 
   5679     size_t i = blockCount;
   5680     if (m_NullBlock->size > 0)
   5681     {
   5682         ++blockCount;
   5683         blockList.push_back(m_NullBlock);
   5684     }
   5685     for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)
   5686     {
   5687         blockList[--i] = block;
   5688     }
   5689     D3D12MA_ASSERT(i == 0);
   5690 
   5691     PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_BlocksFreeCount + static_cast<bool>(m_NullBlock->size));
   5692     for (; i < blockCount; ++i)
   5693     {
   5694         Block* block = blockList[i];
   5695         if (block->IsFree())
   5696             PrintDetailedMap_UnusedRange(json, block->offset, block->size);
   5697         else
   5698             PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData());
   5699     }
   5700     PrintDetailedMap_End(json);
   5701 }
   5702 
   5703 void BlockMetadata_TLSF::DebugLogAllAllocations() const
   5704 {
   5705     for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)
   5706     {
   5707         if (!block->IsFree())
   5708         {
   5709             DebugLogAllocation(block->offset, block->size, block->PrivateData());
   5710         }
   5711     }
   5712 }
   5713 
   5714 UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const
   5715 {
   5716     if (size > SMALL_BUFFER_SIZE)
   5717         return BitScanMSB(size) - MEMORY_CLASS_SHIFT;
   5718     return 0;
   5719 }
   5720 
   5721 UINT16 BlockMetadata_TLSF::SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const
   5722 {
   5723     if (memoryClass == 0)
   5724     {
   5725         if (IsVirtual())
   5726             return static_cast<UINT16>((size - 1) / 8);
   5727         else
   5728             return static_cast<UINT16>((size - 1) / 64);
   5729     }
   5730     return static_cast<UINT16>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));
   5731 }
   5732 
   5733 UINT32 BlockMetadata_TLSF::GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const
   5734 {
   5735     if (memoryClass == 0)
   5736         return secondIndex;
   5737 
   5738     const UINT32 index = static_cast<UINT32>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;
   5739     if (IsVirtual())
   5740         return index + (1 << SECOND_LEVEL_INDEX);
   5741     else
   5742         return index + 4;
   5743 }
   5744 
   5745 UINT32 BlockMetadata_TLSF::GetListIndex(UINT64 size) const
   5746 {
   5747     UINT8 memoryClass = SizeToMemoryClass(size);
   5748     return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));
   5749 }
   5750 
   5751 void BlockMetadata_TLSF::RemoveFreeBlock(Block* block)
   5752 {
   5753     D3D12MA_ASSERT(block != m_NullBlock);
   5754     D3D12MA_ASSERT(block->IsFree());
   5755 
   5756     if (block->NextFree() != NULL)
   5757         block->NextFree()->PrevFree() = block->PrevFree();
   5758     if (block->PrevFree() != NULL)
   5759         block->PrevFree()->NextFree() = block->NextFree();
   5760     else
   5761     {
   5762         UINT8 memClass = SizeToMemoryClass(block->size);
   5763         UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);
   5764         UINT32 index = GetListIndex(memClass, secondIndex);
   5765         m_FreeList[index] = block->NextFree();
   5766         if (block->NextFree() == NULL)
   5767         {
   5768             m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);
   5769             if (m_InnerIsFreeBitmap[memClass] == 0)
   5770                 m_IsFreeBitmap &= ~(1UL << memClass);
   5771         }
   5772     }
   5773     block->MarkTaken();
   5774     block->PrivateData() = NULL;
   5775     --m_BlocksFreeCount;
   5776     m_BlocksFreeSize -= block->size;
   5777 }
   5778 
   5779 void BlockMetadata_TLSF::InsertFreeBlock(Block* block)
   5780 {
   5781     D3D12MA_ASSERT(block != m_NullBlock);
   5782     D3D12MA_ASSERT(!block->IsFree() && "Cannot insert block twice!");
   5783 
   5784     UINT8 memClass = SizeToMemoryClass(block->size);
   5785     UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);
   5786     UINT32 index = GetListIndex(memClass, secondIndex);
   5787     block->PrevFree() = NULL;
   5788     block->NextFree() = m_FreeList[index];
   5789     m_FreeList[index] = block;
   5790     if (block->NextFree() != NULL)
   5791         block->NextFree()->PrevFree() = block;
   5792     else
   5793     {
   5794         m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;
   5795         m_IsFreeBitmap |= 1UL << memClass;
   5796     }
   5797     ++m_BlocksFreeCount;
   5798     m_BlocksFreeSize += block->size;
   5799 }
   5800 
   5801 void BlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)
   5802 {
   5803     D3D12MA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");
   5804     D3D12MA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");
   5805 
   5806     block->offset = prev->offset;
   5807     block->size += prev->size;
   5808     block->prevPhysical = prev->prevPhysical;
   5809     if (block->prevPhysical)
   5810         block->prevPhysical->nextPhysical = block;
   5811     m_BlockAllocator.Free(prev);
   5812 }
   5813 
   5814 BlockMetadata_TLSF::Block* BlockMetadata_TLSF::FindFreeBlock(UINT64 size, UINT32& listIndex) const
   5815 {
   5816     UINT8 memoryClass = SizeToMemoryClass(size);
   5817     UINT32 innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));
   5818     if (!innerFreeMap)
   5819     {
   5820         // Check higher levels for avaiable blocks
   5821         UINT32 freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));
   5822         if (!freeMap)
   5823             return NULL; // No more memory avaible
   5824 
   5825         // Find lowest free region
   5826         memoryClass = BitScanLSB(freeMap);
   5827         innerFreeMap = m_InnerIsFreeBitmap[memoryClass];
   5828         D3D12MA_ASSERT(innerFreeMap != 0);
   5829     }
   5830     // Find lowest free subregion
   5831     listIndex = GetListIndex(memoryClass, BitScanLSB(innerFreeMap));
   5832     return m_FreeList[listIndex];
   5833 }
   5834 
   5835 bool BlockMetadata_TLSF::CheckBlock(
   5836     Block& block,
   5837     UINT32 listIndex,
   5838     UINT64 allocSize,
   5839     UINT64 allocAlignment,
   5840     AllocationRequest* pAllocationRequest)
   5841 {
   5842     D3D12MA_ASSERT(block.IsFree() && "Block is already taken!");
   5843 
   5844     UINT64 alignedOffset = AlignUp(block.offset, allocAlignment);
   5845     if (block.size < allocSize + alignedOffset - block.offset)
   5846         return false;
   5847 
   5848     // Alloc successful
   5849     pAllocationRequest->allocHandle = (AllocHandle)&block;
   5850     pAllocationRequest->size = allocSize - GetDebugMargin();
   5851     pAllocationRequest->algorithmData = alignedOffset;
   5852 
   5853     // Place block at the start of list if it's normal block
   5854     if (listIndex != m_ListsCount && block.PrevFree())
   5855     {
   5856         block.PrevFree()->NextFree() = block.NextFree();
   5857         if (block.NextFree())
   5858             block.NextFree()->PrevFree() = block.PrevFree();
   5859         block.PrevFree() = NULL;
   5860         block.NextFree() = m_FreeList[listIndex];
   5861         m_FreeList[listIndex] = &block;
   5862         if (block.NextFree())
   5863             block.NextFree()->PrevFree() = &block;
   5864     }
   5865 
   5866     return true;
   5867 }
   5868 #endif // _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS
   5869 #endif // _D3D12MA_BLOCK_METADATA_TLSF
   5870 
   5871 #ifndef _D3D12MA_MEMORY_BLOCK
   5872 /*
   5873 Represents a single block of device memory (heap).
   5874 Base class for inheritance.
   5875 Thread-safety: This class must be externally synchronized.
   5876 */
   5877 class MemoryBlock
   5878 {
   5879 public:
   5880     // Creates the ID3D12Heap.
   5881     MemoryBlock(
   5882         AllocatorPimpl* allocator,
   5883         const D3D12_HEAP_PROPERTIES& heapProps,
   5884         D3D12_HEAP_FLAGS heapFlags,
   5885         UINT64 size,
   5886         UINT id);
   5887     virtual ~MemoryBlock();
   5888 
   5889     const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }
   5890     D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }
   5891     UINT64 GetSize() const { return m_Size; }
   5892     UINT GetId() const { return m_Id; }
   5893     ID3D12Heap* GetHeap() const { return m_Heap; }
   5894 
   5895 protected:
   5896     AllocatorPimpl* const m_Allocator;
   5897     const D3D12_HEAP_PROPERTIES m_HeapProps;
   5898     const D3D12_HEAP_FLAGS m_HeapFlags;
   5899     const UINT64 m_Size;
   5900     const UINT m_Id;
   5901 
   5902     HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);
   5903 
   5904 private:
   5905     ID3D12Heap* m_Heap = NULL;
   5906 
   5907     D3D12MA_CLASS_NO_COPY(MemoryBlock)
   5908 };
   5909 #endif // _D3D12MA_MEMORY_BLOCK
   5910 
   5911 #ifndef _D3D12MA_NORMAL_BLOCK
   5912 /*
   5913 Represents a single block of device memory (heap) with all the data about its
   5914 regions (aka suballocations, Allocation), assigned and free.
   5915 Thread-safety: This class must be externally synchronized.
   5916 */
   5917 class NormalBlock : public MemoryBlock
   5918 {
   5919 public:
   5920     BlockMetadata* m_pMetadata;
   5921 
   5922     NormalBlock(
   5923         AllocatorPimpl* allocator,
   5924         BlockVector* blockVector,
   5925         const D3D12_HEAP_PROPERTIES& heapProps,
   5926         D3D12_HEAP_FLAGS heapFlags,
   5927         UINT64 size,
   5928         UINT id);
   5929     virtual ~NormalBlock();
   5930 
   5931     BlockVector* GetBlockVector() const { return m_BlockVector; }
   5932 
   5933     // 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS
   5934     HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);
   5935 
   5936     // Validates all data structures inside this object. If not valid, returns false.
   5937     bool Validate() const;
   5938 
   5939 private:
   5940     BlockVector* m_BlockVector;
   5941 
   5942     D3D12MA_CLASS_NO_COPY(NormalBlock)
   5943 };
   5944 #endif // _D3D12MA_NORMAL_BLOCK
   5945 
   5946 #ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS
   5947 struct CommittedAllocationListItemTraits
   5948 {
   5949     using ItemType = Allocation;
   5950 
   5951     static ItemType* GetPrev(const ItemType* item)
   5952     {
   5953         D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);
   5954         return item->m_Committed.prev;
   5955     }
   5956     static ItemType* GetNext(const ItemType* item)
   5957     {
   5958         D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);
   5959         return item->m_Committed.next;
   5960     }
   5961     static ItemType*& AccessPrev(ItemType* item)
   5962     {
   5963         D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);
   5964         return item->m_Committed.prev;
   5965     }
   5966     static ItemType*& AccessNext(ItemType* item)
   5967     {
   5968         D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);
   5969         return item->m_Committed.next;
   5970     }
   5971 };
   5972 #endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS
   5973 
   5974 #ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST
   5975 /*
   5976 Stores linked list of Allocation objects that are of TYPE_COMMITTED or TYPE_HEAP.
   5977 Thread-safe, synchronized internally.
   5978 */
   5979 class CommittedAllocationList
   5980 {
   5981 public:
   5982     CommittedAllocationList() = default;
   5983     void Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool);
   5984     ~CommittedAllocationList();
   5985 
   5986     D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; }
   5987     PoolPimpl* GetPool() const { return m_Pool; }
   5988     UINT GetMemorySegmentGroup(AllocatorPimpl* allocator) const;
   5989     
   5990     void AddStatistics(Statistics& inoutStats);
   5991     void AddDetailedStatistics(DetailedStatistics& inoutStats);
   5992     // Writes JSON array with the list of allocations.
   5993     void BuildStatsString(JsonWriter& json);
   5994 
   5995     void Register(Allocation* alloc);
   5996     void Unregister(Allocation* alloc);
   5997 
   5998 private:
   5999     using CommittedAllocationLinkedList = IntrusiveLinkedList<CommittedAllocationListItemTraits>;
   6000 
   6001     bool m_UseMutex = true;
   6002     D3D12_HEAP_TYPE m_HeapType = D3D12_HEAP_TYPE_CUSTOM;
   6003     PoolPimpl* m_Pool = NULL;
   6004 
   6005     D3D12MA_RW_MUTEX m_Mutex;
   6006     CommittedAllocationLinkedList m_AllocationList;
   6007 };
   6008 #endif // _D3D12MA_COMMITTED_ALLOCATION_LIST
   6009 
   6010 #ifndef _D3D12M_COMMITTED_ALLOCATION_PARAMETERS
   6011 struct CommittedAllocationParameters
   6012 {
   6013     CommittedAllocationList* m_List = NULL;
   6014     D3D12_HEAP_PROPERTIES m_HeapProperties = {};
   6015     D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE;
   6016     ID3D12ProtectedResourceSession* m_ProtectedSession = NULL;
   6017     bool m_CanAlias = false;
   6018     D3D12_RESIDENCY_PRIORITY m_ResidencyPriority = D3D12_RESIDENCY_PRIORITY_NONE;
   6019 
   6020     bool IsValid() const { return m_List != NULL; }
   6021 };
   6022 #endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS
   6023 
   6024 // Simple variant data structure to hold all possible variations of ID3D12Device*::CreateCommittedResource* and ID3D12Device*::CreatePlacedResource* arguments
   6025 struct CREATE_RESOURCE_PARAMS
   6026 {
   6027     CREATE_RESOURCE_PARAMS() = delete;
   6028     CREATE_RESOURCE_PARAMS(
   6029         const D3D12_RESOURCE_DESC* pResourceDesc, 
   6030         D3D12_RESOURCE_STATES InitialResourceState, 
   6031         const D3D12_CLEAR_VALUE* pOptimizedClearValue)
   6032         : Variant(VARIANT_WITH_STATE)
   6033         , pResourceDesc(pResourceDesc)
   6034         , InitialResourceState(InitialResourceState)
   6035         , pOptimizedClearValue(pOptimizedClearValue)
   6036     {
   6037     }
   6038 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6039     CREATE_RESOURCE_PARAMS(
   6040         const D3D12_RESOURCE_DESC1* pResourceDesc, 
   6041         D3D12_RESOURCE_STATES InitialResourceState, 
   6042         const D3D12_CLEAR_VALUE* pOptimizedClearValue)
   6043         : Variant(VARIANT_WITH_STATE_AND_DESC1)
   6044         , pResourceDesc1(pResourceDesc)
   6045         , InitialResourceState(InitialResourceState)
   6046         , pOptimizedClearValue(pOptimizedClearValue)
   6047     {
   6048     }
   6049 #endif
   6050 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6051     CREATE_RESOURCE_PARAMS(
   6052         const D3D12_RESOURCE_DESC1* pResourceDesc,
   6053         D3D12_BARRIER_LAYOUT InitialLayout,
   6054         const D3D12_CLEAR_VALUE* pOptimizedClearValue,
   6055         UINT32 NumCastableFormats,
   6056         DXGI_FORMAT* pCastableFormats)
   6057         : Variant(VARIANT_WITH_LAYOUT)
   6058         , pResourceDesc1(pResourceDesc)
   6059         , InitialLayout(InitialLayout)
   6060         , pOptimizedClearValue(pOptimizedClearValue)
   6061         , NumCastableFormats(NumCastableFormats)
   6062         , pCastableFormats(pCastableFormats)
   6063     {
   6064     }
   6065 #endif
   6066 
   6067     enum VARIANT
   6068     {
   6069         VARIANT_INVALID = 0,
   6070         VARIANT_WITH_STATE,
   6071         VARIANT_WITH_STATE_AND_DESC1,
   6072         VARIANT_WITH_LAYOUT
   6073     };
   6074 
   6075     VARIANT Variant = VARIANT_INVALID;
   6076 
   6077     const D3D12_RESOURCE_DESC* GetResourceDesc() const
   6078     {
   6079         D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);
   6080         return pResourceDesc;
   6081     }
   6082     const D3D12_RESOURCE_DESC*& AccessResourceDesc()
   6083     {
   6084         D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);
   6085         return pResourceDesc;
   6086     }
   6087     const D3D12_RESOURCE_DESC* GetBaseResourceDesc() const
   6088     {
   6089         // D3D12_RESOURCE_DESC1 can be cast to D3D12_RESOURCE_DESC by discarding the new members at the end.
   6090         return pResourceDesc;
   6091     }
   6092     D3D12_RESOURCE_STATES GetInitialResourceState() const
   6093     {
   6094         D3D12MA_ASSERT(Variant < VARIANT_WITH_LAYOUT);
   6095         return InitialResourceState;
   6096     }
   6097     const D3D12_CLEAR_VALUE* GetOptimizedClearValue() const
   6098     {
   6099         return pOptimizedClearValue;
   6100     }
   6101 
   6102 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6103     const D3D12_RESOURCE_DESC1* GetResourceDesc1() const
   6104     {
   6105         D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);
   6106         return pResourceDesc1;
   6107     }
   6108     const D3D12_RESOURCE_DESC1*& AccessResourceDesc1()
   6109     {
   6110         D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);
   6111         return pResourceDesc1;
   6112     }
   6113 #endif
   6114 
   6115 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6116     D3D12_BARRIER_LAYOUT GetInitialLayout() const
   6117     {
   6118         D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);
   6119         return InitialLayout;
   6120     }
   6121     UINT32 GetNumCastableFormats() const
   6122     {
   6123         D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);
   6124         return NumCastableFormats;
   6125     }
   6126     DXGI_FORMAT* GetCastableFormats() const
   6127     {
   6128         D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);
   6129         return pCastableFormats;
   6130     }
   6131 #endif
   6132 
   6133 private:
   6134     union
   6135     {
   6136         const D3D12_RESOURCE_DESC* pResourceDesc;
   6137 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6138         const D3D12_RESOURCE_DESC1* pResourceDesc1;
   6139 #endif
   6140     };
   6141     union
   6142     {
   6143         D3D12_RESOURCE_STATES InitialResourceState;
   6144 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6145         D3D12_BARRIER_LAYOUT InitialLayout;
   6146 #endif
   6147     };
   6148     const D3D12_CLEAR_VALUE* pOptimizedClearValue;
   6149 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6150     UINT32 NumCastableFormats;
   6151     DXGI_FORMAT* pCastableFormats;
   6152 #endif
   6153 };
   6154 
   6155 #ifndef _D3D12MA_BLOCK_VECTOR
   6156 /*
   6157 Sequence of NormalBlock. Represents memory blocks allocated for a specific
   6158 heap type and possibly resource type (if only Tier 1 is supported).
   6159 
   6160 Synchronized internally with a mutex.
   6161 */
   6162 class BlockVector
   6163 {
   6164     friend class DefragmentationContextPimpl;
   6165     D3D12MA_CLASS_NO_COPY(BlockVector)
   6166 public:
   6167     BlockVector(
   6168         AllocatorPimpl* hAllocator,
   6169         const D3D12_HEAP_PROPERTIES& heapProps,
   6170         D3D12_HEAP_FLAGS heapFlags,
   6171         UINT64 preferredBlockSize,
   6172         size_t minBlockCount,
   6173         size_t maxBlockCount,
   6174         bool explicitBlockSize,
   6175         UINT64 minAllocationAlignment,
   6176         UINT32 algorithm,
   6177         bool denyMsaaTextures,
   6178         ID3D12ProtectedResourceSession* pProtectedSession,
   6179         D3D12_RESIDENCY_PRIORITY residencyPriority);
   6180     ~BlockVector();
   6181     D3D12_RESIDENCY_PRIORITY GetResidencyPriority() const { return m_ResidencyPriority; }
   6182 
   6183     const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }
   6184     D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }
   6185     UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }
   6186     UINT32 GetAlgorithm() const { return m_Algorithm; }
   6187     bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; }
   6188     // To be used only while the m_Mutex is locked. Used during defragmentation.
   6189     size_t GetBlockCount() const { return m_Blocks.size(); }
   6190     // To be used only while the m_Mutex is locked. Used during defragmentation.
   6191     NormalBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
   6192     D3D12MA_RW_MUTEX& GetMutex() { return m_Mutex; }
   6193 
   6194     HRESULT CreateMinBlocks();
   6195     bool IsEmpty();
   6196 
   6197     HRESULT Allocate(
   6198         UINT64 size,
   6199         UINT64 alignment,
   6200         const ALLOCATION_DESC& allocDesc,
   6201         size_t allocationCount,
   6202         Allocation** pAllocations);
   6203 
   6204     void Free(Allocation* hAllocation);
   6205 
   6206     HRESULT CreateResource(
   6207         UINT64 size,
   6208         UINT64 alignment,
   6209         const ALLOCATION_DESC& allocDesc,
   6210         const CREATE_RESOURCE_PARAMS& createParams,
   6211         Allocation** ppAllocation,
   6212         REFIID riidResource,
   6213         void** ppvResource);
   6214 
   6215     void AddStatistics(Statistics& inoutStats);
   6216     void AddDetailedStatistics(DetailedStatistics& inoutStats);
   6217 
   6218     void WriteBlockInfoToJson(JsonWriter& json);
   6219 
   6220 private:
   6221     AllocatorPimpl* const m_hAllocator;
   6222     const D3D12_HEAP_PROPERTIES m_HeapProps;
   6223     const D3D12_HEAP_FLAGS m_HeapFlags;
   6224     const UINT64 m_PreferredBlockSize;
   6225     const size_t m_MinBlockCount;
   6226     const size_t m_MaxBlockCount;
   6227     const bool m_ExplicitBlockSize;
   6228     const UINT64 m_MinAllocationAlignment;
   6229     const UINT32 m_Algorithm;
   6230     const bool m_DenyMsaaTextures;
   6231     ID3D12ProtectedResourceSession* const m_ProtectedSession;
   6232     const D3D12_RESIDENCY_PRIORITY m_ResidencyPriority;
   6233     /* There can be at most one allocation that is completely empty - a
   6234     hysteresis to avoid pessimistic case of alternating creation and destruction
   6235     of a ID3D12Heap. */
   6236     bool m_HasEmptyBlock;
   6237     D3D12MA_RW_MUTEX m_Mutex;
   6238     // Incrementally sorted by sumFreeSize, ascending.
   6239     Vector<NormalBlock*> m_Blocks;
   6240     UINT m_NextBlockId;
   6241     bool m_IncrementalSort = true;
   6242 
   6243     // Disable incremental sorting when freeing allocations
   6244     void SetIncrementalSort(bool val) { m_IncrementalSort = val; }
   6245 
   6246     UINT64 CalcSumBlockSize() const;
   6247     UINT64 CalcMaxBlockSize() const;
   6248 
   6249     // Finds and removes given block from vector.
   6250     void Remove(NormalBlock* pBlock);
   6251 
   6252     // Performs single step in sorting m_Blocks. They may not be fully sorted
   6253     // after this call.
   6254     void IncrementallySortBlocks();
   6255     void SortByFreeSize();
   6256 
   6257     HRESULT AllocatePage(
   6258         UINT64 size,
   6259         UINT64 alignment,
   6260         const ALLOCATION_DESC& allocDesc,
   6261         Allocation** pAllocation);
   6262 
   6263     HRESULT AllocateFromBlock(
   6264         NormalBlock* pBlock,
   6265         UINT64 size,
   6266         UINT64 alignment,
   6267         ALLOCATION_FLAGS allocFlags,
   6268         void* pPrivateData,
   6269         UINT32 strategy,
   6270         Allocation** pAllocation);
   6271 
   6272     HRESULT CommitAllocationRequest(
   6273         AllocationRequest& allocRequest,
   6274         NormalBlock* pBlock,
   6275         UINT64 size,
   6276         UINT64 alignment,
   6277         void* pPrivateData,
   6278         Allocation** pAllocation);
   6279 
   6280     HRESULT CreateBlock(
   6281         UINT64 blockSize,
   6282         size_t* pNewBlockIndex);
   6283 };
   6284 #endif // _D3D12MA_BLOCK_VECTOR
   6285 
   6286 #ifndef _D3D12MA_CURRENT_BUDGET_DATA
   6287 class CurrentBudgetData
   6288 {
   6289 public:
   6290     bool ShouldUpdateBudget() const { return m_OperationsSinceBudgetFetch >= 30; }
   6291 
   6292     void GetStatistics(Statistics& outStats, UINT group) const;
   6293     void GetBudget(bool useMutex,
   6294         UINT64* outLocalUsage, UINT64* outLocalBudget,
   6295         UINT64* outNonLocalUsage, UINT64* outNonLocalBudget);
   6296 
   6297 #if D3D12MA_DXGI_1_4
   6298     HRESULT UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex);
   6299 #endif
   6300 
   6301     void AddAllocation(UINT group, UINT64 allocationBytes);
   6302     void RemoveAllocation(UINT group, UINT64 allocationBytes);
   6303 
   6304     void AddBlock(UINT group, UINT64 blockBytes);
   6305     void RemoveBlock(UINT group, UINT64 blockBytes);
   6306 
   6307 private:
   6308     D3D12MA_ATOMIC_UINT32 m_BlockCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6309     D3D12MA_ATOMIC_UINT32 m_AllocationCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6310     D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6311     D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6312 
   6313     D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = {0};
   6314     D3D12MA_RW_MUTEX m_BudgetMutex;
   6315     UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6316     UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6317     UINT64 m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};
   6318 };
   6319 
   6320 #ifndef _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS
   6321 void CurrentBudgetData::GetStatistics(Statistics& outStats, UINT group) const
   6322 {
   6323     outStats.BlockCount = m_BlockCount[group];
   6324     outStats.AllocationCount = m_AllocationCount[group];
   6325     outStats.BlockBytes = m_BlockBytes[group];
   6326     outStats.AllocationBytes = m_AllocationBytes[group];
   6327 }
   6328 
   6329 void CurrentBudgetData::GetBudget(bool useMutex,
   6330     UINT64* outLocalUsage, UINT64* outLocalBudget,
   6331     UINT64* outNonLocalUsage, UINT64* outNonLocalBudget)
   6332 {
   6333     MutexLockRead lockRead(m_BudgetMutex, useMutex);
   6334 
   6335     if (outLocalUsage)
   6336     {
   6337         const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];
   6338         const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];
   6339         const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];
   6340         *outLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?
   6341             D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;
   6342     }
   6343     if (outLocalBudget)
   6344         *outLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];
   6345 
   6346     if (outNonLocalUsage)
   6347     {
   6348         const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];
   6349         const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];
   6350         const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];
   6351         *outNonLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?
   6352             D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;
   6353     }
   6354     if (outNonLocalBudget)
   6355         *outNonLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];
   6356 }
   6357 
   6358 #if D3D12MA_DXGI_1_4
   6359 HRESULT CurrentBudgetData::UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex)
   6360 {
   6361     D3D12MA_ASSERT(adapter3);
   6362 
   6363     DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};
   6364     DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};
   6365     const HRESULT hrLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);
   6366     const HRESULT hrNonLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);
   6367 
   6368     if (SUCCEEDED(hrLocal) || SUCCEEDED(hrNonLocal))
   6369     {
   6370         MutexLockWrite lockWrite(m_BudgetMutex, useMutex);
   6371 
   6372         if (SUCCEEDED(hrLocal))
   6373         {
   6374             m_D3D12Usage[0] = infoLocal.CurrentUsage;
   6375             m_D3D12Budget[0] = infoLocal.Budget;
   6376         }
   6377         if (SUCCEEDED(hrNonLocal))
   6378         {
   6379             m_D3D12Usage[1] = infoNonLocal.CurrentUsage;
   6380             m_D3D12Budget[1] = infoNonLocal.Budget;
   6381         }
   6382 
   6383         m_BlockBytesAtD3D12Fetch[0] = m_BlockBytes[0];
   6384         m_BlockBytesAtD3D12Fetch[1] = m_BlockBytes[1];
   6385         m_OperationsSinceBudgetFetch = 0;
   6386     }
   6387 
   6388     return FAILED(hrLocal) ? hrLocal : hrNonLocal;
   6389 }
   6390 #endif // #if D3D12MA_DXGI_1_4
   6391 
   6392 void CurrentBudgetData::AddAllocation(UINT group, UINT64 allocationBytes)
   6393 {
   6394     ++m_AllocationCount[group];
   6395     m_AllocationBytes[group] += allocationBytes;
   6396     ++m_OperationsSinceBudgetFetch;
   6397 }
   6398 
   6399 void CurrentBudgetData::RemoveAllocation(UINT group, UINT64 allocationBytes)
   6400 {
   6401     D3D12MA_ASSERT(m_AllocationBytes[group] >= allocationBytes);
   6402     D3D12MA_ASSERT(m_AllocationCount[group] > 0);
   6403     m_AllocationBytes[group] -= allocationBytes;
   6404     --m_AllocationCount[group];
   6405     ++m_OperationsSinceBudgetFetch;
   6406 }
   6407 
   6408 void CurrentBudgetData::AddBlock(UINT group, UINT64 blockBytes)
   6409 {
   6410     ++m_BlockCount[group];
   6411     m_BlockBytes[group] += blockBytes;
   6412     ++m_OperationsSinceBudgetFetch;
   6413 }
   6414 
   6415 void CurrentBudgetData::RemoveBlock(UINT group, UINT64 blockBytes)
   6416 {
   6417     D3D12MA_ASSERT(m_BlockBytes[group] >= blockBytes);
   6418     D3D12MA_ASSERT(m_BlockCount[group] > 0);
   6419     m_BlockBytes[group] -= blockBytes;
   6420     --m_BlockCount[group];
   6421     ++m_OperationsSinceBudgetFetch;
   6422 }
   6423 #endif // _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS
   6424 #endif // _D3D12MA_CURRENT_BUDGET_DATA
   6425 
   6426 #ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL
   6427 class DefragmentationContextPimpl
   6428 {
   6429     D3D12MA_CLASS_NO_COPY(DefragmentationContextPimpl)
   6430 public:
   6431     DefragmentationContextPimpl(
   6432         AllocatorPimpl* hAllocator,
   6433         const DEFRAGMENTATION_DESC& desc,
   6434         BlockVector* poolVector);
   6435     ~DefragmentationContextPimpl();
   6436 
   6437     void GetStats(DEFRAGMENTATION_STATS& outStats) { outStats = m_GlobalStats; }
   6438     const ALLOCATION_CALLBACKS& GetAllocs() const { return m_Moves.GetAllocs(); }
   6439 
   6440     HRESULT DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);
   6441     HRESULT DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);
   6442 
   6443 private:
   6444     // Max number of allocations to ignore due to size constraints before ending single pass
   6445     static const UINT8 MAX_ALLOCS_TO_IGNORE = 16;
   6446     enum class CounterStatus { Pass, Ignore, End };
   6447 
   6448     struct FragmentedBlock
   6449     {
   6450         UINT32 data;
   6451         NormalBlock* block;
   6452     };
   6453     struct StateBalanced
   6454     {
   6455         UINT64 avgFreeSize = 0;
   6456         UINT64 avgAllocSize = UINT64_MAX;
   6457     };
   6458     struct MoveAllocationData
   6459     {
   6460         UINT64 size;
   6461         UINT64 alignment;
   6462         ALLOCATION_FLAGS flags;
   6463         DEFRAGMENTATION_MOVE move = {};
   6464     };
   6465 
   6466     const UINT64 m_MaxPassBytes;
   6467     const UINT32 m_MaxPassAllocations;
   6468 
   6469     Vector<DEFRAGMENTATION_MOVE> m_Moves;
   6470 
   6471     UINT8 m_IgnoredAllocs = 0;
   6472     UINT32 m_Algorithm;
   6473     UINT32 m_BlockVectorCount;
   6474     BlockVector* m_PoolBlockVector;
   6475     BlockVector** m_pBlockVectors;
   6476     size_t m_ImmovableBlockCount = 0;
   6477     DEFRAGMENTATION_STATS m_GlobalStats = { 0 };
   6478     DEFRAGMENTATION_STATS m_PassStats = { 0 };
   6479     void* m_AlgorithmState = NULL;
   6480 
   6481     static MoveAllocationData GetMoveData(AllocHandle handle, BlockMetadata* metadata);
   6482     CounterStatus CheckCounters(UINT64 bytes);
   6483     bool IncrementCounters(UINT64 bytes);
   6484     bool ReallocWithinBlock(BlockVector& vector, NormalBlock* block);
   6485     bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector);
   6486 
   6487     bool ComputeDefragmentation(BlockVector& vector, size_t index);
   6488     bool ComputeDefragmentation_Fast(BlockVector& vector);
   6489     bool ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update);
   6490     bool ComputeDefragmentation_Full(BlockVector& vector);
   6491 
   6492     void UpdateVectorStatistics(BlockVector& vector, StateBalanced& state);
   6493 };
   6494 #endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL
   6495 
   6496 #ifndef _D3D12MA_POOL_PIMPL
   6497 class PoolPimpl
   6498 {
   6499     friend class Allocator;
   6500     friend struct PoolListItemTraits;
   6501 public:
   6502     PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc);
   6503     ~PoolPimpl();
   6504 
   6505     AllocatorPimpl* GetAllocator() const { return m_Allocator; }
   6506     const POOL_DESC& GetDesc() const { return m_Desc; }
   6507     bool SupportsCommittedAllocations() const { return m_Desc.BlockSize == 0; }
   6508     LPCWSTR GetName() const { return m_Name; }
   6509 
   6510     BlockVector* GetBlockVector() { return m_BlockVector; }
   6511     CommittedAllocationList* GetCommittedAllocationList() { return SupportsCommittedAllocations() ? &m_CommittedAllocations : NULL; }
   6512 
   6513     HRESULT Init();
   6514     void GetStatistics(Statistics& outStats);
   6515     void CalculateStatistics(DetailedStatistics& outStats);
   6516     void AddDetailedStatistics(DetailedStatistics& inoutStats);
   6517     void SetName(LPCWSTR Name);
   6518 
   6519 private:
   6520     AllocatorPimpl* m_Allocator; // Externally owned object.
   6521     POOL_DESC m_Desc;
   6522     BlockVector* m_BlockVector; // Owned object.
   6523     CommittedAllocationList m_CommittedAllocations;
   6524     wchar_t* m_Name;
   6525     PoolPimpl* m_PrevPool = NULL;
   6526     PoolPimpl* m_NextPool = NULL;
   6527 
   6528     void FreeName();
   6529 };
   6530 
   6531 struct PoolListItemTraits
   6532 {
   6533     using ItemType = PoolPimpl;
   6534     static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
   6535     static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
   6536     static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
   6537     static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
   6538 };
   6539 #endif // _D3D12MA_POOL_PIMPL
   6540 
   6541 
   6542 #ifndef _D3D12MA_ALLOCATOR_PIMPL
   6543 class AllocatorPimpl
   6544 {
   6545     friend class Allocator;
   6546     friend class Pool;
   6547 public:
   6548     std::atomic_uint32_t m_RefCount = {1};
   6549     CurrentBudgetData m_Budget;
   6550 
   6551     AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);
   6552     ~AllocatorPimpl();
   6553 
   6554     ID3D12Device* GetDevice() const { return m_Device; }
   6555 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   6556     ID3D12Device1* GetDevice1() const { return m_Device1; }
   6557 #endif
   6558 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   6559     ID3D12Device4* GetDevice4() const { return m_Device4; }
   6560 #endif
   6561 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6562     ID3D12Device8* GetDevice8() const { return m_Device8; }
   6563 #endif
   6564     // Shortcut for "Allocation Callbacks", because this function is called so often.
   6565     const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }
   6566     const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; }
   6567     BOOL IsUMA() const { return m_D3D12Architecture.UMA; }
   6568     BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; }
   6569     bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }
   6570     bool UseMutex() const { return m_UseMutex; }
   6571     AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }
   6572     UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
   6573     /*
   6574     If SupportsResourceHeapTier2():
   6575         0: D3D12_HEAP_TYPE_DEFAULT
   6576         1: D3D12_HEAP_TYPE_UPLOAD
   6577         2: D3D12_HEAP_TYPE_READBACK
   6578     else:
   6579         0: D3D12_HEAP_TYPE_DEFAULT + buffer
   6580         1: D3D12_HEAP_TYPE_DEFAULT + texture
   6581         2: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS
   6582         3: D3D12_HEAP_TYPE_UPLOAD + buffer
   6583         4: D3D12_HEAP_TYPE_UPLOAD + texture
   6584         5: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS
   6585         6: D3D12_HEAP_TYPE_READBACK + buffer
   6586         7: D3D12_HEAP_TYPE_READBACK + texture
   6587         8: D3D12_HEAP_TYPE_READBACK + texture RT or DS
   6588     */
   6589     UINT GetDefaultPoolCount() const { return SupportsResourceHeapTier2() ? 3 : 9; }
   6590     BlockVector** GetDefaultPools() { return m_BlockVectors; }
   6591 
   6592     HRESULT Init(const ALLOCATOR_DESC& desc);
   6593     bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;
   6594     UINT StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const;
   6595     UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const;
   6596     UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const;
   6597 
   6598     HRESULT CreatePlacedResourceWrap(
   6599         ID3D12Heap *pHeap,
   6600         UINT64 HeapOffset,
   6601         const CREATE_RESOURCE_PARAMS& createParams,
   6602         REFIID riidResource,
   6603         void** ppvResource);
   6604 
   6605     HRESULT CreateResource(
   6606         const ALLOCATION_DESC* pAllocDesc,
   6607         const CREATE_RESOURCE_PARAMS& createParams,
   6608         Allocation** ppAllocation,
   6609         REFIID riidResource,
   6610         void** ppvResource);
   6611 
   6612     HRESULT CreateAliasingResource(
   6613         Allocation* pAllocation,
   6614         UINT64 AllocationLocalOffset,
   6615         const CREATE_RESOURCE_PARAMS& createParams,
   6616         REFIID riidResource,
   6617         void** ppvResource);
   6618 
   6619     HRESULT AllocateMemory(
   6620         const ALLOCATION_DESC* pAllocDesc,
   6621         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
   6622         Allocation** ppAllocation);
   6623 
   6624     // Unregisters allocation from the collection of dedicated allocations.
   6625     // Allocation object must be deleted externally afterwards.
   6626     void FreeCommittedMemory(Allocation* allocation);
   6627     // Unregisters allocation from the collection of placed allocations.
   6628     // Allocation object must be deleted externally afterwards.
   6629     void FreePlacedMemory(Allocation* allocation);
   6630     // Unregisters allocation from the collection of dedicated allocations and destroys associated heap.
   6631     // Allocation object must be deleted externally afterwards.
   6632     void FreeHeapMemory(Allocation* allocation);
   6633 
   6634     void SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const;
   6635 
   6636     void SetCurrentFrameIndex(UINT frameIndex);
   6637     // For more deailed stats use outCustomHeaps to access statistics divided into L0 and L1 group
   6638     void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2] = NULL);
   6639 
   6640     void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget);
   6641     void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);
   6642 
   6643     void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap);
   6644     void FreeStatsString(WCHAR* pStatsString);
   6645 
   6646 private:
   6647     using PoolList = IntrusiveLinkedList<PoolListItemTraits>;
   6648 
   6649     const bool m_UseMutex;
   6650     const bool m_AlwaysCommitted;
   6651     const bool m_MsaaAlwaysCommitted;
   6652     bool m_DefaultPoolsNotZeroed = false;
   6653     ID3D12Device* m_Device; // AddRef
   6654 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   6655     ID3D12Device1* m_Device1 = NULL; // AddRef, optional
   6656 #endif
   6657 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   6658     ID3D12Device4* m_Device4 = NULL; // AddRef, optional
   6659 #endif
   6660 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6661     ID3D12Device8* m_Device8 = NULL; // AddRef, optional
   6662 #endif
   6663 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6664     ID3D12Device10* m_Device10 = NULL;  // AddRef, optional
   6665 #endif
   6666     IDXGIAdapter* m_Adapter; // AddRef
   6667 #if D3D12MA_DXGI_1_4
   6668     IDXGIAdapter3* m_Adapter3 = NULL; // AddRef, optional
   6669 #endif
   6670     UINT64 m_PreferredBlockSize;
   6671     ALLOCATION_CALLBACKS m_AllocationCallbacks;
   6672     D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;
   6673     DXGI_ADAPTER_DESC m_AdapterDesc;
   6674     D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;
   6675     D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture;
   6676     AllocationObjectAllocator m_AllocationObjectAllocator;
   6677 
   6678     D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT];
   6679     PoolList m_Pools[HEAP_TYPE_COUNT];
   6680     // Default pools.
   6681     BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT];
   6682     CommittedAllocationList m_CommittedAllocations[STANDARD_HEAP_TYPE_COUNT];
   6683 
   6684     /*
   6685     Heuristics that decides whether a resource should better be placed in its own,
   6686     dedicated allocation (committed resource rather than placed resource).
   6687     */
   6688     template<typename D3D12_RESOURCE_DESC_T>
   6689     static bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc);
   6690 
   6691     // Allocates and registers new committed resource with implicit heap, as dedicated allocation.
   6692     // Creates and returns Allocation object and optionally D3D12 resource.
   6693     HRESULT AllocateCommittedResource(
   6694         const CommittedAllocationParameters& committedAllocParams,
   6695         UINT64 resourceSize, bool withinBudget, void* pPrivateData,
   6696         const CREATE_RESOURCE_PARAMS& createParams,
   6697         Allocation** ppAllocation, REFIID riidResource, void** ppvResource);
   6698 
   6699     // Allocates and registers new heap without any resources placed in it, as dedicated allocation.
   6700     // Creates and returns Allocation object.
   6701     HRESULT AllocateHeap(
   6702         const CommittedAllocationParameters& committedAllocParams,
   6703         const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,
   6704         void* pPrivateData, Allocation** ppAllocation);
   6705 
   6706     template<typename D3D12_RESOURCE_DESC_T>
   6707     HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,
   6708         const D3D12_RESOURCE_DESC_T* resDesc, // Optional
   6709         BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted);
   6710 
   6711     // Returns UINT32_MAX if index cannot be calculcated.
   6712     UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const;
   6713     void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;
   6714 
   6715     // Registers Pool object in m_Pools.
   6716     void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);
   6717     // Unregisters Pool object from m_Pools.
   6718     void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);
   6719 
   6720     HRESULT UpdateD3D12Budget();
   6721     
   6722     D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const;
   6723 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6724     D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const;
   6725 #endif
   6726 
   6727     template<typename D3D12_RESOURCE_DESC_T>
   6728     D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const;
   6729 
   6730     bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size);
   6731 
   6732     // Writes object { } with data of given budget.
   6733     static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);
   6734 };
   6735 
   6736 #ifndef _D3D12MA_ALLOCATOR_PIMPL_FUNCTINOS
   6737 AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)
   6738     : m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),
   6739     m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),
   6740     m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0),
   6741     m_Device(desc.pDevice),
   6742     m_Adapter(desc.pAdapter),
   6743     m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),
   6744     m_AllocationCallbacks(allocationCallbacks),
   6745     m_CurrentFrameIndex(0),
   6746     // Below this line don't use allocationCallbacks but m_AllocationCallbacks!!!
   6747     m_AllocationObjectAllocator(m_AllocationCallbacks)
   6748 {
   6749     // desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator.
   6750     ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options));
   6751     ZeroMemory(&m_D3D12Architecture, sizeof(m_D3D12Architecture));
   6752 
   6753     ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors));
   6754 
   6755     for (UINT i = 0; i < STANDARD_HEAP_TYPE_COUNT; ++i)
   6756     {
   6757         m_CommittedAllocations[i].Init(
   6758             m_UseMutex,
   6759             IndexToStandardHeapType(i),
   6760             NULL); // pool
   6761     }
   6762 
   6763     m_Device->AddRef();
   6764     m_Adapter->AddRef();
   6765 }
   6766 
   6767 HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)
   6768 {
   6769 #if D3D12MA_DXGI_1_4
   6770     desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3));
   6771 #endif
   6772 
   6773 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   6774     m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device1));
   6775 #endif
   6776 
   6777 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   6778     m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4));
   6779 #endif
   6780 
   6781 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6782     m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8));
   6783     
   6784     if((desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) != 0)
   6785     {
   6786         D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {};
   6787         if(SUCCEEDED(m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7))))
   6788         {
   6789             // DEFAULT_POOLS_NOT_ZEROED both supported and enabled by the user.
   6790             m_DefaultPoolsNotZeroed = true;
   6791         }
   6792     }
   6793 #endif
   6794 
   6795 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6796     m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device10));
   6797 #endif
   6798 
   6799     HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);
   6800     if (FAILED(hr))
   6801     {
   6802         return hr;
   6803     }
   6804 
   6805     hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));
   6806     if (FAILED(hr))
   6807     {
   6808         return hr;
   6809     }
   6810 #ifdef D3D12MA_FORCE_RESOURCE_HEAP_TIER
   6811     m_D3D12Options.ResourceHeapTier = (D3D12MA_FORCE_RESOURCE_HEAP_TIER);
   6812 #endif
   6813 
   6814     hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture));
   6815     if (FAILED(hr))
   6816     {
   6817         m_D3D12Architecture.UMA = FALSE;
   6818         m_D3D12Architecture.CacheCoherentUMA = FALSE;
   6819     }
   6820 
   6821     D3D12_HEAP_PROPERTIES heapProps = {};
   6822     const UINT defaultPoolCount = GetDefaultPoolCount();
   6823     for (UINT i = 0; i < defaultPoolCount; ++i)
   6824     {
   6825         D3D12_HEAP_FLAGS heapFlags;
   6826         CalcDefaultPoolParams(heapProps.Type, heapFlags, i);
   6827 
   6828 #if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE
   6829         if(m_DefaultPoolsNotZeroed)
   6830         {
   6831             heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
   6832         }
   6833 #endif
   6834 
   6835         m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)(
   6836             this, // hAllocator
   6837             heapProps, // heapType
   6838             heapFlags, // heapFlags
   6839             m_PreferredBlockSize,
   6840             0, // minBlockCount
   6841             SIZE_MAX, // maxBlockCount
   6842             false, // explicitBlockSize
   6843             D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment
   6844             0, // Default algorithm,
   6845             m_MsaaAlwaysCommitted,
   6846             NULL, // pProtectedSession
   6847             D3D12_RESIDENCY_PRIORITY_NONE); // residencyPriority
   6848         // No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.
   6849     }
   6850 
   6851 #if D3D12MA_DXGI_1_4
   6852     UpdateD3D12Budget();
   6853 #endif
   6854 
   6855     return S_OK;
   6856 }
   6857 
   6858 AllocatorPimpl::~AllocatorPimpl()
   6859 {
   6860 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6861     SAFE_RELEASE(m_Device10);
   6862 #endif
   6863 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6864     SAFE_RELEASE(m_Device8);
   6865 #endif
   6866 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   6867     SAFE_RELEASE(m_Device4);
   6868 #endif
   6869 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   6870     SAFE_RELEASE(m_Device1);
   6871 #endif
   6872 #if D3D12MA_DXGI_1_4
   6873     SAFE_RELEASE(m_Adapter3);
   6874 #endif
   6875     SAFE_RELEASE(m_Adapter);
   6876     SAFE_RELEASE(m_Device);
   6877 
   6878     for (UINT i = DEFAULT_POOL_MAX_COUNT; i--; )
   6879     {
   6880         D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);
   6881     }
   6882 
   6883     for (UINT i = HEAP_TYPE_COUNT; i--; )
   6884     {
   6885         if (!m_Pools[i].IsEmpty())
   6886         {
   6887             D3D12MA_ASSERT(0 && "Unfreed pools found!");
   6888         }
   6889     }
   6890 }
   6891 
   6892 bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const
   6893 {
   6894     if (SupportsResourceHeapTier2())
   6895     {
   6896         return true;
   6897     }
   6898     else
   6899     {
   6900         const bool allowBuffers = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;
   6901         const bool allowRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;
   6902         const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;
   6903         const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);
   6904         return allowedGroupCount == 1;
   6905     }
   6906 }
   6907 
   6908 UINT AllocatorPimpl::StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const
   6909 {
   6910     D3D12MA_ASSERT(IsHeapTypeStandard(heapType));
   6911     if (IsUMA())
   6912         return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;
   6913     return heapType == D3D12_HEAP_TYPE_DEFAULT ?
   6914         DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;
   6915 }
   6916 
   6917 UINT AllocatorPimpl::HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const
   6918 {
   6919     if (IsUMA())
   6920         return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;
   6921     if (heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN)
   6922         return StandardHeapTypeToMemorySegmentGroup(heapProps.Type);
   6923     return heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L1 ?
   6924         DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;
   6925 }
   6926 
   6927 UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const
   6928 {
   6929     switch (memorySegmentGroup)
   6930     {
   6931     case DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY:
   6932         return IsUMA() ?
   6933             m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.SharedSystemMemory : m_AdapterDesc.DedicatedVideoMemory;
   6934     case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY:
   6935         return IsUMA() ? 0 : m_AdapterDesc.SharedSystemMemory;
   6936     default:
   6937         D3D12MA_ASSERT(0);
   6938         return UINT64_MAX;
   6939     }
   6940 }
   6941 
   6942 HRESULT AllocatorPimpl::CreatePlacedResourceWrap(
   6943     ID3D12Heap *pHeap,
   6944     UINT64 HeapOffset,
   6945     const CREATE_RESOURCE_PARAMS& createParams,
   6946     REFIID riidResource,
   6947     void** ppvResource)
   6948 {
   6949 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   6950     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)
   6951     {
   6952         if (!m_Device10)
   6953         {
   6954             return E_NOINTERFACE;
   6955         }
   6956         return m_Device10->CreatePlacedResource2(pHeap, HeapOffset,
   6957             createParams.GetResourceDesc1(), createParams.GetInitialLayout(),
   6958             createParams.GetOptimizedClearValue(), createParams.GetNumCastableFormats(),
   6959             createParams.GetCastableFormats(), riidResource, ppvResource);
   6960     } else
   6961 #endif
   6962 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   6963     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
   6964     {
   6965         if (!m_Device8)
   6966         {
   6967             return E_NOINTERFACE;
   6968         }
   6969         return m_Device8->CreatePlacedResource1(pHeap, HeapOffset,
   6970             createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),
   6971             createParams.GetOptimizedClearValue(), riidResource, ppvResource);
   6972     } else 
   6973 #endif
   6974     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)
   6975     {
   6976         return m_Device->CreatePlacedResource(pHeap, HeapOffset,
   6977             createParams.GetResourceDesc(), createParams.GetInitialResourceState(),
   6978             createParams.GetOptimizedClearValue(), riidResource, ppvResource);
   6979     }
   6980     else
   6981     {
   6982         D3D12MA_ASSERT(0);
   6983         return E_INVALIDARG;
   6984     }
   6985 }
   6986 
   6987 
   6988 HRESULT AllocatorPimpl::CreateResource(
   6989     const ALLOCATION_DESC* pAllocDesc,
   6990     const CREATE_RESOURCE_PARAMS& createParams,
   6991     Allocation** ppAllocation,
   6992     REFIID riidResource,
   6993     void** ppvResource)
   6994 {
   6995     D3D12MA_ASSERT(pAllocDesc && createParams.GetBaseResourceDesc() && ppAllocation);
   6996 
   6997     *ppAllocation = NULL;
   6998     if (ppvResource)
   6999     {
   7000         *ppvResource = NULL;
   7001     }
   7002 
   7003     CREATE_RESOURCE_PARAMS finalCreateParams = createParams;
   7004     D3D12_RESOURCE_DESC finalResourceDesc;
   7005 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7006     D3D12_RESOURCE_DESC1 finalResourceDesc1;
   7007 #endif
   7008     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;
   7009     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)
   7010     {
   7011         finalResourceDesc = *createParams.GetResourceDesc();
   7012         finalCreateParams.AccessResourceDesc() = &finalResourceDesc;
   7013         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);
   7014     }
   7015 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7016     else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
   7017     {
   7018         if (!m_Device8)
   7019         {
   7020             return E_NOINTERFACE;
   7021         }
   7022         finalResourceDesc1 = *createParams.GetResourceDesc1();
   7023         finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
   7024         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);
   7025     }
   7026 #endif
   7027 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   7028     else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)
   7029     {
   7030         if (!m_Device10)
   7031         {
   7032             return E_NOINTERFACE;
   7033         }
   7034         finalResourceDesc1 = *createParams.GetResourceDesc1();
   7035         finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
   7036         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);
   7037     }
   7038 #endif
   7039     else
   7040     {
   7041         D3D12MA_ASSERT(0);
   7042         return E_INVALIDARG;
   7043     }
   7044     D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));
   7045     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);
   7046 
   7047     BlockVector* blockVector = NULL;
   7048     CommittedAllocationParameters committedAllocationParams = {};
   7049     bool preferCommitted = false;
   7050     
   7051     HRESULT hr;
   7052 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7053     if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
   7054     {
   7055         hr = CalcAllocationParams<D3D12_RESOURCE_DESC1>(*pAllocDesc, resAllocInfo.SizeInBytes,
   7056             createParams.GetResourceDesc1(),
   7057             blockVector, committedAllocationParams, preferCommitted);
   7058     }
   7059     else
   7060 #endif
   7061     {
   7062         hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, resAllocInfo.SizeInBytes,
   7063             createParams.GetResourceDesc(),
   7064             blockVector, committedAllocationParams, preferCommitted);
   7065     }
   7066     if (FAILED(hr))
   7067         return hr;
   7068 
   7069     const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;
   7070     hr = E_INVALIDARG;
   7071     if (committedAllocationParams.IsValid() && preferCommitted)
   7072     {
   7073         hr = AllocateCommittedResource(committedAllocationParams,
   7074             resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,
   7075             finalCreateParams, ppAllocation, riidResource, ppvResource);
   7076         if (SUCCEEDED(hr))
   7077             return hr;
   7078     }
   7079     if (blockVector != NULL)
   7080     {
   7081         hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment,
   7082             *pAllocDesc, finalCreateParams,
   7083             ppAllocation, riidResource, ppvResource);
   7084         if (SUCCEEDED(hr))
   7085             return hr;
   7086     }
   7087     if (committedAllocationParams.IsValid() && !preferCommitted)
   7088     {
   7089         hr = AllocateCommittedResource(committedAllocationParams,
   7090             resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,
   7091             finalCreateParams, ppAllocation, riidResource, ppvResource);
   7092         if (SUCCEEDED(hr))
   7093             return hr;
   7094     }
   7095     return hr;
   7096 }
   7097 
   7098 HRESULT AllocatorPimpl::AllocateMemory(
   7099     const ALLOCATION_DESC* pAllocDesc,
   7100     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
   7101     Allocation** ppAllocation)
   7102 {
   7103     *ppAllocation = NULL;
   7104 
   7105     BlockVector* blockVector = NULL;
   7106     CommittedAllocationParameters committedAllocationParams = {};
   7107     bool preferCommitted = false;
   7108     HRESULT hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, pAllocInfo->SizeInBytes,
   7109         NULL, // pResDesc
   7110         blockVector, committedAllocationParams, preferCommitted);
   7111     if (FAILED(hr))
   7112         return hr;
   7113 
   7114     const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;
   7115     hr = E_INVALIDARG;
   7116     if (committedAllocationParams.IsValid() && preferCommitted)
   7117     {
   7118         hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);
   7119         if (SUCCEEDED(hr))
   7120             return hr;
   7121     }
   7122     if (blockVector != NULL)
   7123     {
   7124         hr = blockVector->Allocate(pAllocInfo->SizeInBytes, pAllocInfo->Alignment,
   7125             *pAllocDesc, 1, (Allocation**)ppAllocation);
   7126         if (SUCCEEDED(hr))
   7127             return hr;
   7128     }
   7129     if (committedAllocationParams.IsValid() && !preferCommitted)
   7130     {
   7131         hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);
   7132         if (SUCCEEDED(hr))
   7133             return hr;
   7134     }
   7135     return hr;
   7136 }
   7137 
   7138 HRESULT AllocatorPimpl::CreateAliasingResource(
   7139     Allocation* pAllocation,
   7140     UINT64 AllocationLocalOffset,
   7141     const CREATE_RESOURCE_PARAMS& createParams,
   7142     REFIID riidResource,
   7143     void** ppvResource)
   7144 {
   7145     *ppvResource = NULL;
   7146 
   7147     CREATE_RESOURCE_PARAMS finalCreateParams = createParams;
   7148     D3D12_RESOURCE_DESC finalResourceDesc;
   7149 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7150     D3D12_RESOURCE_DESC1 finalResourceDesc1;
   7151 #endif
   7152     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;
   7153     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)
   7154     {
   7155         finalResourceDesc = *createParams.GetResourceDesc();
   7156         finalCreateParams.AccessResourceDesc() = &finalResourceDesc;
   7157         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);
   7158     }
   7159 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7160     else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
   7161     {
   7162         if (!m_Device8)
   7163         {
   7164             return E_NOINTERFACE;
   7165         }
   7166         finalResourceDesc1 = *createParams.GetResourceDesc1();
   7167         finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
   7168         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);
   7169     }
   7170 #endif
   7171 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   7172     else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)
   7173     {
   7174         if (!m_Device10)
   7175         {
   7176             return E_NOINTERFACE;
   7177         }
   7178         finalResourceDesc1 = *createParams.GetResourceDesc1();
   7179         finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
   7180         resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);
   7181     }
   7182 #endif
   7183     else
   7184     {
   7185         D3D12MA_ASSERT(0);
   7186         return E_INVALIDARG;
   7187     }
   7188     D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));
   7189     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);
   7190 
   7191     ID3D12Heap* const existingHeap = pAllocation->GetHeap();
   7192     const UINT64 existingOffset = pAllocation->GetOffset();
   7193     const UINT64 existingSize = pAllocation->GetSize();
   7194     const UINT64 newOffset = existingOffset + AllocationLocalOffset;
   7195 
   7196     if (existingHeap == NULL ||
   7197         AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||
   7198         newOffset % resAllocInfo.Alignment != 0)
   7199     {
   7200         return E_INVALIDARG;
   7201     }
   7202 
   7203     return CreatePlacedResourceWrap(existingHeap, newOffset, finalCreateParams, riidResource, ppvResource);
   7204 }
   7205 
   7206 void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)
   7207 {
   7208     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED);
   7209 
   7210     CommittedAllocationList* const allocList = allocation->m_Committed.list;
   7211     allocList->Unregister(allocation);
   7212 
   7213     const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);
   7214     const UINT64 allocSize = allocation->GetSize();
   7215     m_Budget.RemoveAllocation(memSegmentGroup, allocSize);
   7216     m_Budget.RemoveBlock(memSegmentGroup, allocSize);
   7217 }
   7218 
   7219 void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)
   7220 {
   7221     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED);
   7222 
   7223     NormalBlock* const block = allocation->m_Placed.block;
   7224     D3D12MA_ASSERT(block);
   7225     BlockVector* const blockVector = block->GetBlockVector();
   7226     D3D12MA_ASSERT(blockVector);
   7227     m_Budget.RemoveAllocation(HeapPropertiesToMemorySegmentGroup(block->GetHeapProperties()), allocation->GetSize());
   7228     blockVector->Free(allocation);
   7229 }
   7230 
   7231 void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)
   7232 {
   7233     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP);
   7234 
   7235     CommittedAllocationList* const allocList = allocation->m_Committed.list;
   7236     allocList->Unregister(allocation);
   7237     SAFE_RELEASE(allocation->m_Heap.heap);
   7238 
   7239     const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);
   7240     const UINT64 allocSize = allocation->GetSize();
   7241     m_Budget.RemoveAllocation(memSegmentGroup, allocSize);
   7242     m_Budget.RemoveBlock(memSegmentGroup, allocSize);
   7243 }
   7244 
   7245 void AllocatorPimpl::SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const
   7246 {
   7247 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   7248     if (priority != D3D12_RESIDENCY_PRIORITY_NONE && m_Device1)
   7249     {
   7250         // Intentionally ignoring the result.
   7251         m_Device1->SetResidencyPriority(1, &obj, &priority);
   7252     }
   7253 #endif
   7254 }
   7255 
   7256 void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)
   7257 {
   7258     m_CurrentFrameIndex.store(frameIndex);
   7259 
   7260 #if D3D12MA_DXGI_1_4
   7261     UpdateD3D12Budget();
   7262 #endif
   7263 }
   7264 
   7265 void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2])
   7266 {
   7267     // Init stats
   7268     for (size_t i = 0; i < HEAP_TYPE_COUNT; i++)
   7269         ClearDetailedStatistics(outStats.HeapType[i]);
   7270     for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++)
   7271         ClearDetailedStatistics(outStats.MemorySegmentGroup[i]);
   7272     ClearDetailedStatistics(outStats.Total);
   7273     if (outCustomHeaps)
   7274     {
   7275         ClearDetailedStatistics(outCustomHeaps[0]);
   7276         ClearDetailedStatistics(outCustomHeaps[1]);
   7277     }
   7278 
   7279     // Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i].
   7280     if (SupportsResourceHeapTier2())
   7281     {
   7282         // DEFAULT, UPLOAD, READBACK.
   7283         for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)
   7284         {
   7285             BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex];
   7286             D3D12MA_ASSERT(pBlockVector);
   7287             pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);
   7288         }
   7289     }
   7290     else
   7291     {
   7292         // DEFAULT, UPLOAD, READBACK.
   7293         for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)
   7294         {
   7295             for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)
   7296             {
   7297                 BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex * 3 + heapSubType];
   7298                 D3D12MA_ASSERT(pBlockVector);
   7299                 pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);
   7300             }
   7301         }
   7302     }
   7303 
   7304     // Sum them up to memory segment groups.
   7305     AddDetailedStatistics(
   7306         outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_DEFAULT)],
   7307         outStats.HeapType[0]);
   7308     AddDetailedStatistics(
   7309         outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_UPLOAD)],
   7310         outStats.HeapType[1]);
   7311     AddDetailedStatistics(
   7312         outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_READBACK)],
   7313         outStats.HeapType[2]);
   7314 
   7315     // Process custom pools.
   7316     DetailedStatistics tmpStats;
   7317     for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)
   7318     {
   7319         MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
   7320         PoolList& poolList = m_Pools[heapTypeIndex];
   7321         for (PoolPimpl* pool = poolList.Front(); pool != NULL; pool = poolList.GetNext(pool))
   7322         {
   7323             const D3D12_HEAP_PROPERTIES& poolHeapProps = pool->GetDesc().HeapProperties;
   7324             ClearDetailedStatistics(tmpStats);
   7325             pool->AddDetailedStatistics(tmpStats);
   7326             AddDetailedStatistics(
   7327                 outStats.HeapType[heapTypeIndex], tmpStats);
   7328 
   7329             UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps);
   7330             AddDetailedStatistics(
   7331                 outStats.MemorySegmentGroup[memorySegment], tmpStats);
   7332 
   7333             if (outCustomHeaps)
   7334                 AddDetailedStatistics(outCustomHeaps[memorySegment], tmpStats);
   7335         }
   7336     }
   7337 
   7338     // Process committed allocations. 3 standard heap types only.
   7339     for (UINT heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)
   7340     {
   7341         ClearDetailedStatistics(tmpStats);
   7342         m_CommittedAllocations[heapTypeIndex].AddDetailedStatistics(tmpStats);
   7343         AddDetailedStatistics(
   7344             outStats.HeapType[heapTypeIndex], tmpStats);
   7345         AddDetailedStatistics(
   7346             outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToStandardHeapType(heapTypeIndex))], tmpStats);
   7347     }
   7348 
   7349     // Sum up memory segment groups to totals.
   7350     AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[0]);
   7351     AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[1]);
   7352 
   7353     D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==
   7354         outStats.MemorySegmentGroup[0].Stats.BlockCount + outStats.MemorySegmentGroup[1].Stats.BlockCount);
   7355     D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==
   7356         outStats.MemorySegmentGroup[0].Stats.AllocationCount + outStats.MemorySegmentGroup[1].Stats.AllocationCount);
   7357     D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==
   7358         outStats.MemorySegmentGroup[0].Stats.BlockBytes + outStats.MemorySegmentGroup[1].Stats.BlockBytes);
   7359     D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==
   7360         outStats.MemorySegmentGroup[0].Stats.AllocationBytes + outStats.MemorySegmentGroup[1].Stats.AllocationBytes);
   7361     D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==
   7362         outStats.MemorySegmentGroup[0].UnusedRangeCount + outStats.MemorySegmentGroup[1].UnusedRangeCount);
   7363 
   7364     D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==
   7365         outStats.HeapType[0].Stats.BlockCount + outStats.HeapType[1].Stats.BlockCount +
   7366         outStats.HeapType[2].Stats.BlockCount + outStats.HeapType[3].Stats.BlockCount);
   7367     D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==
   7368         outStats.HeapType[0].Stats.AllocationCount + outStats.HeapType[1].Stats.AllocationCount +
   7369         outStats.HeapType[2].Stats.AllocationCount + outStats.HeapType[3].Stats.AllocationCount);
   7370     D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==
   7371         outStats.HeapType[0].Stats.BlockBytes + outStats.HeapType[1].Stats.BlockBytes +
   7372         outStats.HeapType[2].Stats.BlockBytes + outStats.HeapType[3].Stats.BlockBytes);
   7373     D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==
   7374         outStats.HeapType[0].Stats.AllocationBytes + outStats.HeapType[1].Stats.AllocationBytes +
   7375         outStats.HeapType[2].Stats.AllocationBytes + outStats.HeapType[3].Stats.AllocationBytes);
   7376     D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==
   7377         outStats.HeapType[0].UnusedRangeCount + outStats.HeapType[1].UnusedRangeCount +
   7378         outStats.HeapType[2].UnusedRangeCount + outStats.HeapType[3].UnusedRangeCount);
   7379 }
   7380 
   7381 void AllocatorPimpl::GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget)
   7382 {
   7383     if (outLocalBudget)
   7384         m_Budget.GetStatistics(outLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY);
   7385     if (outNonLocalBudget)
   7386         m_Budget.GetStatistics(outNonLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY);
   7387 
   7388 #if D3D12MA_DXGI_1_4
   7389     if (m_Adapter3)
   7390     {
   7391         if (!m_Budget.ShouldUpdateBudget())
   7392         {
   7393             m_Budget.GetBudget(m_UseMutex,
   7394                 outLocalBudget ? &outLocalBudget->UsageBytes : NULL,
   7395                 outLocalBudget ? &outLocalBudget->BudgetBytes : NULL,
   7396                 outNonLocalBudget ? &outNonLocalBudget->UsageBytes : NULL,
   7397                 outNonLocalBudget ? &outNonLocalBudget->BudgetBytes : NULL);
   7398         }
   7399         else
   7400         {
   7401             UpdateD3D12Budget();
   7402             GetBudget(outLocalBudget, outNonLocalBudget); // Recursion
   7403         }
   7404     }
   7405     else
   7406 #endif
   7407     {
   7408         if (outLocalBudget)
   7409         {
   7410             outLocalBudget->UsageBytes = outLocalBudget->Stats.BlockBytes;
   7411             outLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY) * 8 / 10; // 80% heuristics.
   7412         }
   7413         if (outNonLocalBudget)
   7414         {
   7415             outNonLocalBudget->UsageBytes = outNonLocalBudget->Stats.BlockBytes;
   7416             outNonLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY) * 8 / 10; // 80% heuristics.
   7417         }
   7418     }
   7419 }
   7420 
   7421 void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)
   7422 {
   7423     switch (heapType)
   7424     {
   7425     case D3D12_HEAP_TYPE_DEFAULT:
   7426         GetBudget(&outBudget, NULL);
   7427         break;
   7428     case D3D12_HEAP_TYPE_UPLOAD:
   7429     case D3D12_HEAP_TYPE_READBACK:
   7430         GetBudget(NULL, &outBudget);
   7431         break;
   7432     default: D3D12MA_ASSERT(0);
   7433     }
   7434 }
   7435 
   7436 void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
   7437 {
   7438     StringBuilder sb(GetAllocs());
   7439     {
   7440         Budget localBudget = {}, nonLocalBudget = {};
   7441         GetBudget(&localBudget, &nonLocalBudget);
   7442 
   7443         TotalStatistics stats;
   7444         DetailedStatistics customHeaps[2];
   7445         CalculateStatistics(stats, customHeaps);
   7446 
   7447         JsonWriter json(GetAllocs(), sb);
   7448         json.BeginObject();
   7449         {
   7450             json.WriteString(L"General");
   7451             json.BeginObject();
   7452             {
   7453                 json.WriteString(L"API");
   7454                 json.WriteString(L"Direct3D 12");
   7455 
   7456                 json.WriteString(L"GPU");
   7457                 json.WriteString(m_AdapterDesc.Description);
   7458 
   7459                 json.WriteString(L"DedicatedVideoMemory");
   7460                 json.WriteNumber((UINT64)m_AdapterDesc.DedicatedVideoMemory);
   7461                 json.WriteString(L"DedicatedSystemMemory");
   7462                 json.WriteNumber((UINT64)m_AdapterDesc.DedicatedSystemMemory);
   7463                 json.WriteString(L"SharedSystemMemory");
   7464                 json.WriteNumber((UINT64)m_AdapterDesc.SharedSystemMemory);
   7465                 
   7466                 json.WriteString(L"ResourceHeapTier");
   7467                 json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceHeapTier));
   7468 
   7469                 json.WriteString(L"ResourceBindingTier");
   7470                 json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceBindingTier));
   7471 
   7472                 json.WriteString(L"TiledResourcesTier");
   7473                 json.WriteNumber(static_cast<UINT>(m_D3D12Options.TiledResourcesTier));
   7474 
   7475                 json.WriteString(L"TileBasedRenderer");
   7476                 json.WriteBool(m_D3D12Architecture.TileBasedRenderer);
   7477 
   7478                 json.WriteString(L"UMA");
   7479                 json.WriteBool(m_D3D12Architecture.UMA);
   7480                 json.WriteString(L"CacheCoherentUMA");
   7481                 json.WriteBool(m_D3D12Architecture.CacheCoherentUMA);
   7482             }
   7483             json.EndObject();
   7484         }
   7485         {
   7486             json.WriteString(L"Total");
   7487             json.AddDetailedStatisticsInfoObject(stats.Total);
   7488         }
   7489         {
   7490             json.WriteString(L"MemoryInfo");
   7491             json.BeginObject();
   7492             {
   7493                 json.WriteString(L"L0");
   7494                 json.BeginObject();
   7495                 {
   7496                     json.WriteString(L"Budget");
   7497                     WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local
   7498 
   7499                     json.WriteString(L"Stats");
   7500                     json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]);
   7501 
   7502                     json.WriteString(L"MemoryPools");
   7503                     json.BeginObject();
   7504                     {
   7505                         if (IsUMA())
   7506                         {
   7507                             json.WriteString(L"DEFAULT");
   7508                             json.BeginObject();
   7509                             {
   7510                                 json.WriteString(L"Stats");
   7511                                 json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
   7512                             }
   7513                             json.EndObject();
   7514                         }
   7515                         json.WriteString(L"UPLOAD");
   7516                         json.BeginObject();
   7517                         {
   7518                             json.WriteString(L"Stats");
   7519                             json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);
   7520                         }
   7521                         json.EndObject();
   7522 
   7523                         json.WriteString(L"READBACK");
   7524                         json.BeginObject();
   7525                         {
   7526                             json.WriteString(L"Stats");
   7527                             json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);
   7528                         }
   7529                         json.EndObject();
   7530 
   7531                         json.WriteString(L"CUSTOM");
   7532                         json.BeginObject();
   7533                         {
   7534                             json.WriteString(L"Stats");
   7535                             json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);
   7536                         }
   7537                         json.EndObject();
   7538                     }
   7539                     json.EndObject();
   7540                 }
   7541                 json.EndObject();
   7542                 if (!IsUMA())
   7543                 {
   7544                     json.WriteString(L"L1");
   7545                     json.BeginObject();
   7546                     {
   7547                         json.WriteString(L"Budget");
   7548                         WriteBudgetToJson(json, localBudget);
   7549 
   7550                         json.WriteString(L"Stats");
   7551                         json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]);
   7552 
   7553                         json.WriteString(L"MemoryPools");
   7554                         json.BeginObject();
   7555                         {
   7556                             json.WriteString(L"DEFAULT");
   7557                             json.BeginObject();
   7558                             {
   7559                                 json.WriteString(L"Stats");
   7560                                 json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
   7561                             }
   7562                             json.EndObject();
   7563 
   7564                             json.WriteString(L"CUSTOM");
   7565                             json.BeginObject();
   7566                             {
   7567                                 json.WriteString(L"Stats");
   7568                                 json.AddDetailedStatisticsInfoObject(customHeaps[0]);
   7569                             }
   7570                             json.EndObject();
   7571                         }
   7572                         json.EndObject();
   7573                     }
   7574                     json.EndObject();
   7575                 }
   7576             }
   7577             json.EndObject();
   7578         }
   7579 
   7580         if (detailedMap)
   7581         {
   7582             const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap)
   7583             {
   7584                 D3D12MA_ASSERT(blockVector);
   7585 
   7586                 D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags();
   7587                 json.WriteString(L"Flags");
   7588                 json.BeginArray(true);
   7589                 {
   7590                     if (flags & D3D12_HEAP_FLAG_SHARED)
   7591                         json.WriteString(L"HEAP_FLAG_SHARED");
   7592                     if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY)
   7593                         json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY");
   7594                     if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER)
   7595                         json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER");
   7596                     if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED)
   7597                         json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED");
   7598                     if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)
   7599                         json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH");
   7600                     if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS)
   7601                         json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS");
   7602 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7603                     if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)
   7604                         json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT");
   7605                     if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED)
   7606                         json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED");
   7607 #endif
   7608 
   7609                     if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS)
   7610                         json.WriteString(L"HEAP_FLAG_DENY_BUFFERS");
   7611                     if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES)
   7612                         json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES");
   7613                     if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES)
   7614                         json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES");
   7615 
   7616                     flags &= ~(D3D12_HEAP_FLAG_SHARED
   7617                         | D3D12_HEAP_FLAG_DENY_BUFFERS
   7618                         | D3D12_HEAP_FLAG_ALLOW_DISPLAY
   7619                         | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER
   7620                         | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES
   7621                         | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES
   7622                         | D3D12_HEAP_FLAG_HARDWARE_PROTECTED
   7623                         | D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH
   7624                         | D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS);
   7625 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7626                     flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT
   7627                         | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED);
   7628 #endif
   7629                     if (flags != 0)
   7630                         json.WriteNumber((UINT)flags);
   7631 
   7632                     if (customHeap)
   7633                     {
   7634                         const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties();
   7635                         switch (properties.MemoryPoolPreference)
   7636                         {
   7637                         default:
   7638                             D3D12MA_ASSERT(0);
   7639                         case D3D12_MEMORY_POOL_UNKNOWN:
   7640                             json.WriteString(L"MEMORY_POOL_UNKNOWN");
   7641                             break;
   7642                         case D3D12_MEMORY_POOL_L0:
   7643                             json.WriteString(L"MEMORY_POOL_L0");
   7644                             break;
   7645                         case D3D12_MEMORY_POOL_L1:
   7646                             json.WriteString(L"MEMORY_POOL_L1");
   7647                             break;
   7648                         }
   7649                         switch (properties.CPUPageProperty)
   7650                         {
   7651                         default:
   7652                             D3D12MA_ASSERT(0);
   7653                         case D3D12_CPU_PAGE_PROPERTY_UNKNOWN:
   7654                             json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN");
   7655                             break;
   7656                         case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE:
   7657                             json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE");
   7658                             break;
   7659                         case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE:
   7660                             json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE");
   7661                             break;
   7662                         case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK:
   7663                             json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK");
   7664                             break;
   7665                         }
   7666                     }
   7667                 }
   7668                 json.EndArray();
   7669 
   7670                 json.WriteString(L"PreferredBlockSize");
   7671                 json.WriteNumber(blockVector->GetPreferredBlockSize());
   7672 
   7673                 json.WriteString(L"Blocks");
   7674                 blockVector->WriteBlockInfoToJson(json);
   7675 
   7676                 json.WriteString(L"DedicatedAllocations");
   7677                 json.BeginArray();
   7678                 if (committedAllocs)
   7679                     committedAllocs->BuildStatsString(json);
   7680                 json.EndArray();
   7681             };
   7682 
   7683             json.WriteString(L"DefaultPools");
   7684             json.BeginObject();
   7685             {
   7686                 if (SupportsResourceHeapTier2())
   7687                 {
   7688                     for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)
   7689                     {
   7690                         json.WriteString(StandardHeapTypeNames[heapType]);
   7691                         json.BeginObject();
   7692                         writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false);
   7693                         json.EndObject();
   7694                     }
   7695                 }
   7696                 else
   7697                 {
   7698                     for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)
   7699                     {
   7700                         for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType)
   7701                         {
   7702                             static const WCHAR* const heapSubTypeName[] = {
   7703                                 L" - Buffers",
   7704                                 L" - Textures",
   7705                                 L" - Textures RT/DS",
   7706                             };
   7707                             json.BeginString(StandardHeapTypeNames[heapType]);
   7708                             json.EndString(heapSubTypeName[heapSubType]);
   7709 
   7710                             json.BeginObject();
   7711                             writeHeapInfo(m_BlockVectors[heapType * 3 + heapSubType], m_CommittedAllocations + heapType, false);
   7712                             json.EndObject();
   7713                         }
   7714                     }
   7715                 }
   7716             }
   7717             json.EndObject();
   7718 
   7719             json.WriteString(L"CustomPools");
   7720             json.BeginObject();
   7721             for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)
   7722             {
   7723                 MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);
   7724                 auto* item = m_Pools[heapTypeIndex].Front();
   7725                 if (item != NULL)
   7726                 {
   7727                     size_t index = 0;
   7728                     json.WriteString(HeapTypeNames[heapTypeIndex]);
   7729                     json.BeginArray();
   7730                     do
   7731                     {
   7732                         json.BeginObject();
   7733                         json.WriteString(L"Name");
   7734                         json.BeginString();
   7735                         json.ContinueString(index++);
   7736                         if (item->GetName())
   7737                         {
   7738                             json.ContinueString(L" - ");
   7739                             json.ContinueString(item->GetName());
   7740                         }
   7741                         json.EndString();
   7742 
   7743                         writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3);
   7744                         json.EndObject();
   7745                     } while ((item = PoolList::GetNext(item)) != NULL);
   7746                     json.EndArray();
   7747                 }
   7748             }
   7749             json.EndObject();
   7750         }
   7751         json.EndObject();
   7752     }
   7753 
   7754     const size_t length = sb.GetLength();
   7755     WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 2);
   7756     result[0] = 0xFEFF;
   7757     memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR));
   7758     result[length + 1] = L'\0';
   7759     *ppStatsString = result;
   7760 }
   7761 
   7762 void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)
   7763 {
   7764     D3D12MA_ASSERT(pStatsString);
   7765     Free(GetAllocs(), pStatsString);
   7766 }
   7767 
   7768 template<typename D3D12_RESOURCE_DESC_T>
   7769 bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc)
   7770 {
   7771     // Intentional. It may change in the future.
   7772     return false;
   7773 }
   7774 
   7775 HRESULT AllocatorPimpl::AllocateCommittedResource(
   7776     const CommittedAllocationParameters& committedAllocParams,
   7777     UINT64 resourceSize, bool withinBudget, void* pPrivateData,
   7778     const CREATE_RESOURCE_PARAMS& createParams,
   7779     Allocation** ppAllocation, REFIID riidResource, void** ppvResource)
   7780 {
   7781     D3D12MA_ASSERT(committedAllocParams.IsValid());
   7782 
   7783     HRESULT hr;
   7784     ID3D12Resource* res = NULL;
   7785     // Allocate aliasing memory with explicit heap
   7786     if (committedAllocParams.m_CanAlias)
   7787     {
   7788         D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {};
   7789         heapAllocInfo.SizeInBytes = resourceSize;
   7790         heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted);
   7791         hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation);
   7792         if (SUCCEEDED(hr))
   7793         {
   7794             hr = CreatePlacedResourceWrap((*ppAllocation)->GetHeap(), 0, 
   7795                     createParams, D3D12MA_IID_PPV_ARGS(&res));
   7796             if (SUCCEEDED(hr))
   7797             {
   7798                 if (ppvResource != NULL)
   7799                     hr = res->QueryInterface(riidResource, ppvResource);
   7800                 if (SUCCEEDED(hr))
   7801                 {
   7802                     (*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());
   7803                     return hr;
   7804                 }
   7805                 res->Release();
   7806             }
   7807             FreeHeapMemory(*ppAllocation);
   7808         }
   7809         return hr;
   7810     }
   7811 
   7812     if (withinBudget &&
   7813         !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize))
   7814     {
   7815         return E_OUTOFMEMORY;
   7816     }
   7817 
   7818     /* D3D12 ERROR:
   7819      * ID3D12Device::CreateCommittedResource:
   7820      * When creating a committed resource, D3D12_HEAP_FLAGS must not have either
   7821      *      D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES,
   7822      *      D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES,
   7823      *      nor D3D12_HEAP_FLAG_DENY_BUFFERS set.
   7824      * These flags will be set automatically to correspond with the committed resource type.
   7825      *
   7826      * [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS]
   7827     */
   7828 
   7829 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
   7830     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)
   7831     {
   7832         if (!m_Device10)
   7833         {
   7834             return E_NOINTERFACE;
   7835         }
   7836         hr = m_Device10->CreateCommittedResource3(
   7837                 &committedAllocParams.m_HeapProperties,
   7838                 committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,
   7839                 createParams.GetResourceDesc1(), createParams.GetInitialLayout(),
   7840                 createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,
   7841                 createParams.GetNumCastableFormats(), createParams.GetCastableFormats(),
   7842                 D3D12MA_IID_PPV_ARGS(&res));
   7843     } else
   7844 #endif
   7845 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   7846     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
   7847     {
   7848         if (!m_Device8)
   7849         {
   7850             return E_NOINTERFACE;
   7851         }
   7852         hr = m_Device8->CreateCommittedResource2(
   7853                 &committedAllocParams.m_HeapProperties,
   7854                 committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,
   7855                 createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),
   7856                 createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,
   7857                 D3D12MA_IID_PPV_ARGS(&res));
   7858     } else
   7859 #endif
   7860     if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)
   7861     {
   7862 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   7863         if (m_Device4)
   7864         {
   7865                 hr = m_Device4->CreateCommittedResource1(
   7866                     &committedAllocParams.m_HeapProperties,
   7867                     committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,
   7868                     createParams.GetResourceDesc(), createParams.GetInitialResourceState(),
   7869                     createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,
   7870                     D3D12MA_IID_PPV_ARGS(&res));
   7871         }
   7872         else
   7873 #endif
   7874         {
   7875             if (committedAllocParams.m_ProtectedSession == NULL)
   7876             {
   7877                 hr = m_Device->CreateCommittedResource(
   7878                     &committedAllocParams.m_HeapProperties,
   7879                     committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,
   7880                     createParams.GetResourceDesc(), createParams.GetInitialResourceState(),
   7881                     createParams.GetOptimizedClearValue(), D3D12MA_IID_PPV_ARGS(&res));
   7882             }
   7883             else
   7884                 hr = E_NOINTERFACE;
   7885         }
   7886     }
   7887     else
   7888     {
   7889         D3D12MA_ASSERT(0);
   7890         return E_INVALIDARG;
   7891     }
   7892 
   7893     if (SUCCEEDED(hr))
   7894     {
   7895         SetResidencyPriority(res, committedAllocParams.m_ResidencyPriority);
   7896 
   7897         if (ppvResource != NULL)
   7898         {
   7899             hr = res->QueryInterface(riidResource, ppvResource);
   7900         }
   7901         if (SUCCEEDED(hr))
   7902         {
   7903             BOOL wasZeroInitialized = TRUE;
   7904 #if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE
   7905             if((committedAllocParams.m_HeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)
   7906             {
   7907                 wasZeroInitialized = FALSE;
   7908             }
   7909 #endif
   7910 
   7911             Allocation* alloc = m_AllocationObjectAllocator.Allocate(
   7912                 this, resourceSize, createParams.GetBaseResourceDesc()->Alignment, wasZeroInitialized);
   7913             alloc->InitCommitted(committedAllocParams.m_List);
   7914             alloc->SetResourcePointer(res, createParams.GetBaseResourceDesc());
   7915             alloc->SetPrivateData(pPrivateData);
   7916 
   7917             *ppAllocation = alloc;
   7918 
   7919             committedAllocParams.m_List->Register(alloc);
   7920 
   7921             const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);
   7922             m_Budget.AddBlock(memSegmentGroup, resourceSize);
   7923             m_Budget.AddAllocation(memSegmentGroup, resourceSize);
   7924         }
   7925         else
   7926         {
   7927             res->Release();
   7928         }
   7929     }
   7930     return hr;
   7931 }
   7932 
   7933 HRESULT AllocatorPimpl::AllocateHeap(
   7934     const CommittedAllocationParameters& committedAllocParams,
   7935     const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,
   7936     void* pPrivateData, Allocation** ppAllocation)
   7937 {
   7938     D3D12MA_ASSERT(committedAllocParams.IsValid());
   7939 
   7940     *ppAllocation = nullptr;
   7941 
   7942     if (withinBudget &&
   7943         !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, allocInfo.SizeInBytes))
   7944     {
   7945         return E_OUTOFMEMORY;
   7946     }
   7947 
   7948     D3D12_HEAP_DESC heapDesc = {};
   7949     heapDesc.SizeInBytes = allocInfo.SizeInBytes;
   7950     heapDesc.Properties = committedAllocParams.m_HeapProperties;
   7951     heapDesc.Alignment = allocInfo.Alignment;
   7952     heapDesc.Flags = committedAllocParams.m_HeapFlags;
   7953 
   7954     HRESULT hr;
   7955     ID3D12Heap* heap = nullptr;
   7956 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   7957     if (m_Device4)
   7958         hr = m_Device4->CreateHeap1(&heapDesc, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&heap));
   7959     else
   7960 #endif
   7961     {
   7962         if (committedAllocParams.m_ProtectedSession == NULL)
   7963             hr = m_Device->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&heap));
   7964         else
   7965             hr = E_NOINTERFACE;
   7966     }
   7967 
   7968     if (SUCCEEDED(hr))
   7969     {
   7970         SetResidencyPriority(heap, committedAllocParams.m_ResidencyPriority);
   7971 
   7972         BOOL wasZeroInitialized = TRUE;
   7973 #if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE
   7974         if((heapDesc.Flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)
   7975         {
   7976             wasZeroInitialized = FALSE;
   7977         }
   7978 #endif
   7979 
   7980         (*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment, wasZeroInitialized);
   7981         (*ppAllocation)->InitHeap(committedAllocParams.m_List, heap);
   7982         (*ppAllocation)->SetPrivateData(pPrivateData);
   7983         committedAllocParams.m_List->Register(*ppAllocation);
   7984 
   7985         const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);
   7986         m_Budget.AddBlock(memSegmentGroup, allocInfo.SizeInBytes);
   7987         m_Budget.AddAllocation(memSegmentGroup, allocInfo.SizeInBytes);
   7988     }
   7989     return hr;
   7990 }
   7991 
   7992 template<typename D3D12_RESOURCE_DESC_T>
   7993 HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,
   7994     const D3D12_RESOURCE_DESC_T* resDesc,
   7995     BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted)
   7996 {
   7997     outBlockVector = NULL;
   7998     outCommittedAllocationParams = CommittedAllocationParameters();
   7999     outPreferCommitted = false;
   8000 
   8001     bool msaaAlwaysCommitted;
   8002     if (allocDesc.CustomPool != NULL)
   8003     {
   8004         PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl;
   8005 
   8006         msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures();
   8007         outBlockVector = pool->GetBlockVector();
   8008 
   8009         const auto& desc = pool->GetDesc();
   8010         outCommittedAllocationParams.m_ProtectedSession = desc.pProtectedSession;
   8011         outCommittedAllocationParams.m_HeapProperties = desc.HeapProperties;
   8012         outCommittedAllocationParams.m_HeapFlags = desc.HeapFlags;
   8013         outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList();
   8014         outCommittedAllocationParams.m_ResidencyPriority = pool->GetDesc().ResidencyPriority;
   8015     }
   8016     else
   8017     {
   8018         if (!IsHeapTypeStandard(allocDesc.HeapType))
   8019         {
   8020             return E_INVALIDARG;
   8021         }
   8022         msaaAlwaysCommitted = m_MsaaAlwaysCommitted;
   8023 
   8024         outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType);
   8025         outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags;
   8026         outCommittedAllocationParams.m_List = &m_CommittedAllocations[StandardHeapTypeToIndex(allocDesc.HeapType)];
   8027         // outCommittedAllocationParams.m_ResidencyPriority intentionally left with default value.
   8028 
   8029         const ResourceClass resourceClass = (resDesc != NULL) ?
   8030             ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags);
   8031         const UINT defaultPoolIndex = CalcDefaultPoolIndex(allocDesc, resourceClass);
   8032         if (defaultPoolIndex != UINT32_MAX)
   8033         {
   8034             outBlockVector = m_BlockVectors[defaultPoolIndex];
   8035             const UINT64 preferredBlockSize = outBlockVector->GetPreferredBlockSize();
   8036             if (allocSize > preferredBlockSize)
   8037             {
   8038                 outBlockVector = NULL;
   8039             }
   8040             else if (allocSize > preferredBlockSize / 2)
   8041             {
   8042                 // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.
   8043                 outPreferCommitted = true;
   8044             }
   8045         }
   8046 
   8047         const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;
   8048         if (outBlockVector != NULL && extraHeapFlags != 0)
   8049         {
   8050             outBlockVector = NULL;
   8051         }
   8052     }
   8053 
   8054     if ((allocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0 ||
   8055         m_AlwaysCommitted)
   8056     {
   8057         outBlockVector = NULL;
   8058     }
   8059     if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)
   8060     {
   8061         outCommittedAllocationParams.m_List = NULL;
   8062     }
   8063     outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS;
   8064 
   8065     if (resDesc != NULL)
   8066     {
   8067         if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted)
   8068             outBlockVector = NULL;
   8069         if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc))
   8070             outPreferCommitted = true;
   8071     }
   8072 
   8073     return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG;
   8074 }
   8075 
   8076 UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const
   8077 {
   8078     D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;
   8079 
   8080 #if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE
   8081     // If allocator was created with ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED, also ignore
   8082     // D3D12_HEAP_FLAG_CREATE_NOT_ZEROED.
   8083     if(m_DefaultPoolsNotZeroed)
   8084     {
   8085         extraHeapFlags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
   8086     }
   8087 #endif
   8088 
   8089     if (extraHeapFlags != 0)
   8090     {
   8091         return UINT32_MAX;
   8092     }
   8093 
   8094     UINT poolIndex = UINT_MAX;
   8095     switch (allocDesc.HeapType)
   8096     {
   8097     case D3D12_HEAP_TYPE_DEFAULT:  poolIndex = 0; break;
   8098     case D3D12_HEAP_TYPE_UPLOAD:   poolIndex = 1; break;
   8099     case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;
   8100     default: D3D12MA_ASSERT(0);
   8101     }
   8102 
   8103     if (SupportsResourceHeapTier2())
   8104         return poolIndex;
   8105     else
   8106     {
   8107         switch (resourceClass)
   8108         {
   8109         case ResourceClass::Buffer:
   8110             return poolIndex * 3;
   8111         case ResourceClass::Non_RT_DS_Texture:
   8112             return poolIndex * 3 + 1;
   8113         case ResourceClass::RT_DS_Texture:
   8114             return poolIndex * 3 + 2;
   8115         default:
   8116             return UINT32_MAX;
   8117         }
   8118     }
   8119 }
   8120 
   8121 void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const
   8122 {
   8123     outHeapType = D3D12_HEAP_TYPE_DEFAULT;
   8124     outHeapFlags = D3D12_HEAP_FLAG_NONE;
   8125 
   8126     if (!SupportsResourceHeapTier2())
   8127     {
   8128         switch (index % 3)
   8129         {
   8130         case 0:
   8131             outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
   8132             break;
   8133         case 1:
   8134             outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
   8135             break;
   8136         case 2:
   8137             outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
   8138             break;
   8139         }
   8140 
   8141         index /= 3;
   8142     }
   8143 
   8144     switch (index)
   8145     {
   8146     case 0:
   8147         outHeapType = D3D12_HEAP_TYPE_DEFAULT;
   8148         break;
   8149     case 1:
   8150         outHeapType = D3D12_HEAP_TYPE_UPLOAD;
   8151         break;
   8152     case 2:
   8153         outHeapType = D3D12_HEAP_TYPE_READBACK;
   8154         break;
   8155     default:
   8156         D3D12MA_ASSERT(0);
   8157     }
   8158 }
   8159 
   8160 void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)
   8161 {
   8162     const UINT heapTypeIndex = (UINT)heapType - 1;
   8163 
   8164     MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
   8165     m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl);
   8166 }
   8167 
   8168 void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)
   8169 {
   8170     const UINT heapTypeIndex = (UINT)heapType - 1;
   8171 
   8172     MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
   8173     m_Pools[heapTypeIndex].Remove(pool->m_Pimpl);
   8174 }
   8175 
   8176 HRESULT AllocatorPimpl::UpdateD3D12Budget()
   8177 {
   8178 #if D3D12MA_DXGI_1_4
   8179     if (m_Adapter3)
   8180         return m_Budget.UpdateBudget(m_Adapter3, m_UseMutex);
   8181     else
   8182         return E_NOINTERFACE;
   8183 #else
   8184     return S_OK;
   8185 #endif
   8186 }
   8187 
   8188 D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const
   8189 {
   8190     return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);
   8191 }
   8192 
   8193 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   8194 D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const
   8195 {
   8196     D3D12MA_ASSERT(m_Device8 != NULL);
   8197     D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;
   8198     return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);
   8199 }
   8200 #endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__
   8201 
   8202 template<typename D3D12_RESOURCE_DESC_T>
   8203 D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const
   8204 {
   8205 #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   8206     /* Optional optimization: Microsoft documentation says:
   8207     https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo
   8208 
   8209     Your application can forgo using GetResourceAllocationInfo for buffer resources
   8210     (D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters,
   8211     which is merely the smallest multiple of 64KB that's greater or equal to
   8212     D3D12_RESOURCE_DESC::Width.
   8213     */
   8214     if (inOutResourceDesc.Alignment == 0 &&
   8215         inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
   8216     {
   8217         return {
   8218             AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes
   8219             D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment
   8220     }
   8221 #endif // #ifdef __ID3D12Device1_INTERFACE_DEFINED__
   8222 
   8223 #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
   8224     if (inOutResourceDesc.Alignment == 0 &&
   8225         inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
   8226         (inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0
   8227 #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 1
   8228         && CanUseSmallAlignment(inOutResourceDesc)
   8229 #endif
   8230         )
   8231     {
   8232         /*
   8233         The algorithm here is based on Microsoft sample: "Small Resources Sample"
   8234         https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources
   8235         */
   8236         const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ?
   8237             D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :
   8238             D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;
   8239         inOutResourceDesc.Alignment = smallAlignmentToTry;
   8240         const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = GetResourceAllocationInfoNative(inOutResourceDesc);
   8241         // Check if alignment requested has been granted.
   8242         if (smallAllocInfo.Alignment == smallAlignmentToTry)
   8243         {
   8244             return smallAllocInfo;
   8245         }
   8246         inOutResourceDesc.Alignment = 0; // Restore original
   8247     }
   8248 #endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
   8249 
   8250     return GetResourceAllocationInfoNative(inOutResourceDesc);
   8251 }
   8252 
   8253 bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size)
   8254 {
   8255     Budget budget = {};
   8256     GetBudgetForHeapType(budget, heapType);
   8257     return budget.UsageBytes + size <= budget.BudgetBytes;
   8258 }
   8259 
   8260 void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)
   8261 {
   8262     json.BeginObject();
   8263     {
   8264         json.WriteString(L"BudgetBytes");
   8265         json.WriteNumber(budget.BudgetBytes);
   8266         json.WriteString(L"UsageBytes");
   8267         json.WriteNumber(budget.UsageBytes);
   8268     }
   8269     json.EndObject();
   8270 }
   8271 
   8272 #endif // _D3D12MA_ALLOCATOR_PIMPL
   8273 #endif // _D3D12MA_ALLOCATOR_PIMPL
   8274 
   8275 #ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL
   8276 class VirtualBlockPimpl
   8277 {
   8278 public:
   8279     const ALLOCATION_CALLBACKS m_AllocationCallbacks;
   8280     const UINT64 m_Size;
   8281     BlockMetadata* m_Metadata;
   8282 
   8283     VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);
   8284     ~VirtualBlockPimpl();
   8285 };
   8286 
   8287 #ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS
   8288 VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)
   8289     : m_AllocationCallbacks(allocationCallbacks), m_Size(desc.Size)
   8290 {
   8291     switch (desc.Flags & VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK)
   8292     {
   8293     case VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR:
   8294         m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_Linear)(&m_AllocationCallbacks, true);
   8295         break;
   8296     default:
   8297         D3D12MA_ASSERT(0);
   8298     case 0:
   8299         m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_TLSF)(&m_AllocationCallbacks, true);
   8300         break;
   8301     }
   8302     m_Metadata->Init(m_Size);
   8303 }
   8304 
   8305 VirtualBlockPimpl::~VirtualBlockPimpl()
   8306 {
   8307     D3D12MA_DELETE(m_AllocationCallbacks, m_Metadata);
   8308 }
   8309 #endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS
   8310 #endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL
   8311 
   8312 
   8313 #ifndef _D3D12MA_MEMORY_BLOCK_FUNCTIONS
   8314 MemoryBlock::MemoryBlock(
   8315     AllocatorPimpl* allocator,
   8316     const D3D12_HEAP_PROPERTIES& heapProps,
   8317     D3D12_HEAP_FLAGS heapFlags,
   8318     UINT64 size,
   8319     UINT id)
   8320     : m_Allocator(allocator),
   8321     m_HeapProps(heapProps),
   8322     m_HeapFlags(heapFlags),
   8323     m_Size(size),
   8324     m_Id(id) {}
   8325 
   8326 MemoryBlock::~MemoryBlock()
   8327 {
   8328     if (m_Heap)
   8329     {
   8330         m_Heap->Release();
   8331         m_Allocator->m_Budget.RemoveBlock(
   8332             m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);
   8333     }
   8334 }
   8335 
   8336 HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)
   8337 {
   8338     D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0);
   8339 
   8340     D3D12_HEAP_DESC heapDesc = {};
   8341     heapDesc.SizeInBytes = m_Size;
   8342     heapDesc.Properties = m_HeapProps;
   8343     heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures);
   8344     heapDesc.Flags = m_HeapFlags;
   8345 
   8346     HRESULT hr;
   8347 #ifdef __ID3D12Device4_INTERFACE_DEFINED__
   8348     ID3D12Device4* const device4 = m_Allocator->GetDevice4();
   8349     if (device4)
   8350         hr = m_Allocator->GetDevice4()->CreateHeap1(&heapDesc, pProtectedSession, D3D12MA_IID_PPV_ARGS(&m_Heap));
   8351     else
   8352 #endif
   8353     {
   8354         if (pProtectedSession == NULL)
   8355             hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&m_Heap));
   8356         else
   8357             hr = E_NOINTERFACE;
   8358     }
   8359 
   8360     if (SUCCEEDED(hr))
   8361     {
   8362         m_Allocator->m_Budget.AddBlock(
   8363             m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);
   8364     }
   8365     return hr;
   8366 }
   8367 #endif // _D3D12MA_MEMORY_BLOCK_FUNCTIONS
   8368 
   8369 #ifndef _D3D12MA_NORMAL_BLOCK_FUNCTIONS
   8370 NormalBlock::NormalBlock(
   8371     AllocatorPimpl* allocator,
   8372     BlockVector* blockVector,
   8373     const D3D12_HEAP_PROPERTIES& heapProps,
   8374     D3D12_HEAP_FLAGS heapFlags,
   8375     UINT64 size,
   8376     UINT id)
   8377     : MemoryBlock(allocator, heapProps, heapFlags, size, id),
   8378     m_pMetadata(NULL),
   8379     m_BlockVector(blockVector) {}
   8380 
   8381 NormalBlock::~NormalBlock()
   8382 {
   8383     if (m_pMetadata != NULL)
   8384     {
   8385         // Define macro D3D12MA_DEBUG_LOG to receive the list of the unfreed allocations.
   8386         if (!m_pMetadata->IsEmpty())
   8387             m_pMetadata->DebugLogAllAllocations();
   8388 
   8389         // THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!
   8390         // Hitting it means you have some memory leak - unreleased Allocation objects.
   8391         D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
   8392 
   8393         D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);
   8394     }
   8395 }
   8396 
   8397 HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)
   8398 {
   8399     HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures);
   8400     if (FAILED(hr))
   8401     {
   8402         return hr;
   8403     }
   8404 
   8405     switch (algorithm)
   8406     {
   8407     case POOL_FLAG_ALGORITHM_LINEAR:
   8408         m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Linear)(&m_Allocator->GetAllocs(), false);
   8409         break;
   8410     default:
   8411         D3D12MA_ASSERT(0);
   8412     case 0:
   8413         m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_TLSF)(&m_Allocator->GetAllocs(), false);
   8414         break;
   8415     }
   8416     m_pMetadata->Init(m_Size);
   8417 
   8418     return hr;
   8419 }
   8420 
   8421 bool NormalBlock::Validate() const
   8422 {
   8423     D3D12MA_VALIDATE(GetHeap() &&
   8424         m_pMetadata &&
   8425         m_pMetadata->GetSize() != 0 &&
   8426         m_pMetadata->GetSize() == GetSize());
   8427     return m_pMetadata->Validate();
   8428 }
   8429 #endif // _D3D12MA_NORMAL_BLOCK_FUNCTIONS
   8430 
   8431 #ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS
   8432 void CommittedAllocationList::Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool)
   8433 {
   8434     m_UseMutex = useMutex;
   8435     m_HeapType = heapType;
   8436     m_Pool = pool;
   8437 }
   8438 
   8439 CommittedAllocationList::~CommittedAllocationList()
   8440 {
   8441     if (!m_AllocationList.IsEmpty())
   8442     {
   8443         D3D12MA_ASSERT(0 && "Unfreed committed allocations found!");
   8444     }
   8445 }
   8446 
   8447 UINT CommittedAllocationList::GetMemorySegmentGroup(AllocatorPimpl* allocator) const
   8448 {
   8449     if (m_Pool)
   8450         return allocator->HeapPropertiesToMemorySegmentGroup(m_Pool->GetDesc().HeapProperties);
   8451     else
   8452         return allocator->StandardHeapTypeToMemorySegmentGroup(m_HeapType);
   8453 }
   8454 
   8455 void CommittedAllocationList::AddStatistics(Statistics& inoutStats)
   8456 {
   8457     MutexLockRead lock(m_Mutex, m_UseMutex);
   8458 
   8459     for (Allocation* alloc = m_AllocationList.Front();
   8460         alloc != NULL; alloc = m_AllocationList.GetNext(alloc))
   8461     {
   8462         const UINT64 size = alloc->GetSize();
   8463         inoutStats.BlockCount++;
   8464         inoutStats.AllocationCount++;
   8465         inoutStats.BlockBytes += size;
   8466         inoutStats.AllocationBytes += size;
   8467     }
   8468 }
   8469 
   8470 void CommittedAllocationList::AddDetailedStatistics(DetailedStatistics& inoutStats)
   8471 {
   8472     MutexLockRead lock(m_Mutex, m_UseMutex);
   8473 
   8474     for (Allocation* alloc = m_AllocationList.Front();
   8475         alloc != NULL; alloc = m_AllocationList.GetNext(alloc))
   8476     {
   8477         const UINT64 size = alloc->GetSize();
   8478         inoutStats.Stats.BlockCount++;
   8479         inoutStats.Stats.BlockBytes += size;
   8480         AddDetailedStatisticsAllocation(inoutStats, size);
   8481     }
   8482 }
   8483 
   8484 void CommittedAllocationList::BuildStatsString(JsonWriter& json)
   8485 {
   8486     MutexLockRead lock(m_Mutex, m_UseMutex);
   8487 
   8488     for (Allocation* alloc = m_AllocationList.Front();
   8489         alloc != NULL; alloc = m_AllocationList.GetNext(alloc))
   8490     {
   8491         json.BeginObject(true);
   8492         json.AddAllocationToObject(*alloc);
   8493         json.EndObject();
   8494     }
   8495 }
   8496 
   8497 void CommittedAllocationList::Register(Allocation* alloc)
   8498 {
   8499     MutexLockWrite lock(m_Mutex, m_UseMutex);
   8500     m_AllocationList.PushBack(alloc);
   8501 }
   8502 
   8503 void CommittedAllocationList::Unregister(Allocation* alloc)
   8504 {
   8505     MutexLockWrite lock(m_Mutex, m_UseMutex);
   8506     m_AllocationList.Remove(alloc);
   8507 }
   8508 #endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS
   8509 
   8510 #ifndef _D3D12MA_BLOCK_VECTOR_FUNCTIONS
   8511 BlockVector::BlockVector(
   8512     AllocatorPimpl* hAllocator,
   8513     const D3D12_HEAP_PROPERTIES& heapProps,
   8514     D3D12_HEAP_FLAGS heapFlags,
   8515     UINT64 preferredBlockSize,
   8516     size_t minBlockCount,
   8517     size_t maxBlockCount,
   8518     bool explicitBlockSize,
   8519     UINT64 minAllocationAlignment,
   8520     UINT32 algorithm,
   8521     bool denyMsaaTextures,
   8522     ID3D12ProtectedResourceSession* pProtectedSession,
   8523     D3D12_RESIDENCY_PRIORITY residencyPriority)
   8524     : m_hAllocator(hAllocator),
   8525     m_HeapProps(heapProps),
   8526     m_HeapFlags(heapFlags),
   8527     m_PreferredBlockSize(preferredBlockSize),
   8528     m_MinBlockCount(minBlockCount),
   8529     m_MaxBlockCount(maxBlockCount),
   8530     m_ExplicitBlockSize(explicitBlockSize),
   8531     m_MinAllocationAlignment(minAllocationAlignment),
   8532     m_Algorithm(algorithm),
   8533     m_DenyMsaaTextures(denyMsaaTextures),
   8534     m_ProtectedSession(pProtectedSession),
   8535     m_ResidencyPriority(residencyPriority),
   8536     m_HasEmptyBlock(false),
   8537     m_Blocks(hAllocator->GetAllocs()),
   8538     m_NextBlockId(0) {}
   8539 
   8540 BlockVector::~BlockVector()
   8541 {
   8542     for (size_t i = m_Blocks.size(); i--; )
   8543     {
   8544         D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]);
   8545     }
   8546 }
   8547 
   8548 HRESULT BlockVector::CreateMinBlocks()
   8549 {
   8550     for (size_t i = 0; i < m_MinBlockCount; ++i)
   8551     {
   8552         HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL);
   8553         if (FAILED(hr))
   8554         {
   8555             return hr;
   8556         }
   8557     }
   8558     return S_OK;
   8559 }
   8560 
   8561 bool BlockVector::IsEmpty()
   8562 {
   8563     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
   8564     return m_Blocks.empty();
   8565 }
   8566 
   8567 HRESULT BlockVector::Allocate(
   8568     UINT64 size,
   8569     UINT64 alignment,
   8570     const ALLOCATION_DESC& allocDesc,
   8571     size_t allocationCount,
   8572     Allocation** pAllocations)
   8573 {
   8574     size_t allocIndex;
   8575     HRESULT hr = S_OK;
   8576 
   8577     {
   8578         MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
   8579         for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
   8580         {
   8581             hr = AllocatePage(
   8582                 size,
   8583                 alignment,
   8584                 allocDesc,
   8585                 pAllocations + allocIndex);
   8586             if (FAILED(hr))
   8587             {
   8588                 break;
   8589             }
   8590         }
   8591     }
   8592 
   8593     if (FAILED(hr))
   8594     {
   8595         // Free all already created allocations.
   8596         while (allocIndex--)
   8597         {
   8598             Free(pAllocations[allocIndex]);
   8599         }
   8600         ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount);
   8601     }
   8602 
   8603     return hr;
   8604 }
   8605 
   8606 void BlockVector::Free(Allocation* hAllocation)
   8607 {
   8608     NormalBlock* pBlockToDelete = NULL;
   8609 
   8610     bool budgetExceeded = false;
   8611     if (IsHeapTypeStandard(m_HeapProps.Type))
   8612     {
   8613         Budget budget = {};
   8614         m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);
   8615         budgetExceeded = budget.UsageBytes >= budget.BudgetBytes;
   8616     }
   8617 
   8618     // Scope for lock.
   8619     {
   8620         MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
   8621 
   8622         NormalBlock* pBlock = hAllocation->m_Placed.block;
   8623 
   8624         pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());
   8625         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
   8626 
   8627         const size_t blockCount = m_Blocks.size();
   8628         // pBlock became empty after this deallocation.
   8629         if (pBlock->m_pMetadata->IsEmpty())
   8630         {
   8631             // Already has empty Allocation. We don't want to have two, so delete this one.
   8632             if ((m_HasEmptyBlock || budgetExceeded) &&
   8633                 blockCount > m_MinBlockCount)
   8634             {
   8635                 pBlockToDelete = pBlock;
   8636                 Remove(pBlock);
   8637             }
   8638             // We now have first empty block.
   8639             else
   8640             {
   8641                 m_HasEmptyBlock = true;
   8642             }
   8643         }
   8644         // pBlock didn't become empty, but we have another empty block - find and free that one.
   8645         // (This is optional, heuristics.)
   8646         else if (m_HasEmptyBlock && blockCount > m_MinBlockCount)
   8647         {
   8648             NormalBlock* pLastBlock = m_Blocks.back();
   8649             if (pLastBlock->m_pMetadata->IsEmpty())
   8650             {
   8651                 pBlockToDelete = pLastBlock;
   8652                 m_Blocks.pop_back();
   8653                 m_HasEmptyBlock = false;
   8654             }
   8655         }
   8656 
   8657         IncrementallySortBlocks();
   8658     }
   8659 
   8660     // Destruction of a free Allocation. Deferred until this point, outside of mutex
   8661     // lock, for performance reason.
   8662     if (pBlockToDelete != NULL)
   8663     {
   8664         D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete);
   8665     }
   8666 }
   8667 
   8668 HRESULT BlockVector::CreateResource(
   8669     UINT64 size,
   8670     UINT64 alignment,
   8671     const ALLOCATION_DESC& allocDesc,
   8672     const CREATE_RESOURCE_PARAMS& createParams,
   8673     Allocation** ppAllocation,
   8674     REFIID riidResource,
   8675     void** ppvResource)
   8676 {
   8677     HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);
   8678     if (SUCCEEDED(hr))
   8679     {
   8680         ID3D12Resource* res = NULL;
   8681         hr = m_hAllocator->CreatePlacedResourceWrap(
   8682             (*ppAllocation)->m_Placed.block->GetHeap(),
   8683             (*ppAllocation)->GetOffset(),
   8684             createParams,
   8685             D3D12MA_IID_PPV_ARGS(&res));
   8686         if (SUCCEEDED(hr))
   8687         {
   8688             if (ppvResource != NULL)
   8689             {
   8690                 hr = res->QueryInterface(riidResource, ppvResource);
   8691             }
   8692             if (SUCCEEDED(hr))
   8693             {
   8694                 (*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());
   8695             }
   8696             else
   8697             {
   8698                 res->Release();
   8699                 SAFE_RELEASE(*ppAllocation);
   8700             }
   8701         }
   8702         else
   8703         {
   8704             SAFE_RELEASE(*ppAllocation);
   8705         }
   8706     }
   8707     return hr;
   8708 }
   8709 
   8710 void BlockVector::AddStatistics(Statistics& inoutStats)
   8711 {
   8712     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
   8713 
   8714     for (size_t i = 0; i < m_Blocks.size(); ++i)
   8715     {
   8716         const NormalBlock* const pBlock = m_Blocks[i];
   8717         D3D12MA_ASSERT(pBlock);
   8718         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
   8719         pBlock->m_pMetadata->AddStatistics(inoutStats);
   8720     }
   8721 }
   8722 
   8723 void BlockVector::AddDetailedStatistics(DetailedStatistics& inoutStats)
   8724 {
   8725     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
   8726 
   8727     for (size_t i = 0; i < m_Blocks.size(); ++i)
   8728     {
   8729         const NormalBlock* const pBlock = m_Blocks[i];
   8730         D3D12MA_ASSERT(pBlock);
   8731         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
   8732         pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);
   8733     }
   8734 }
   8735 
   8736 void BlockVector::WriteBlockInfoToJson(JsonWriter& json)
   8737 {
   8738     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
   8739 
   8740     json.BeginObject();
   8741 
   8742     for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)
   8743     {
   8744         const NormalBlock* const pBlock = m_Blocks[i];
   8745         D3D12MA_ASSERT(pBlock);
   8746         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
   8747         json.BeginString();
   8748         json.ContinueString(pBlock->GetId());
   8749         json.EndString();
   8750 
   8751         json.BeginObject();
   8752         pBlock->m_pMetadata->WriteAllocationInfoToJson(json);
   8753         json.EndObject();
   8754     }
   8755 
   8756     json.EndObject();
   8757 }
   8758 
   8759 UINT64 BlockVector::CalcSumBlockSize() const
   8760 {
   8761     UINT64 result = 0;
   8762     for (size_t i = m_Blocks.size(); i--; )
   8763     {
   8764         result += m_Blocks[i]->m_pMetadata->GetSize();
   8765     }
   8766     return result;
   8767 }
   8768 
   8769 UINT64 BlockVector::CalcMaxBlockSize() const
   8770 {
   8771     UINT64 result = 0;
   8772     for (size_t i = m_Blocks.size(); i--; )
   8773     {
   8774         result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
   8775         if (result >= m_PreferredBlockSize)
   8776         {
   8777             break;
   8778         }
   8779     }
   8780     return result;
   8781 }
   8782 
   8783 void BlockVector::Remove(NormalBlock* pBlock)
   8784 {
   8785     for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
   8786     {
   8787         if (m_Blocks[blockIndex] == pBlock)
   8788         {
   8789             m_Blocks.remove(blockIndex);
   8790             return;
   8791         }
   8792     }
   8793     D3D12MA_ASSERT(0);
   8794 }
   8795 
   8796 void BlockVector::IncrementallySortBlocks()
   8797 {
   8798     if (!m_IncrementalSort)
   8799         return;
   8800     // Bubble sort only until first swap.
   8801     for (size_t i = 1; i < m_Blocks.size(); ++i)
   8802     {
   8803         if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
   8804         {
   8805             D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
   8806             return;
   8807         }
   8808     }
   8809 }
   8810 
   8811 void BlockVector::SortByFreeSize()
   8812 {
   8813     D3D12MA_SORT(m_Blocks.begin(), m_Blocks.end(),
   8814         [](auto* b1, auto* b2)
   8815         {
   8816             return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();
   8817         });
   8818 }
   8819 
   8820 HRESULT BlockVector::AllocatePage(
   8821     UINT64 size,
   8822     UINT64 alignment,
   8823     const ALLOCATION_DESC& allocDesc,
   8824     Allocation** pAllocation)
   8825 {
   8826     // Early reject: requested allocation size is larger that maximum block size for this block vector.
   8827     if (size + D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize)
   8828     {
   8829         return E_OUTOFMEMORY;
   8830     }
   8831 
   8832     UINT64 freeMemory = UINT64_MAX;
   8833     if (IsHeapTypeStandard(m_HeapProps.Type))
   8834     {
   8835         Budget budget = {};
   8836         m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);
   8837         freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0;
   8838     }
   8839 
   8840     const bool canCreateNewBlock =
   8841         ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&
   8842         (m_Blocks.size() < m_MaxBlockCount) &&
   8843         // Even if we don't have to stay within budget with this allocation, when the
   8844         // budget would be exceeded, we don't want to allocate new blocks, but always
   8845         // create resources as committed.
   8846         freeMemory >= size;
   8847 
   8848     // 1. Search existing allocations
   8849     {
   8850         // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
   8851         for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
   8852         {
   8853             NormalBlock* const pCurrBlock = m_Blocks[blockIndex];
   8854             D3D12MA_ASSERT(pCurrBlock);
   8855             HRESULT hr = AllocateFromBlock(
   8856                 pCurrBlock,
   8857                 size,
   8858                 alignment,
   8859                 allocDesc.Flags,
   8860                 allocDesc.pPrivateData,
   8861                 allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,
   8862                 pAllocation);
   8863             if (SUCCEEDED(hr))
   8864             {
   8865                 return hr;
   8866             }
   8867         }
   8868     }
   8869 
   8870     // 2. Try to create new block.
   8871     if (canCreateNewBlock)
   8872     {
   8873         // Calculate optimal size for new block.
   8874         UINT64 newBlockSize = m_PreferredBlockSize;
   8875         UINT newBlockSizeShift = 0;
   8876 
   8877         if (!m_ExplicitBlockSize)
   8878         {
   8879             // Allocate 1/8, 1/4, 1/2 as first blocks.
   8880             const UINT64 maxExistingBlockSize = CalcMaxBlockSize();
   8881             for (UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
   8882             {
   8883                 const UINT64 smallerNewBlockSize = newBlockSize / 2;
   8884                 if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
   8885                 {
   8886                     newBlockSize = smallerNewBlockSize;
   8887                     ++newBlockSizeShift;
   8888                 }
   8889                 else
   8890                 {
   8891                     break;
   8892                 }
   8893             }
   8894         }
   8895 
   8896         size_t newBlockIndex = 0;
   8897         HRESULT hr = newBlockSize <= freeMemory ?
   8898             CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
   8899         // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
   8900         if (!m_ExplicitBlockSize)
   8901         {
   8902             while (FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
   8903             {
   8904                 const UINT64 smallerNewBlockSize = newBlockSize / 2;
   8905                 if (smallerNewBlockSize >= size)
   8906                 {
   8907                     newBlockSize = smallerNewBlockSize;
   8908                     ++newBlockSizeShift;
   8909                     hr = newBlockSize <= freeMemory ?
   8910                         CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
   8911                 }
   8912                 else
   8913                 {
   8914                     break;
   8915                 }
   8916             }
   8917         }
   8918 
   8919         if (SUCCEEDED(hr))
   8920         {
   8921             NormalBlock* const pBlock = m_Blocks[newBlockIndex];
   8922             D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
   8923 
   8924             hr = AllocateFromBlock(
   8925                 pBlock,
   8926                 size,
   8927                 alignment,
   8928                 allocDesc.Flags,
   8929                 allocDesc.pPrivateData,
   8930                 allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,
   8931                 pAllocation);
   8932             if (SUCCEEDED(hr))
   8933             {
   8934                 return hr;
   8935             }
   8936             else
   8937             {
   8938                 // Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment.
   8939                 return E_OUTOFMEMORY;
   8940             }
   8941         }
   8942     }
   8943 
   8944     return E_OUTOFMEMORY;
   8945 }
   8946 
   8947 HRESULT BlockVector::AllocateFromBlock(
   8948     NormalBlock* pBlock,
   8949     UINT64 size,
   8950     UINT64 alignment,
   8951     ALLOCATION_FLAGS allocFlags,
   8952     void* pPrivateData,
   8953     UINT32 strategy,
   8954     Allocation** pAllocation)
   8955 {
   8956     alignment = D3D12MA_MAX(alignment, m_MinAllocationAlignment);
   8957 
   8958     AllocationRequest currRequest = {};
   8959     if (pBlock->m_pMetadata->CreateAllocationRequest(
   8960         size,
   8961         alignment,
   8962         allocFlags & ALLOCATION_FLAG_UPPER_ADDRESS,
   8963         strategy,
   8964         &currRequest))
   8965     {
   8966         return CommitAllocationRequest(currRequest, pBlock, size, alignment, pPrivateData, pAllocation);
   8967     }
   8968     return E_OUTOFMEMORY;
   8969 }
   8970 
   8971 HRESULT BlockVector::CommitAllocationRequest(
   8972     AllocationRequest& allocRequest,
   8973     NormalBlock* pBlock,
   8974     UINT64 size,
   8975     UINT64 alignment,
   8976     void* pPrivateData,
   8977     Allocation** pAllocation)
   8978 {
   8979     // We no longer have an empty Allocation.
   8980     if (pBlock->m_pMetadata->IsEmpty())
   8981         m_HasEmptyBlock = false;
   8982 
   8983     *pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, alignment, allocRequest.zeroInitialized);
   8984     pBlock->m_pMetadata->Alloc(allocRequest, size, *pAllocation);
   8985 
   8986     (*pAllocation)->InitPlaced(allocRequest.allocHandle, pBlock);
   8987     (*pAllocation)->SetPrivateData(pPrivateData);
   8988 
   8989     D3D12MA_HEAVY_ASSERT(pBlock->Validate());
   8990     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), size);
   8991 
   8992     return S_OK;
   8993 }
   8994 
   8995 HRESULT BlockVector::CreateBlock(
   8996     UINT64 blockSize,
   8997     size_t* pNewBlockIndex)
   8998 {
   8999     NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)(
   9000         m_hAllocator,
   9001         this,
   9002         m_HeapProps,
   9003         m_HeapFlags,
   9004         blockSize,
   9005         m_NextBlockId++);
   9006     HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures);
   9007     if (FAILED(hr))
   9008     {
   9009         D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock);
   9010         return hr;
   9011     }
   9012 
   9013     m_hAllocator->SetResidencyPriority(pBlock->GetHeap(), m_ResidencyPriority);
   9014 
   9015     m_Blocks.push_back(pBlock);
   9016     if (pNewBlockIndex != NULL)
   9017     {
   9018         *pNewBlockIndex = m_Blocks.size() - 1;
   9019     }
   9020 
   9021     return hr;
   9022 }
   9023 #endif // _D3D12MA_BLOCK_VECTOR_FUNCTIONS
   9024 
   9025 #ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS
   9026 DefragmentationContextPimpl::DefragmentationContextPimpl(
   9027     AllocatorPimpl* hAllocator,
   9028     const DEFRAGMENTATION_DESC& desc,
   9029     BlockVector* poolVector)
   9030     : m_MaxPassBytes(desc.MaxBytesPerPass == 0 ? UINT64_MAX : desc.MaxBytesPerPass),
   9031     m_MaxPassAllocations(desc.MaxAllocationsPerPass == 0 ? UINT32_MAX : desc.MaxAllocationsPerPass),
   9032     m_Moves(hAllocator->GetAllocs())
   9033 {
   9034     m_Algorithm = desc.Flags & DEFRAGMENTATION_FLAG_ALGORITHM_MASK;
   9035 
   9036     if (poolVector != NULL)
   9037     {
   9038         m_BlockVectorCount = 1;
   9039         m_PoolBlockVector = poolVector;
   9040         m_pBlockVectors = &m_PoolBlockVector;
   9041         m_PoolBlockVector->SetIncrementalSort(false);
   9042         m_PoolBlockVector->SortByFreeSize();
   9043     }
   9044     else
   9045     {
   9046         m_BlockVectorCount = hAllocator->GetDefaultPoolCount();
   9047         m_PoolBlockVector = NULL;
   9048         m_pBlockVectors = hAllocator->GetDefaultPools();
   9049         for (UINT32 i = 0; i < m_BlockVectorCount; ++i)
   9050         {
   9051             BlockVector* vector = m_pBlockVectors[i];
   9052             if (vector != NULL)
   9053             {
   9054                 vector->SetIncrementalSort(false);
   9055                 vector->SortByFreeSize();
   9056             }
   9057         }
   9058     }
   9059 
   9060     switch (m_Algorithm)
   9061     {
   9062     case 0: // Default algorithm
   9063         m_Algorithm = DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED;
   9064     case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:
   9065     {
   9066         m_AlgorithmState = D3D12MA_NEW_ARRAY(hAllocator->GetAllocs(), StateBalanced, m_BlockVectorCount);
   9067         break;
   9068     }
   9069     }
   9070 }
   9071 
   9072 DefragmentationContextPimpl::~DefragmentationContextPimpl()
   9073 {
   9074     if (m_PoolBlockVector != NULL)
   9075         m_PoolBlockVector->SetIncrementalSort(true);
   9076     else
   9077     {
   9078         for (UINT32 i = 0; i < m_BlockVectorCount; ++i)
   9079         {
   9080             BlockVector* vector = m_pBlockVectors[i];
   9081             if (vector != NULL)
   9082                 vector->SetIncrementalSort(true);
   9083         }
   9084     }
   9085 
   9086     if (m_AlgorithmState)
   9087     {
   9088         switch (m_Algorithm)
   9089         {
   9090         case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:
   9091             D3D12MA_DELETE_ARRAY(m_Moves.GetAllocs(), reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);
   9092             break;
   9093         default:
   9094             D3D12MA_ASSERT(0);
   9095         }
   9096     }
   9097 }
   9098 
   9099 HRESULT DefragmentationContextPimpl::DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)
   9100 {
   9101     if (m_PoolBlockVector != NULL)
   9102     {
   9103         MutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->m_hAllocator->UseMutex());
   9104 
   9105         if (m_PoolBlockVector->GetBlockCount() > 1)
   9106             ComputeDefragmentation(*m_PoolBlockVector, 0);
   9107         else if (m_PoolBlockVector->GetBlockCount() == 1)
   9108             ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));
   9109 
   9110         // Setup index into block vector
   9111         for (size_t i = 0; i < m_Moves.size(); ++i)
   9112             m_Moves[i].pDstTmpAllocation->SetPrivateData(0);
   9113     }
   9114     else
   9115     {
   9116         for (UINT32 i = 0; i < m_BlockVectorCount; ++i)
   9117         {
   9118             if (m_pBlockVectors[i] != NULL)
   9119             {
   9120                 MutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->m_hAllocator->UseMutex());
   9121 
   9122                 bool end = false;
   9123                 size_t movesOffset = m_Moves.size();
   9124                 if (m_pBlockVectors[i]->GetBlockCount() > 1)
   9125                 {
   9126                     end = ComputeDefragmentation(*m_pBlockVectors[i], i);
   9127                 }
   9128                 else if (m_pBlockVectors[i]->GetBlockCount() == 1)
   9129                 {
   9130                     end = ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0));
   9131                 }
   9132 
   9133                 // Setup index into block vector
   9134                 for (; movesOffset < m_Moves.size(); ++movesOffset)
   9135                     m_Moves[movesOffset].pDstTmpAllocation->SetPrivateData(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));
   9136 
   9137                 if (end)
   9138                     break;
   9139             }
   9140         }
   9141     }
   9142 
   9143     moveInfo.MoveCount = static_cast<UINT32>(m_Moves.size());
   9144     if (moveInfo.MoveCount > 0)
   9145     {
   9146         moveInfo.pMoves = m_Moves.data();
   9147         return S_FALSE;
   9148     }
   9149 
   9150     moveInfo.pMoves = NULL;
   9151     return S_OK;
   9152 }
   9153 
   9154 HRESULT DefragmentationContextPimpl::DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)
   9155 {
   9156     D3D12MA_ASSERT(moveInfo.MoveCount > 0 ? moveInfo.pMoves != NULL : true);
   9157 
   9158     HRESULT result = S_OK;
   9159     Vector<FragmentedBlock> immovableBlocks(m_Moves.GetAllocs());
   9160 
   9161     for (uint32_t i = 0; i < moveInfo.MoveCount; ++i)
   9162     {
   9163         DEFRAGMENTATION_MOVE& move = moveInfo.pMoves[i];
   9164         size_t prevCount = 0, currentCount = 0;
   9165         UINT64 freedBlockSize = 0;
   9166 
   9167         UINT32 vectorIndex;
   9168         BlockVector* vector;
   9169         if (m_PoolBlockVector != NULL)
   9170         {
   9171             vectorIndex = 0;
   9172             vector = m_PoolBlockVector;
   9173         }
   9174         else
   9175         {
   9176             vectorIndex = static_cast<UINT32>(reinterpret_cast<uintptr_t>(move.pDstTmpAllocation->GetPrivateData()));
   9177             vector = m_pBlockVectors[vectorIndex];
   9178             D3D12MA_ASSERT(vector != NULL);
   9179         }
   9180 
   9181         switch (move.Operation)
   9182         {
   9183         case DEFRAGMENTATION_MOVE_OPERATION_COPY:
   9184         {
   9185             move.pSrcAllocation->SwapBlockAllocation(move.pDstTmpAllocation);
   9186 
   9187             // Scope for locks, Free have it's own lock
   9188             {
   9189                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9190                 prevCount = vector->GetBlockCount();
   9191                 freedBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
   9192             }
   9193             move.pDstTmpAllocation->Release();
   9194             {
   9195                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9196                 currentCount = vector->GetBlockCount();
   9197             }
   9198 
   9199             result = S_FALSE;
   9200             break;
   9201         }
   9202         case DEFRAGMENTATION_MOVE_OPERATION_IGNORE:
   9203         {
   9204             m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();
   9205             --m_PassStats.AllocationsMoved;
   9206             move.pDstTmpAllocation->Release();
   9207 
   9208             NormalBlock* newBlock = move.pSrcAllocation->GetBlock();
   9209             bool notPresent = true;
   9210             for (const FragmentedBlock& block : immovableBlocks)
   9211             {
   9212                 if (block.block == newBlock)
   9213                 {
   9214                     notPresent = false;
   9215                     break;
   9216                 }
   9217             }
   9218             if (notPresent)
   9219                 immovableBlocks.push_back({ vectorIndex, newBlock });
   9220             break;
   9221         }
   9222         case DEFRAGMENTATION_MOVE_OPERATION_DESTROY:
   9223         {
   9224             m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();
   9225             --m_PassStats.AllocationsMoved;
   9226             // Scope for locks, Free have it's own lock
   9227             {
   9228                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9229                 prevCount = vector->GetBlockCount();
   9230                 freedBlockSize = move.pSrcAllocation->GetBlock()->m_pMetadata->GetSize();
   9231             }
   9232             move.pSrcAllocation->Release();
   9233             {
   9234                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9235                 currentCount = vector->GetBlockCount();
   9236             }
   9237             freedBlockSize *= prevCount - currentCount;
   9238 
   9239             UINT64 dstBlockSize;
   9240             {
   9241                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9242                 dstBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
   9243             }
   9244             move.pDstTmpAllocation->Release();
   9245             {
   9246                 MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9247                 freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());
   9248                 currentCount = vector->GetBlockCount();
   9249             }
   9250 
   9251             result = S_FALSE;
   9252             break;
   9253         }
   9254         default:
   9255             D3D12MA_ASSERT(0);
   9256         }
   9257 
   9258         if (prevCount > currentCount)
   9259         {
   9260             size_t freedBlocks = prevCount - currentCount;
   9261             m_PassStats.HeapsFreed += static_cast<UINT32>(freedBlocks);
   9262             m_PassStats.BytesFreed += freedBlockSize;
   9263         }
   9264     }
   9265     moveInfo.MoveCount = 0;
   9266     moveInfo.pMoves = NULL;
   9267     m_Moves.clear();
   9268 
   9269     // Update stats
   9270     m_GlobalStats.AllocationsMoved += m_PassStats.AllocationsMoved;
   9271     m_GlobalStats.BytesFreed += m_PassStats.BytesFreed;
   9272     m_GlobalStats.BytesMoved += m_PassStats.BytesMoved;
   9273     m_GlobalStats.HeapsFreed += m_PassStats.HeapsFreed;
   9274     m_PassStats = { 0 };
   9275 
   9276     // Move blocks with immovable allocations according to algorithm
   9277     if (immovableBlocks.size() > 0)
   9278     {
   9279         // Move to the begining
   9280         for (const FragmentedBlock& block : immovableBlocks)
   9281         {
   9282             BlockVector* vector = m_pBlockVectors[block.data];
   9283             MutexLockWrite lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());
   9284 
   9285             for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)
   9286             {
   9287                 if (vector->GetBlock(i) == block.block)
   9288                 {
   9289                     D3D12MA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);
   9290                     break;
   9291                 }
   9292             }
   9293         }
   9294     }
   9295     return result;
   9296 }
   9297 
   9298 bool DefragmentationContextPimpl::ComputeDefragmentation(BlockVector& vector, size_t index)
   9299 {
   9300     switch (m_Algorithm)
   9301     {
   9302     case DEFRAGMENTATION_FLAG_ALGORITHM_FAST:
   9303         return ComputeDefragmentation_Fast(vector);
   9304     default:
   9305         D3D12MA_ASSERT(0);
   9306     case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:
   9307         return ComputeDefragmentation_Balanced(vector, index, true);
   9308     case DEFRAGMENTATION_FLAG_ALGORITHM_FULL:
   9309         return ComputeDefragmentation_Full(vector);
   9310     }
   9311 }
   9312 
   9313 DefragmentationContextPimpl::MoveAllocationData DefragmentationContextPimpl::GetMoveData(
   9314     AllocHandle handle, BlockMetadata* metadata)
   9315 {
   9316     MoveAllocationData moveData;
   9317     moveData.move.pSrcAllocation = (Allocation*)metadata->GetAllocationPrivateData(handle);
   9318     moveData.size = moveData.move.pSrcAllocation->GetSize();
   9319     moveData.alignment = moveData.move.pSrcAllocation->GetAlignment();
   9320     moveData.flags = ALLOCATION_FLAG_NONE;
   9321 
   9322     return moveData;
   9323 }
   9324 
   9325 DefragmentationContextPimpl::CounterStatus DefragmentationContextPimpl::CheckCounters(UINT64 bytes)
   9326 {
   9327     // Ignore allocation if will exceed max size for copy
   9328     if (m_PassStats.BytesMoved + bytes > m_MaxPassBytes)
   9329     {
   9330         if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)
   9331             return CounterStatus::Ignore;
   9332         else
   9333             return CounterStatus::End;
   9334     }
   9335     return CounterStatus::Pass;
   9336 }
   9337 
   9338 bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes)
   9339 {
   9340     m_PassStats.BytesMoved += bytes;
   9341     // Early return when max found
   9342     if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes)
   9343     {
   9344         D3D12MA_ASSERT((m_PassStats.AllocationsMoved == m_MaxPassAllocations ||
   9345             m_PassStats.BytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");
   9346         return true;
   9347     }
   9348     return false;
   9349 }
   9350 
   9351 bool DefragmentationContextPimpl::ReallocWithinBlock(BlockVector& vector, NormalBlock* block)
   9352 {
   9353     BlockMetadata* metadata = block->m_pMetadata;
   9354 
   9355     for (AllocHandle handle = metadata->GetAllocationListBegin();
   9356         handle != (AllocHandle)0;
   9357         handle = metadata->GetNextAllocation(handle))
   9358     {
   9359         MoveAllocationData moveData = GetMoveData(handle, metadata);
   9360         // Ignore newly created allocations by defragmentation algorithm
   9361         if (moveData.move.pSrcAllocation->GetPrivateData() == this)
   9362             continue;
   9363         switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))
   9364         {
   9365         case CounterStatus::Ignore:
   9366             continue;
   9367         case CounterStatus::End:
   9368             return true;
   9369         default:
   9370             D3D12MA_ASSERT(0);
   9371         case CounterStatus::Pass:
   9372             break;
   9373         }
   9374         
   9375         UINT64 offset = moveData.move.pSrcAllocation->GetOffset();
   9376         if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
   9377         {
   9378             AllocationRequest request = {};
   9379             if (metadata->CreateAllocationRequest(
   9380                 moveData.size,
   9381                 moveData.alignment,
   9382                 false,
   9383                 ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,
   9384                 &request))
   9385             {
   9386                 if (metadata->GetAllocationOffset(request.allocHandle) < offset)
   9387                 {
   9388                     if (SUCCEEDED(vector.CommitAllocationRequest(
   9389                         request,
   9390                         block,
   9391                         moveData.size,
   9392                         moveData.alignment,
   9393                         this,
   9394                         &moveData.move.pDstTmpAllocation)))
   9395                     {
   9396                         m_Moves.push_back(moveData.move);
   9397                         if (IncrementCounters(moveData.size))
   9398                             return true;
   9399                     }
   9400                 }
   9401             }
   9402         }
   9403     }
   9404     return false;
   9405 }
   9406 
   9407 bool DefragmentationContextPimpl::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector)
   9408 {
   9409     for (; start < end; ++start)
   9410     {
   9411         NormalBlock* dstBlock = vector.GetBlock(start);
   9412         if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)
   9413         {
   9414             if (SUCCEEDED(vector.AllocateFromBlock(dstBlock,
   9415                 data.size,
   9416                 data.alignment,
   9417                 data.flags,
   9418                 this,
   9419                 0,
   9420                 &data.move.pDstTmpAllocation)))
   9421             {
   9422                 m_Moves.push_back(data.move);
   9423                 if (IncrementCounters(data.size))
   9424                     return true;
   9425                 break;
   9426             }
   9427         }
   9428     }
   9429     return false;
   9430 }
   9431 
   9432 bool DefragmentationContextPimpl::ComputeDefragmentation_Fast(BlockVector& vector)
   9433 {
   9434     // Move only between blocks
   9435 
   9436     // Go through allocations in last blocks and try to fit them inside first ones
   9437     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
   9438     {
   9439         BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
   9440 
   9441         for (AllocHandle handle = metadata->GetAllocationListBegin();
   9442             handle != (AllocHandle)0;
   9443             handle = metadata->GetNextAllocation(handle))
   9444         {
   9445             MoveAllocationData moveData = GetMoveData(handle, metadata);
   9446             // Ignore newly created allocations by defragmentation algorithm
   9447             if (moveData.move.pSrcAllocation->GetPrivateData() == this)
   9448                 continue;
   9449             switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))
   9450             {
   9451             case CounterStatus::Ignore:
   9452                 continue;
   9453             case CounterStatus::End:
   9454                 return true;
   9455             default:
   9456                 D3D12MA_ASSERT(0);
   9457             case CounterStatus::Pass:
   9458                 break;
   9459             }
   9460 
   9461             // Check all previous blocks for free space
   9462             if (AllocInOtherBlock(0, i, moveData, vector))
   9463                 return true;
   9464         }
   9465     }
   9466     return false;
   9467 }
   9468 
   9469 bool DefragmentationContextPimpl::ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update)
   9470 {
   9471     // Go over every allocation and try to fit it in previous blocks at lowest offsets,
   9472     // if not possible: realloc within single block to minimize offset (exclude offset == 0),
   9473     // but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)
   9474     D3D12MA_ASSERT(m_AlgorithmState != NULL);
   9475 
   9476     StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];
   9477     if (update && vectorState.avgAllocSize == UINT64_MAX)
   9478         UpdateVectorStatistics(vector, vectorState);
   9479 
   9480     const size_t startMoveCount = m_Moves.size();
   9481     UINT64 minimalFreeRegion = vectorState.avgFreeSize / 2;
   9482     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
   9483     {
   9484         NormalBlock* block = vector.GetBlock(i);
   9485         BlockMetadata* metadata = block->m_pMetadata;
   9486         UINT64 prevFreeRegionSize = 0;
   9487 
   9488         for (AllocHandle handle = metadata->GetAllocationListBegin();
   9489             handle != (AllocHandle)0;
   9490             handle = metadata->GetNextAllocation(handle))
   9491         {
   9492             MoveAllocationData moveData = GetMoveData(handle, metadata);
   9493             // Ignore newly created allocations by defragmentation algorithm
   9494             if (moveData.move.pSrcAllocation->GetPrivateData() == this)
   9495                 continue;
   9496             switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))
   9497             {
   9498             case CounterStatus::Ignore:
   9499                 continue;
   9500             case CounterStatus::End:
   9501                 return true;
   9502             default:
   9503                 D3D12MA_ASSERT(0);
   9504             case CounterStatus::Pass:
   9505                 break;
   9506             }
   9507 
   9508             // Check all previous blocks for free space
   9509             const size_t prevMoveCount = m_Moves.size();
   9510             if (AllocInOtherBlock(0, i, moveData, vector))
   9511                 return true;
   9512 
   9513             UINT64 nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);
   9514             // If no room found then realloc within block for lower offset
   9515             UINT64 offset = moveData.move.pSrcAllocation->GetOffset();
   9516             if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
   9517             {
   9518                 // Check if realloc will make sense
   9519                 if (prevFreeRegionSize >= minimalFreeRegion ||
   9520                     nextFreeRegionSize >= minimalFreeRegion ||
   9521                     moveData.size <= vectorState.avgFreeSize ||
   9522                     moveData.size <= vectorState.avgAllocSize)
   9523                 {
   9524                     AllocationRequest request = {};
   9525                     if (metadata->CreateAllocationRequest(
   9526                         moveData.size,
   9527                         moveData.alignment,
   9528                         false,
   9529                         ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,
   9530                         &request))
   9531                     {
   9532                         if (metadata->GetAllocationOffset(request.allocHandle) < offset)
   9533                         {
   9534                             if (SUCCEEDED(vector.CommitAllocationRequest(
   9535                                 request,
   9536                                 block,
   9537                                 moveData.size,
   9538                                 moveData.alignment,
   9539                                 this,
   9540                                 &moveData.move.pDstTmpAllocation)))
   9541                             {
   9542                                 m_Moves.push_back(moveData.move);
   9543                                 if (IncrementCounters(moveData.size))
   9544                                     return true;
   9545                             }
   9546                         }
   9547                     }
   9548                 }
   9549             }
   9550             prevFreeRegionSize = nextFreeRegionSize;
   9551         }
   9552     }
   9553 
   9554     // No moves perfomed, update statistics to current vector state
   9555     if (startMoveCount == m_Moves.size() && !update)
   9556     {
   9557         vectorState.avgAllocSize = UINT64_MAX;
   9558         return ComputeDefragmentation_Balanced(vector, index, false);
   9559     }
   9560     return false;
   9561 }
   9562 
   9563 bool DefragmentationContextPimpl::ComputeDefragmentation_Full(BlockVector& vector)
   9564 {
   9565     // Go over every allocation and try to fit it in previous blocks at lowest offsets,
   9566     // if not possible: realloc within single block to minimize offset (exclude offset == 0)
   9567 
   9568     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
   9569     {
   9570         NormalBlock* block = vector.GetBlock(i);
   9571         BlockMetadata* metadata = block->m_pMetadata;
   9572 
   9573         for (AllocHandle handle = metadata->GetAllocationListBegin();
   9574             handle != (AllocHandle)0;
   9575             handle = metadata->GetNextAllocation(handle))
   9576         {
   9577             MoveAllocationData moveData = GetMoveData(handle, metadata);
   9578             // Ignore newly created allocations by defragmentation algorithm
   9579             if (moveData.move.pSrcAllocation->GetPrivateData() == this)
   9580                 continue;
   9581             switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))
   9582             {
   9583             case CounterStatus::Ignore:
   9584                 continue;
   9585             case CounterStatus::End:
   9586                 return true;
   9587             default:
   9588                 D3D12MA_ASSERT(0);
   9589             case CounterStatus::Pass:
   9590                 break;
   9591             }
   9592 
   9593             // Check all previous blocks for free space
   9594             const size_t prevMoveCount = m_Moves.size();
   9595             if (AllocInOtherBlock(0, i, moveData, vector))
   9596                 return true;
   9597 
   9598             // If no room found then realloc within block for lower offset
   9599             UINT64 offset = moveData.move.pSrcAllocation->GetOffset();
   9600             if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
   9601             {
   9602                 AllocationRequest request = {};
   9603                 if (metadata->CreateAllocationRequest(
   9604                     moveData.size,
   9605                     moveData.alignment,
   9606                     false,
   9607                     ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,
   9608                     &request))
   9609                 {
   9610                     if (metadata->GetAllocationOffset(request.allocHandle) < offset)
   9611                     {
   9612                         if (SUCCEEDED(vector.CommitAllocationRequest(
   9613                             request,
   9614                             block,
   9615                             moveData.size,
   9616                             moveData.alignment,
   9617                             this,
   9618                             &moveData.move.pDstTmpAllocation)))
   9619                         {
   9620                             m_Moves.push_back(moveData.move);
   9621                             if (IncrementCounters(moveData.size))
   9622                                 return true;
   9623                         }
   9624                     }
   9625                 }
   9626             }
   9627         }
   9628     }
   9629     return false;
   9630 }
   9631 
   9632 void DefragmentationContextPimpl::UpdateVectorStatistics(BlockVector& vector, StateBalanced& state)
   9633 {
   9634     size_t allocCount = 0;
   9635     size_t freeCount = 0;
   9636     state.avgFreeSize = 0;
   9637     state.avgAllocSize = 0;
   9638 
   9639     for (size_t i = 0; i < vector.GetBlockCount(); ++i)
   9640     {
   9641         BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
   9642 
   9643         allocCount += metadata->GetAllocationCount();
   9644         freeCount += metadata->GetFreeRegionsCount();
   9645         state.avgFreeSize += metadata->GetSumFreeSize();
   9646         state.avgAllocSize += metadata->GetSize();
   9647     }
   9648 
   9649     state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;
   9650     state.avgFreeSize /= freeCount;
   9651 }
   9652 #endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS
   9653 
   9654 #ifndef _D3D12MA_POOL_PIMPL_FUNCTIONS
   9655 PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc)
   9656     : m_Allocator(allocator),
   9657     m_Desc(desc),
   9658     m_BlockVector(NULL),
   9659     m_Name(NULL)
   9660 {
   9661     const bool explicitBlockSize = desc.BlockSize != 0;
   9662     const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE;
   9663     UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX;
   9664 
   9665 #ifndef __ID3D12Device4_INTERFACE_DEFINED__
   9666     D3D12MA_ASSERT(m_Desc.pProtectedSession == NULL);
   9667 #endif
   9668 
   9669     m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(
   9670         allocator, desc.HeapProperties, desc.HeapFlags,
   9671         preferredBlockSize,
   9672         desc.MinBlockCount, maxBlockCount,
   9673         explicitBlockSize,
   9674         D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT),
   9675         (desc.Flags & POOL_FLAG_ALGORITHM_MASK) != 0,
   9676         (desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0,
   9677         desc.pProtectedSession,
   9678         desc.ResidencyPriority);
   9679 }
   9680 
   9681 PoolPimpl::~PoolPimpl()
   9682 {
   9683     D3D12MA_ASSERT(m_PrevPool == NULL && m_NextPool == NULL);
   9684     FreeName();
   9685     D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);
   9686 }
   9687 
   9688 HRESULT PoolPimpl::Init()
   9689 {
   9690     m_CommittedAllocations.Init(m_Allocator->UseMutex(), m_Desc.HeapProperties.Type, this);
   9691     return m_BlockVector->CreateMinBlocks();
   9692 }
   9693 
   9694 void PoolPimpl::GetStatistics(Statistics& outStats)
   9695 {
   9696     ClearStatistics(outStats);
   9697     m_BlockVector->AddStatistics(outStats);
   9698     m_CommittedAllocations.AddStatistics(outStats);
   9699 }
   9700 
   9701 void PoolPimpl::CalculateStatistics(DetailedStatistics& outStats)
   9702 {
   9703     ClearDetailedStatistics(outStats);
   9704     AddDetailedStatistics(outStats);
   9705 }
   9706 
   9707 void PoolPimpl::AddDetailedStatistics(DetailedStatistics& inoutStats)
   9708 {
   9709     m_BlockVector->AddDetailedStatistics(inoutStats);
   9710     m_CommittedAllocations.AddDetailedStatistics(inoutStats);
   9711 }
   9712 
   9713 void PoolPimpl::SetName(LPCWSTR Name)
   9714 {
   9715     FreeName();
   9716 
   9717     if (Name)
   9718     {
   9719         const size_t nameCharCount = wcslen(Name) + 1;
   9720         m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);
   9721         memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));
   9722     }
   9723 }
   9724 
   9725 void PoolPimpl::FreeName()
   9726 {
   9727     if (m_Name)
   9728     {
   9729         const size_t nameCharCount = wcslen(m_Name) + 1;
   9730         D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);
   9731         m_Name = NULL;
   9732     }
   9733 }
   9734 #endif // _D3D12MA_POOL_PIMPL_FUNCTIONS
   9735 
   9736 
   9737 #ifndef _D3D12MA_PUBLIC_INTERFACE
   9738 HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
   9739 {
   9740     if (!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||
   9741         !(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))
   9742     {
   9743         D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");
   9744         return E_INVALIDARG;
   9745     }
   9746 
   9747     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
   9748 
   9749     ALLOCATION_CALLBACKS allocationCallbacks;
   9750     SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
   9751 
   9752     *ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);
   9753     HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);
   9754     if (FAILED(hr))
   9755     {
   9756         D3D12MA_DELETE(allocationCallbacks, *ppAllocator);
   9757         *ppAllocator = NULL;
   9758     }
   9759     return hr;
   9760 }
   9761 
   9762 HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)
   9763 {
   9764     if (!pDesc || !ppVirtualBlock)
   9765     {
   9766         D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");
   9767         return E_INVALIDARG;
   9768     }
   9769 
   9770     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
   9771 
   9772     ALLOCATION_CALLBACKS allocationCallbacks;
   9773     SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
   9774 
   9775     *ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);
   9776     return S_OK;
   9777 }
   9778 
   9779 #ifndef _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS
   9780 HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject)
   9781 {
   9782     if (ppvObject == NULL)
   9783         return E_POINTER;
   9784     if (riid == IID_IUnknown)
   9785     {
   9786         ++m_RefCount;
   9787         *ppvObject = this;
   9788         return S_OK;
   9789     }
   9790     *ppvObject = NULL;
   9791     return E_NOINTERFACE;
   9792 }
   9793 
   9794 ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef()
   9795 {
   9796     return ++m_RefCount;
   9797 }
   9798 
   9799 ULONG STDMETHODCALLTYPE IUnknownImpl::Release()
   9800 {
   9801     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
   9802 
   9803     const uint32_t newRefCount = --m_RefCount;
   9804     if (newRefCount == 0)
   9805         ReleaseThis();
   9806     return newRefCount;
   9807 }
   9808 #endif // _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS
   9809 
   9810 #ifndef _D3D12MA_ALLOCATION_FUNCTIONS
   9811 void Allocation::PackedData::SetType(Type type)
   9812 {
   9813     const UINT u = (UINT)type;
   9814     D3D12MA_ASSERT(u < (1u << 2));
   9815     m_Type = u;
   9816 }
   9817 
   9818 void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)
   9819 {
   9820     const UINT u = (UINT)resourceDimension;
   9821     D3D12MA_ASSERT(u < (1u << 3));
   9822     m_ResourceDimension = u;
   9823 }
   9824 
   9825 void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)
   9826 {
   9827     const UINT u = (UINT)resourceFlags;
   9828     D3D12MA_ASSERT(u < (1u << 24));
   9829     m_ResourceFlags = u;
   9830 }
   9831 
   9832 void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)
   9833 {
   9834     const UINT u = (UINT)textureLayout;
   9835     D3D12MA_ASSERT(u < (1u << 9));
   9836     m_TextureLayout = u;
   9837 }
   9838 
   9839 UINT64 Allocation::GetOffset() const
   9840 {
   9841     switch (m_PackedData.GetType())
   9842     {
   9843     case TYPE_COMMITTED:
   9844     case TYPE_HEAP:
   9845         return 0;
   9846     case TYPE_PLACED:
   9847         return m_Placed.block->m_pMetadata->GetAllocationOffset(m_Placed.allocHandle);
   9848     default:
   9849         D3D12MA_ASSERT(0);
   9850         return 0;
   9851     }
   9852 }
   9853 
   9854 void Allocation::SetResource(ID3D12Resource* pResource)
   9855 {
   9856     if (pResource != m_Resource)
   9857     {
   9858         if (m_Resource)
   9859             m_Resource->Release();
   9860         m_Resource = pResource;
   9861         if (m_Resource)
   9862             m_Resource->AddRef();
   9863     }
   9864 }
   9865 
   9866 ID3D12Heap* Allocation::GetHeap() const
   9867 {
   9868     switch (m_PackedData.GetType())
   9869     {
   9870     case TYPE_COMMITTED:
   9871         return NULL;
   9872     case TYPE_PLACED:
   9873         return m_Placed.block->GetHeap();
   9874     case TYPE_HEAP:
   9875         return m_Heap.heap;
   9876     default:
   9877         D3D12MA_ASSERT(0);
   9878         return 0;
   9879     }
   9880 }
   9881 
   9882 void Allocation::SetName(LPCWSTR Name)
   9883 {
   9884     FreeName();
   9885 
   9886     if (Name)
   9887     {
   9888         const size_t nameCharCount = wcslen(Name) + 1;
   9889         m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);
   9890         memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));
   9891     }
   9892 }
   9893 
   9894 void Allocation::ReleaseThis()
   9895 {
   9896     if (this == NULL)
   9897     {
   9898         return;
   9899     }
   9900 
   9901     SAFE_RELEASE(m_Resource);
   9902 
   9903     switch (m_PackedData.GetType())
   9904     {
   9905     case TYPE_COMMITTED:
   9906         m_Allocator->FreeCommittedMemory(this);
   9907         break;
   9908     case TYPE_PLACED:
   9909         m_Allocator->FreePlacedMemory(this);
   9910         break;
   9911     case TYPE_HEAP:
   9912         m_Allocator->FreeHeapMemory(this);
   9913         break;
   9914     }
   9915 
   9916     FreeName();
   9917 
   9918     m_Allocator->GetAllocationObjectAllocator().Free(this);
   9919 }
   9920 
   9921 Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized)
   9922     : m_Allocator{ allocator },
   9923     m_Size{ size },
   9924     m_Alignment{ alignment },
   9925     m_Resource{ NULL },
   9926     m_pPrivateData{ NULL },
   9927     m_Name{ NULL }
   9928 {
   9929     D3D12MA_ASSERT(allocator);
   9930 
   9931     m_PackedData.SetType(TYPE_COUNT);
   9932     m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN);
   9933     m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE);
   9934     m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN);
   9935     m_PackedData.SetWasZeroInitialized(wasZeroInitialized);
   9936 }
   9937 
   9938 void Allocation::InitCommitted(CommittedAllocationList* list)
   9939 {
   9940     m_PackedData.SetType(TYPE_COMMITTED);
   9941     m_Committed.list = list;
   9942     m_Committed.prev = NULL;
   9943     m_Committed.next = NULL;
   9944 }
   9945 
   9946 void Allocation::InitPlaced(AllocHandle allocHandle, NormalBlock* block)
   9947 {
   9948     m_PackedData.SetType(TYPE_PLACED);
   9949     m_Placed.allocHandle = allocHandle;
   9950     m_Placed.block = block;
   9951 }
   9952 
   9953 void Allocation::InitHeap(CommittedAllocationList* list, ID3D12Heap* heap)
   9954 {
   9955     m_PackedData.SetType(TYPE_HEAP);
   9956     m_Heap.list = list;
   9957     m_Committed.prev = NULL;
   9958     m_Committed.next = NULL;
   9959     m_Heap.heap = heap;
   9960 }
   9961 
   9962 void Allocation::SwapBlockAllocation(Allocation* allocation)
   9963 {
   9964     D3D12MA_ASSERT(allocation != NULL);
   9965     D3D12MA_ASSERT(m_PackedData.GetType() == TYPE_PLACED);
   9966     D3D12MA_ASSERT(allocation->m_PackedData.GetType() == TYPE_PLACED);
   9967 
   9968     D3D12MA_SWAP(m_Resource, allocation->m_Resource);
   9969     m_PackedData.SetWasZeroInitialized(allocation->m_PackedData.WasZeroInitialized());
   9970     m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, allocation);
   9971     D3D12MA_SWAP(m_Placed, allocation->m_Placed);
   9972     m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, this);
   9973 }
   9974 
   9975 AllocHandle Allocation::GetAllocHandle() const
   9976 {
   9977     switch (m_PackedData.GetType())
   9978     {
   9979     case TYPE_COMMITTED:
   9980     case TYPE_HEAP:
   9981         return (AllocHandle)0;
   9982     case TYPE_PLACED:
   9983         return m_Placed.allocHandle;
   9984     default:
   9985         D3D12MA_ASSERT(0);
   9986         return (AllocHandle)0;
   9987     }
   9988 }
   9989 
   9990 NormalBlock* Allocation::GetBlock()
   9991 {
   9992     switch (m_PackedData.GetType())
   9993     {
   9994     case TYPE_COMMITTED:
   9995     case TYPE_HEAP:
   9996         return NULL;
   9997     case TYPE_PLACED:
   9998         return m_Placed.block;
   9999     default:
  10000         D3D12MA_ASSERT(0);
  10001         return NULL;
  10002     }
  10003 }
  10004 
  10005 template<typename D3D12_RESOURCE_DESC_T>
  10006 void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc)
  10007 {
  10008     D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc);
  10009     m_Resource = resource;
  10010     m_PackedData.SetResourceDimension(pResourceDesc->Dimension);
  10011     m_PackedData.SetResourceFlags(pResourceDesc->Flags);
  10012     m_PackedData.SetTextureLayout(pResourceDesc->Layout);
  10013 }
  10014 
  10015 void Allocation::FreeName()
  10016 {
  10017     if (m_Name)
  10018     {
  10019         const size_t nameCharCount = wcslen(m_Name) + 1;
  10020         D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);
  10021         m_Name = NULL;
  10022     }
  10023 }
  10024 #endif // _D3D12MA_ALLOCATION_FUNCTIONS
  10025 
  10026 #ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
  10027 HRESULT DefragmentationContext::BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)
  10028 {
  10029     D3D12MA_ASSERT(pPassInfo);
  10030     return m_Pimpl->DefragmentPassBegin(*pPassInfo);
  10031 }
  10032 
  10033 HRESULT DefragmentationContext::EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)
  10034 {
  10035     D3D12MA_ASSERT(pPassInfo);
  10036     return m_Pimpl->DefragmentPassEnd(*pPassInfo);
  10037 }
  10038 
  10039 void DefragmentationContext::GetStats(DEFRAGMENTATION_STATS* pStats)
  10040 {
  10041     D3D12MA_ASSERT(pStats);
  10042     m_Pimpl->GetStats(*pStats);
  10043 }
  10044 
  10045 void DefragmentationContext::ReleaseThis()
  10046 {
  10047     if (this == NULL)
  10048     {
  10049         return;
  10050     }
  10051 
  10052     D3D12MA_DELETE(m_Pimpl->GetAllocs(), this);
  10053 }
  10054 
  10055 DefragmentationContext::DefragmentationContext(AllocatorPimpl* allocator,
  10056     const DEFRAGMENTATION_DESC& desc,
  10057     BlockVector* poolVector)
  10058     : m_Pimpl(D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContextPimpl)(allocator, desc, poolVector)) {}
  10059 
  10060 DefragmentationContext::~DefragmentationContext()
  10061 {
  10062     D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);
  10063 }
  10064 #endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
  10065 
  10066 #ifndef _D3D12MA_POOL_FUNCTIONS
  10067 POOL_DESC Pool::GetDesc() const
  10068 {
  10069     return m_Pimpl->GetDesc();
  10070 }
  10071 
  10072 void Pool::GetStatistics(Statistics* pStats)
  10073 {
  10074     D3D12MA_ASSERT(pStats);
  10075     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10076     m_Pimpl->GetStatistics(*pStats);
  10077 }
  10078 
  10079 void Pool::CalculateStatistics(DetailedStatistics* pStats)
  10080 {
  10081     D3D12MA_ASSERT(pStats);
  10082     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10083     m_Pimpl->CalculateStatistics(*pStats);
  10084 }
  10085 
  10086 void Pool::SetName(LPCWSTR Name)
  10087 {
  10088     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10089     m_Pimpl->SetName(Name);
  10090 }
  10091 
  10092 LPCWSTR Pool::GetName() const
  10093 {
  10094     return m_Pimpl->GetName();
  10095 }
  10096 
  10097 HRESULT Pool::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)
  10098 {
  10099     D3D12MA_ASSERT(pDesc && ppContext);
  10100 
  10101     // Check for support
  10102     if (m_Pimpl->GetBlockVector()->GetAlgorithm() & POOL_FLAG_ALGORITHM_LINEAR)
  10103         return E_NOINTERFACE;
  10104 
  10105     AllocatorPimpl* allocator = m_Pimpl->GetAllocator();
  10106     *ppContext = D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContext)(allocator, *pDesc, m_Pimpl->GetBlockVector());
  10107     return S_OK;
  10108 }
  10109 
  10110 void Pool::ReleaseThis()
  10111 {
  10112     if (this == NULL)
  10113     {
  10114         return;
  10115     }
  10116 
  10117     D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);
  10118 }
  10119 
  10120 Pool::Pool(Allocator* allocator, const POOL_DESC& desc)
  10121     : m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc)) {}
  10122 
  10123 Pool::~Pool()
  10124 {
  10125     m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapProperties.Type);
  10126 
  10127     D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);
  10128 }
  10129 #endif // _D3D12MA_POOL_FUNCTIONS
  10130 
  10131 #ifndef _D3D12MA_ALLOCATOR_FUNCTIONS
  10132 const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const
  10133 {
  10134     return m_Pimpl->GetD3D12Options();
  10135 }
  10136 
  10137 BOOL Allocator::IsUMA() const
  10138 {
  10139     return m_Pimpl->IsUMA();
  10140 }
  10141 
  10142 BOOL Allocator::IsCacheCoherentUMA() const
  10143 {
  10144     return m_Pimpl->IsCacheCoherentUMA();
  10145 }
  10146 
  10147 UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const
  10148 {
  10149     return m_Pimpl->GetMemoryCapacity(memorySegmentGroup);
  10150 }
  10151 
  10152 HRESULT Allocator::CreateResource(
  10153     const ALLOCATION_DESC* pAllocDesc,
  10154     const D3D12_RESOURCE_DESC* pResourceDesc,
  10155     D3D12_RESOURCE_STATES InitialResourceState,
  10156     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10157     Allocation** ppAllocation,
  10158     REFIID riidResource,
  10159     void** ppvResource)
  10160 {
  10161     if (!pAllocDesc || !pResourceDesc || !ppAllocation)
  10162     {
  10163         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource.");
  10164         return E_INVALIDARG;
  10165     }
  10166     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10167     return m_Pimpl->CreateResource(
  10168         pAllocDesc, 
  10169         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), 
  10170         ppAllocation, 
  10171         riidResource, 
  10172         ppvResource);
  10173 }
  10174 
  10175 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
  10176 HRESULT Allocator::CreateResource2(
  10177     const ALLOCATION_DESC* pAllocDesc,
  10178     const D3D12_RESOURCE_DESC1* pResourceDesc,
  10179     D3D12_RESOURCE_STATES InitialResourceState,
  10180     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10181     Allocation** ppAllocation,
  10182     REFIID riidResource,
  10183     void** ppvResource)
  10184 {
  10185     if (!pAllocDesc || !pResourceDesc || !ppAllocation)
  10186     {
  10187         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource2.");
  10188         return E_INVALIDARG;
  10189     }
  10190     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10191     return m_Pimpl->CreateResource(
  10192         pAllocDesc, 
  10193         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), 
  10194         ppAllocation, 
  10195         riidResource, 
  10196         ppvResource);
  10197 }
  10198 #endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__
  10199 
  10200 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
  10201 HRESULT Allocator::CreateResource3(
  10202     const ALLOCATION_DESC* pAllocDesc,
  10203     const D3D12_RESOURCE_DESC1* pResourceDesc,
  10204     D3D12_BARRIER_LAYOUT InitialLayout,
  10205     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10206     UINT32 NumCastableFormats,
  10207     DXGI_FORMAT* pCastableFormats,
  10208     Allocation** ppAllocation,
  10209     REFIID riidResource,
  10210     void** ppvResource)
  10211 {
  10212     if (!pAllocDesc || !pResourceDesc || !ppAllocation)
  10213     {
  10214         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource3.");
  10215         return E_INVALIDARG;
  10216     }
  10217     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10218     return m_Pimpl->CreateResource(
  10219         pAllocDesc, 
  10220         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats), 
  10221         ppAllocation, 
  10222         riidResource, 
  10223         ppvResource);
  10224 }
  10225 #endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__
  10226 
  10227 HRESULT Allocator::AllocateMemory(
  10228     const ALLOCATION_DESC* pAllocDesc,
  10229     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
  10230     Allocation** ppAllocation)
  10231 {
  10232     if (!ValidateAllocateMemoryParameters(pAllocDesc, pAllocInfo, ppAllocation))
  10233     {
  10234         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory.");
  10235         return E_INVALIDARG;
  10236     }
  10237     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10238         return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);
  10239 }
  10240 
  10241 HRESULT Allocator::CreateAliasingResource(
  10242     Allocation* pAllocation,
  10243     UINT64 AllocationLocalOffset,
  10244     const D3D12_RESOURCE_DESC* pResourceDesc,
  10245     D3D12_RESOURCE_STATES InitialResourceState,
  10246     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10247     REFIID riidResource,
  10248     void** ppvResource)
  10249 {
  10250     if (!pAllocation || !pResourceDesc || !ppvResource)
  10251     {
  10252         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");
  10253         return E_INVALIDARG;
  10254     }
  10255     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10256     return m_Pimpl->CreateAliasingResource(
  10257         pAllocation, 
  10258         AllocationLocalOffset, 
  10259         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), 
  10260         riidResource, 
  10261         ppvResource);
  10262 }
  10263 
  10264 #ifdef __ID3D12Device8_INTERFACE_DEFINED__
  10265 HRESULT Allocator::CreateAliasingResource1(
  10266     Allocation* pAllocation,
  10267     UINT64 AllocationLocalOffset,
  10268     const D3D12_RESOURCE_DESC1* pResourceDesc,
  10269     D3D12_RESOURCE_STATES InitialResourceState,
  10270     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10271     REFIID riidResource,
  10272     void** ppvResource)
  10273 {
  10274     if (!pAllocation || !pResourceDesc || !ppvResource)
  10275     {
  10276         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");
  10277         return E_INVALIDARG;
  10278     }
  10279     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10280     return m_Pimpl->CreateAliasingResource(
  10281         pAllocation, 
  10282         AllocationLocalOffset, 
  10283         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), 
  10284         riidResource, 
  10285         ppvResource);
  10286 }
  10287 #endif  // #ifdef __ID3D12Device8_INTERFACE_DEFINED__
  10288 
  10289 #ifdef __ID3D12Device10_INTERFACE_DEFINED__
  10290 HRESULT Allocator::CreateAliasingResource2(
  10291     Allocation* pAllocation,
  10292     UINT64 AllocationLocalOffset,
  10293     const D3D12_RESOURCE_DESC1* pResourceDesc,
  10294     D3D12_BARRIER_LAYOUT InitialLayout,
  10295     const D3D12_CLEAR_VALUE* pOptimizedClearValue,
  10296     UINT32 NumCastableFormats,
  10297     DXGI_FORMAT* pCastableFormats,
  10298     REFIID riidResource,
  10299     void** ppvResource)
  10300 {
  10301     if (!pAllocation || !pResourceDesc || !ppvResource)
  10302     {
  10303         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");
  10304         return E_INVALIDARG;
  10305     }
  10306     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10307     return m_Pimpl->CreateAliasingResource(
  10308         pAllocation, 
  10309         AllocationLocalOffset, 
  10310         CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),
  10311         riidResource, 
  10312         ppvResource);
  10313 }
  10314 #endif  // #ifdef __ID3D12Device10_INTERFACE_DEFINED__
  10315 
  10316 HRESULT Allocator::CreatePool(
  10317     const POOL_DESC* pPoolDesc,
  10318     Pool** ppPool)
  10319 {
  10320     if (!pPoolDesc || !ppPool ||
  10321         (pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount) ||
  10322         (pPoolDesc->MinAllocationAlignment > 0 && !IsPow2(pPoolDesc->MinAllocationAlignment)))
  10323     {
  10324         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");
  10325         return E_INVALIDARG;
  10326     }
  10327     if (!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))
  10328     {
  10329         D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");
  10330         return E_INVALIDARG;
  10331     }
  10332     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10333     * ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);
  10334     HRESULT hr = (*ppPool)->m_Pimpl->Init();
  10335     if (SUCCEEDED(hr))
  10336     {
  10337         m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapProperties.Type);
  10338     }
  10339     else
  10340     {
  10341         D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);
  10342         *ppPool = NULL;
  10343     }
  10344     return hr;
  10345 }
  10346 
  10347 void Allocator::SetCurrentFrameIndex(UINT frameIndex)
  10348 {
  10349     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10350     m_Pimpl->SetCurrentFrameIndex(frameIndex);
  10351 }
  10352 
  10353 void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget)
  10354 {
  10355     if (pLocalBudget == NULL && pNonLocalBudget == NULL)
  10356     {
  10357         return;
  10358     }
  10359     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10360     m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget);
  10361 }
  10362 
  10363 void Allocator::CalculateStatistics(TotalStatistics* pStats)
  10364 {
  10365     D3D12MA_ASSERT(pStats);
  10366     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10367     m_Pimpl->CalculateStatistics(*pStats);
  10368 }
  10369 
  10370 void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const
  10371 {
  10372     D3D12MA_ASSERT(ppStatsString);
  10373     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10374     m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);
  10375 }
  10376 
  10377 void Allocator::FreeStatsString(WCHAR* pStatsString) const
  10378 {
  10379     if (pStatsString != NULL)
  10380     {
  10381         D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10382         m_Pimpl->FreeStatsString(pStatsString);
  10383     }
  10384 }
  10385 
  10386 void Allocator::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)
  10387 {
  10388     D3D12MA_ASSERT(pDesc && ppContext);
  10389 
  10390     *ppContext = D3D12MA_NEW(m_Pimpl->GetAllocs(), DefragmentationContext)(m_Pimpl, *pDesc, NULL);
  10391 }
  10392 
  10393 void Allocator::ReleaseThis()
  10394 {
  10395     // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.
  10396     const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs();
  10397     D3D12MA_DELETE(allocationCallbacksCopy, this);
  10398 }
  10399 
  10400 Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)
  10401     : m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc)) {}
  10402 
  10403 Allocator::~Allocator()
  10404 {
  10405     D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);
  10406 }
  10407 #endif // _D3D12MA_ALLOCATOR_FUNCTIONS
  10408 
  10409 #ifndef _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS
  10410 BOOL VirtualBlock::IsEmpty() const
  10411 {
  10412     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10413     return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE;
  10414 }
  10415 
  10416 void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const
  10417 {
  10418     D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo);
  10419 
  10420     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10421     m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo);
  10422 }
  10423 
  10424 HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset)
  10425 {
  10426     if (!pDesc || !pAllocation || pDesc->Size == 0 || !IsPow2(pDesc->Alignment))
  10427     {
  10428         D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");
  10429         return E_INVALIDARG;
  10430     }
  10431 
  10432     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10433 
  10434     const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1;
  10435     AllocationRequest allocRequest = {};
  10436     if (m_Pimpl->m_Metadata->CreateAllocationRequest(
  10437         pDesc->Size,
  10438         alignment,
  10439         pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS,
  10440         pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK,
  10441         &allocRequest))
  10442     {
  10443         m_Pimpl->m_Metadata->Alloc(allocRequest, pDesc->Size, pDesc->pPrivateData);
  10444         D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10445         pAllocation->AllocHandle = allocRequest.allocHandle;
  10446 
  10447         if (pOffset)
  10448             *pOffset = m_Pimpl->m_Metadata->GetAllocationOffset(allocRequest.allocHandle);
  10449         return S_OK;
  10450     }
  10451 
  10452     pAllocation->AllocHandle = (AllocHandle)0;
  10453     if (pOffset)
  10454         *pOffset = UINT64_MAX;
  10455 
  10456     return E_OUTOFMEMORY;
  10457 }
  10458 
  10459 void VirtualBlock::FreeAllocation(VirtualAllocation allocation)
  10460 {
  10461     if (allocation.AllocHandle == (AllocHandle)0)
  10462         return;
  10463 
  10464     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10465 
  10466     m_Pimpl->m_Metadata->Free(allocation.AllocHandle);
  10467     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10468 }
  10469 
  10470 void VirtualBlock::Clear()
  10471 {
  10472     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10473 
  10474     m_Pimpl->m_Metadata->Clear();
  10475     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10476 }
  10477 
  10478 void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData)
  10479 {
  10480     D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0);
  10481 
  10482     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10483     m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData);
  10484 }
  10485 
  10486 void VirtualBlock::GetStatistics(Statistics* pStats) const
  10487 {
  10488     D3D12MA_ASSERT(pStats);
  10489     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10490     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10491     ClearStatistics(*pStats);
  10492     m_Pimpl->m_Metadata->AddStatistics(*pStats);
  10493 }
  10494 
  10495 void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const
  10496 {
  10497     D3D12MA_ASSERT(pStats);
  10498     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10499     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10500     ClearDetailedStatistics(*pStats);
  10501     m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats);
  10502 }
  10503 
  10504 void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const
  10505 {
  10506     D3D12MA_ASSERT(ppStatsString);
  10507 
  10508     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10509 
  10510     StringBuilder sb(m_Pimpl->m_AllocationCallbacks);
  10511     {
  10512         JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);
  10513         D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());
  10514         json.BeginObject();
  10515         m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json);
  10516         json.EndObject();
  10517     } // Scope for JsonWriter
  10518 
  10519     const size_t length = sb.GetLength();
  10520     WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);
  10521     memcpy(result, sb.GetData(), length * sizeof(WCHAR));
  10522     result[length] = L'\0';
  10523     *ppStatsString = result;
  10524 }
  10525 
  10526 void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const
  10527 {
  10528     if (pStatsString != NULL)
  10529     {
  10530         D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
  10531         D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);
  10532     }
  10533 }
  10534 
  10535 void VirtualBlock::ReleaseThis()
  10536 {
  10537     // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.
  10538     const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;
  10539     D3D12MA_DELETE(allocationCallbacksCopy, this);
  10540 }
  10541 
  10542 VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)
  10543     : m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc)) {}
  10544 
  10545 VirtualBlock::~VirtualBlock()
  10546 {
  10547     // THIS IS AN IMPORTANT ASSERT!
  10548     // Hitting it means you have some memory leak - unreleased allocations in this virtual block.
  10549     D3D12MA_ASSERT(m_Pimpl->m_Metadata->IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");
  10550 
  10551     D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);
  10552 }
  10553 #endif // _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS
  10554 #endif // _D3D12MA_PUBLIC_INTERFACE
  10555 } // namespace D3D12MA