• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "D3D12MemAlloc.h"
24 
25 #ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
26     #include <dxgi.h>
27     #if D3D12MA_DXGI_1_4
28         #include <dxgi1_4.h>
29     #endif
30 #endif
31 
32 #include <combaseapi.h>
33 #include <mutex>
34 #include <atomic>
35 #include <algorithm>
36 #include <utility>
37 #include <cstdlib>
38 #include <malloc.h> // for _aligned_malloc, _aligned_free
39 
40 ////////////////////////////////////////////////////////////////////////////////
41 ////////////////////////////////////////////////////////////////////////////////
42 //
43 // Configuration Begin
44 //
45 ////////////////////////////////////////////////////////////////////////////////
46 ////////////////////////////////////////////////////////////////////////////////
47 
48 #ifndef D3D12MA_ASSERT
49     #include <cassert>
50     #define D3D12MA_ASSERT(cond) assert(cond)
51 #endif
52 
53 // Assert that will be called very often, like inside data structures e.g. operator[].
54 // Making it non-empty can make program slow.
55 #ifndef D3D12MA_HEAVY_ASSERT
56     #ifdef _DEBUG
57         #define D3D12MA_HEAVY_ASSERT(expr)   //D3D12MA_ASSERT(expr)
58     #else
59         #define D3D12MA_HEAVY_ASSERT(expr)
60     #endif
61 #endif
62 
63 #ifndef D3D12MA_DEBUG_ALIGNMENT
64     /*
65     Minimum alignment of all allocations, in bytes.
66     Set to more than 1 for debugging purposes only. Must be power of two.
67     */
68     #define D3D12MA_DEBUG_ALIGNMENT (1)
69 #endif
70 
71 #ifndef D3D12MA_DEBUG_MARGIN
72     // Minimum margin before and after every allocation, in bytes.
73     // Set nonzero for debugging purposes only.
74     #define D3D12MA_DEBUG_MARGIN (0)
75 #endif
76 
77 #ifndef D3D12MA_DEBUG_GLOBAL_MUTEX
78     /*
79     Set this to 1 for debugging purposes only, to enable single mutex protecting all
80     entry calls to the library. Can be useful for debugging multithreading issues.
81     */
82     #define D3D12MA_DEBUG_GLOBAL_MUTEX (0)
83 #endif
84 
85 #ifndef D3D12MA_DEFAULT_BLOCK_SIZE
86    /// Default size of a block allocated as single ID3D12Heap.
87    #define D3D12MA_DEFAULT_BLOCK_SIZE (256ull * 1024 * 1024)
88 #endif
89 
90 ////////////////////////////////////////////////////////////////////////////////
91 ////////////////////////////////////////////////////////////////////////////////
92 //
93 // Configuration End
94 //
95 ////////////////////////////////////////////////////////////////////////////////
96 ////////////////////////////////////////////////////////////////////////////////
97 
98 
99 namespace D3D12MA
100 {
101 
102 ////////////////////////////////////////////////////////////////////////////////
103 // Private globals - CPU memory allocation
104 
DefaultAllocate(size_t Size,size_t Alignment,void *)105 static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pUserData*/)
106 {
107     return _aligned_malloc(Size, Alignment);
108 }
DefaultFree(void * pMemory,void *)109 static void DefaultFree(void* pMemory, void* /*pUserData*/)
110 {
111     return _aligned_free(pMemory);
112 }
113 
Malloc(const ALLOCATION_CALLBACKS & allocs,size_t size,size_t alignment)114 static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)
115 {
116     void* const result = (*allocs.pAllocate)(size, alignment, allocs.pUserData);
117     D3D12MA_ASSERT(result);
118     return result;
119 }
Free(const ALLOCATION_CALLBACKS & allocs,void * memory)120 static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)
121 {
122     (*allocs.pFree)(memory, allocs.pUserData);
123 }
124 
125 template<typename T>
Allocate(const ALLOCATION_CALLBACKS & allocs)126 static T* Allocate(const ALLOCATION_CALLBACKS& allocs)
127 {
128     return (T*)Malloc(allocs, sizeof(T), __alignof(T));
129 }
130 template<typename T>
AllocateArray(const ALLOCATION_CALLBACKS & allocs,size_t count)131 static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)
132 {
133     return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));
134 }
135 
136 #define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)
137 #define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)
138 
139 template<typename T>
D3D12MA_DELETE(const ALLOCATION_CALLBACKS & allocs,T * memory)140 void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)
141 {
142     if(memory)
143     {
144         memory->~T();
145         Free(allocs, memory);
146     }
147 }
148 template<typename T>
D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS & allocs,T * memory,size_t count)149 void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)
150 {
151     if(memory)
152     {
153         for(size_t i = count; i--; )
154         {
155             memory[i].~T();
156         }
157         Free(allocs, memory);
158     }
159 }
160 
SetupAllocationCallbacks(ALLOCATION_CALLBACKS & outAllocs,const ALLOCATION_CALLBACKS * allocationCallbacks)161 static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)
162 {
163     if(allocationCallbacks)
164     {
165         outAllocs = *allocationCallbacks;
166         D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);
167     }
168     else
169     {
170         outAllocs.pAllocate = &DefaultAllocate;
171         outAllocs.pFree = &DefaultFree;
172         outAllocs.pUserData = NULL;
173     }
174 }
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 // Private globals - basic facilities
178 
179 #define SAFE_RELEASE(ptr)   do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)
180 
181 #define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \
182         D3D12MA_ASSERT(0 && "Validation failed: " #cond); \
183         return false; \
184     } } while(false)
185 
186 const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;
187 
188 template<typename T>
D3D12MA_MIN(const T & a,const T & b)189 static inline T D3D12MA_MIN(const T& a, const T& b)
190 {
191     return a <= b ? a : b;
192 }
193 template<typename T>
D3D12MA_MAX(const T & a,const T & b)194 static inline T D3D12MA_MAX(const T& a, const T& b)
195 {
196     return a <= b ? b : a;
197 }
198 
199 template<typename T>
D3D12MA_SWAP(T & a,T & b)200 static inline void D3D12MA_SWAP(T& a, T& b)
201 {
202     T tmp = a; a = b; b = tmp;
203 }
204 
205 #ifndef D3D12MA_MUTEX
206     class Mutex
207     {
208     public:
Lock()209         void Lock() { m_Mutex.lock(); }
Unlock()210         void Unlock() { m_Mutex.unlock(); }
211     private:
212         std::mutex m_Mutex;
213     };
214     #define D3D12MA_MUTEX Mutex
215 #endif
216 
217 #if !defined(_WIN32) || !defined(WINVER) || WINVER < 0x0600
218     #error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.
219 #endif
220 
221 #ifndef D3D12MA_RW_MUTEX
222     class RWMutex
223     {
224     public:
RWMutex()225         RWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()226         void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()227         void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
LockWrite()228         void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()229         void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
230     private:
231         SRWLOCK m_Lock;
232     };
233     #define D3D12MA_RW_MUTEX RWMutex
234 #endif
235 
236 /*
237 If providing your own implementation, you need to implement a subset of std::atomic.
238 */
239 #ifndef D3D12MA_ATOMIC_UINT32
240     #define D3D12MA_ATOMIC_UINT32 std::atomic<UINT>
241 #endif
242 
243 #ifndef D3D12MA_ATOMIC_UINT64
244     #define D3D12MA_ATOMIC_UINT64 std::atomic<UINT64>
245 #endif
246 
247 /*
248 Returns true if given number is a power of two.
249 T must be unsigned integer number or signed integer but always nonnegative.
250 For 0 returns true.
251 */
252 template <typename T>
IsPow2(T x)253 inline bool IsPow2(T x)
254 {
255     return (x & (x-1)) == 0;
256 }
257 
258 // Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.
259 // Use types like UINT, uint64_t as T.
260 template <typename T>
AlignUp(T val,T alignment)261 static inline T AlignUp(T val, T alignment)
262 {
263     D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
264 	return (val + alignment - 1) & ~(alignment - 1);
265 }
266 // Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.
267 // Use types like UINT, uint64_t as T.
268 template <typename T>
AlignDown(T val,T alignment)269 static inline T AlignDown(T val, T alignment)
270 {
271     D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
272     return val & ~(alignment - 1);
273 }
274 
275 // Division with mathematical rounding to nearest number.
276 template <typename T>
RoundDiv(T x,T y)277 static inline T RoundDiv(T x, T y)
278 {
279 	return (x + (y / (T)2)) / y;
280 }
281 template <typename T>
DivideRoudingUp(T x,T y)282 static inline T DivideRoudingUp(T x, T y)
283 {
284     return (x + y - 1) / y;
285 }
286 
287 // Returns smallest power of 2 greater or equal to v.
NextPow2(UINT v)288 static inline UINT NextPow2(UINT v)
289 {
290 	v--;
291     v |= v >> 1;
292     v |= v >> 2;
293     v |= v >> 4;
294     v |= v >> 8;
295     v |= v >> 16;
296     v++;
297     return v;
298 }
NextPow2(uint64_t v)299 static inline uint64_t NextPow2(uint64_t v)
300 {
301 	v--;
302     v |= v >> 1;
303     v |= v >> 2;
304     v |= v >> 4;
305     v |= v >> 8;
306     v |= v >> 16;
307     v |= v >> 32;
308     v++;
309     return v;
310 }
311 
312 // Returns largest power of 2 less or equal to v.
PrevPow2(UINT v)313 static inline UINT PrevPow2(UINT v)
314 {
315     v |= v >> 1;
316     v |= v >> 2;
317     v |= v >> 4;
318     v |= v >> 8;
319     v |= v >> 16;
320     v = v ^ (v >> 1);
321     return v;
322 }
PrevPow2(uint64_t v)323 static inline uint64_t PrevPow2(uint64_t v)
324 {
325     v |= v >> 1;
326     v |= v >> 2;
327     v |= v >> 4;
328     v |= v >> 8;
329     v |= v >> 16;
330     v |= v >> 32;
331     v = v ^ (v >> 1);
332     return v;
333 }
334 
StrIsEmpty(const char * pStr)335 static inline bool StrIsEmpty(const char* pStr)
336 {
337     return pStr == NULL || *pStr == '\0';
338 }
339 
340 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
341 struct MutexLock
342 {
343 public:
MutexLockD3D12MA::MutexLock344     MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :
345         m_pMutex(useMutex ? &mutex : NULL)
346     {
347         if(m_pMutex)
348         {
349             m_pMutex->Lock();
350         }
351     }
~MutexLockD3D12MA::MutexLock352     ~MutexLock()
353     {
354         if(m_pMutex)
355         {
356             m_pMutex->Unlock();
357         }
358     }
359 private:
360     D3D12MA_MUTEX* m_pMutex;
361 
362     D3D12MA_CLASS_NO_COPY(MutexLock)
363 };
364 
365 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
366 struct MutexLockRead
367 {
368 public:
MutexLockReadD3D12MA::MutexLockRead369     MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex) :
370         m_pMutex(useMutex ? &mutex : NULL)
371     {
372         if(m_pMutex)
373         {
374             m_pMutex->LockRead();
375         }
376     }
~MutexLockReadD3D12MA::MutexLockRead377     ~MutexLockRead()
378     {
379         if(m_pMutex)
380         {
381             m_pMutex->UnlockRead();
382         }
383     }
384 private:
385     D3D12MA_RW_MUTEX* m_pMutex;
386 
387     D3D12MA_CLASS_NO_COPY(MutexLockRead)
388 };
389 
390 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
391 struct MutexLockWrite
392 {
393 public:
MutexLockWriteD3D12MA::MutexLockWrite394     MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex) :
395         m_pMutex(useMutex ? &mutex : NULL)
396     {
397         if(m_pMutex)
398         {
399             m_pMutex->LockWrite();
400         }
401     }
~MutexLockWriteD3D12MA::MutexLockWrite402     ~MutexLockWrite()
403     {
404         if(m_pMutex)
405         {
406             m_pMutex->UnlockWrite();
407         }
408     }
409 private:
410     D3D12MA_RW_MUTEX* m_pMutex;
411 
412     D3D12MA_CLASS_NO_COPY(MutexLockWrite)
413 };
414 
415 #if D3D12MA_DEBUG_GLOBAL_MUTEX
416     static D3D12MA_MUTEX g_DebugGlobalMutex;
417     #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);
418 #else
419     #define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
420 #endif
421 
422 // Minimum size of a free suballocation to register it in the free suballocation collection.
423 static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
424 
425 /*
426 Performs binary search and returns iterator to first element that is greater or
427 equal to `key`, according to comparison `cmp`.
428 
429 Cmp should return true if first argument is less than second argument.
430 
431 Returned value is the found element, if present in the collection or place where
432 new element with value (key) should be inserted.
433 */
434 template <typename CmpLess, typename IterT, typename KeyT>
BinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)435 static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
436 {
437     size_t down = 0, up = (end - beg);
438     while(down < up)
439     {
440         const size_t mid = (down + up) / 2;
441         if(cmp(*(beg+mid), key))
442         {
443             down = mid + 1;
444         }
445         else
446         {
447             up = mid;
448         }
449     }
450     return beg + down;
451 }
452 
453 /*
454 Performs binary search and returns iterator to an element that is equal to `key`,
455 according to comparison `cmp`.
456 
457 Cmp should return true if first argument is less than second argument.
458 
459 Returned value is the found element, if present in the collection or end if not
460 found.
461 */
462 template<typename CmpLess, typename IterT, typename KeyT>
BinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)463 IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
464 {
465     IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);
466     if(it == end ||
467         (!cmp(*it, value) && !cmp(value, *it)))
468     {
469         return it;
470     }
471     return end;
472 }
473 
474 struct PointerLess
475 {
operator ()D3D12MA::PointerLess476     bool operator()(const void* lhs, const void* rhs) const
477     {
478         return lhs < rhs;
479     }
480 };
481 
HeapTypeToIndex(D3D12_HEAP_TYPE type)482 static UINT HeapTypeToIndex(D3D12_HEAP_TYPE type)
483 {
484     switch(type)
485     {
486     case D3D12_HEAP_TYPE_DEFAULT:  return 0;
487     case D3D12_HEAP_TYPE_UPLOAD:   return 1;
488     case D3D12_HEAP_TYPE_READBACK: return 2;
489     default: D3D12MA_ASSERT(0); return UINT_MAX;
490     }
491 }
492 
493 static const WCHAR* const HeapTypeNames[] = {
494     L"DEFAULT",
495     L"UPLOAD",
496     L"READBACK",
497 };
498 
499 // Stat helper functions
500 
AddStatInfo(StatInfo & dst,const StatInfo & src)501 static void AddStatInfo(StatInfo& dst, const StatInfo& src)
502 {
503     dst.BlockCount += src.BlockCount;
504     dst.AllocationCount += src.AllocationCount;
505     dst.UnusedRangeCount += src.UnusedRangeCount;
506     dst.UsedBytes += src.UsedBytes;
507     dst.UnusedBytes += src.UnusedBytes;
508     dst.AllocationSizeMin = D3D12MA_MIN(dst.AllocationSizeMin, src.AllocationSizeMin);
509     dst.AllocationSizeMax = D3D12MA_MAX(dst.AllocationSizeMax, src.AllocationSizeMax);
510     dst.UnusedRangeSizeMin = D3D12MA_MIN(dst.UnusedRangeSizeMin, src.UnusedRangeSizeMin);
511     dst.UnusedRangeSizeMax = D3D12MA_MAX(dst.UnusedRangeSizeMax, src.UnusedRangeSizeMax);
512 }
513 
PostProcessStatInfo(StatInfo & statInfo)514 static void PostProcessStatInfo(StatInfo& statInfo)
515 {
516     statInfo.AllocationSizeAvg = statInfo.AllocationCount ?
517         statInfo.UsedBytes / statInfo.AllocationCount : 0;
518     statInfo.UnusedRangeSizeAvg = statInfo.UnusedRangeCount ?
519         statInfo.UnusedBytes / statInfo.UnusedRangeCount : 0;
520 }
521 
HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags)522 static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags)
523 {
524     /*
525     Documentation of D3D12_HEAP_DESC structure says:
526 
527     - D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT   defined as 64KB.
528     - D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT   defined as 4MB. An
529     application must decide whether the heap will contain multi-sample
530     anti-aliasing (MSAA), in which case, the application must choose [this flag].
531 
532     https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc
533     */
534 
535     const D3D12_HEAP_FLAGS denyAllTexturesFlags =
536         D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
537     const bool canContainAnyTextures =
538         (flags & denyAllTexturesFlags) != denyAllTexturesFlags;
539     return canContainAnyTextures ?
540         D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
541 }
542 
IsFormatCompressed(DXGI_FORMAT format)543 static bool IsFormatCompressed(DXGI_FORMAT format)
544 {
545     switch(format)
546     {
547     case DXGI_FORMAT_BC1_TYPELESS:
548     case DXGI_FORMAT_BC1_UNORM:
549     case DXGI_FORMAT_BC1_UNORM_SRGB:
550     case DXGI_FORMAT_BC2_TYPELESS:
551     case DXGI_FORMAT_BC2_UNORM:
552     case DXGI_FORMAT_BC2_UNORM_SRGB:
553     case DXGI_FORMAT_BC3_TYPELESS:
554     case DXGI_FORMAT_BC3_UNORM:
555     case DXGI_FORMAT_BC3_UNORM_SRGB:
556     case DXGI_FORMAT_BC4_TYPELESS:
557     case DXGI_FORMAT_BC4_UNORM:
558     case DXGI_FORMAT_BC4_SNORM:
559     case DXGI_FORMAT_BC5_TYPELESS:
560     case DXGI_FORMAT_BC5_UNORM:
561     case DXGI_FORMAT_BC5_SNORM:
562     case DXGI_FORMAT_BC6H_TYPELESS:
563     case DXGI_FORMAT_BC6H_UF16:
564     case DXGI_FORMAT_BC6H_SF16:
565     case DXGI_FORMAT_BC7_TYPELESS:
566     case DXGI_FORMAT_BC7_UNORM:
567     case DXGI_FORMAT_BC7_UNORM_SRGB:
568         return true;
569     default:
570         return false;
571     }
572 }
573 
574 // Only some formats are supported. For others it returns 0.
GetBitsPerPixel(DXGI_FORMAT format)575 static UINT GetBitsPerPixel(DXGI_FORMAT format)
576 {
577     switch(format)
578     {
579     case DXGI_FORMAT_R32G32B32A32_TYPELESS:
580     case DXGI_FORMAT_R32G32B32A32_FLOAT:
581     case DXGI_FORMAT_R32G32B32A32_UINT:
582     case DXGI_FORMAT_R32G32B32A32_SINT:
583         return 128;
584     case DXGI_FORMAT_R32G32B32_TYPELESS:
585     case DXGI_FORMAT_R32G32B32_FLOAT:
586     case DXGI_FORMAT_R32G32B32_UINT:
587     case DXGI_FORMAT_R32G32B32_SINT:
588         return 96;
589     case DXGI_FORMAT_R16G16B16A16_TYPELESS:
590     case DXGI_FORMAT_R16G16B16A16_FLOAT:
591     case DXGI_FORMAT_R16G16B16A16_UNORM:
592     case DXGI_FORMAT_R16G16B16A16_UINT:
593     case DXGI_FORMAT_R16G16B16A16_SNORM:
594     case DXGI_FORMAT_R16G16B16A16_SINT:
595         return 64;
596     case DXGI_FORMAT_R32G32_TYPELESS:
597     case DXGI_FORMAT_R32G32_FLOAT:
598     case DXGI_FORMAT_R32G32_UINT:
599     case DXGI_FORMAT_R32G32_SINT:
600         return 64;
601     case DXGI_FORMAT_R32G8X24_TYPELESS:
602     case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
603     case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
604     case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
605         return 64;
606     case DXGI_FORMAT_R10G10B10A2_TYPELESS:
607     case DXGI_FORMAT_R10G10B10A2_UNORM:
608     case DXGI_FORMAT_R10G10B10A2_UINT:
609     case DXGI_FORMAT_R11G11B10_FLOAT:
610         return 32;
611     case DXGI_FORMAT_R8G8B8A8_TYPELESS:
612     case DXGI_FORMAT_R8G8B8A8_UNORM:
613     case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
614     case DXGI_FORMAT_R8G8B8A8_UINT:
615     case DXGI_FORMAT_R8G8B8A8_SNORM:
616     case DXGI_FORMAT_R8G8B8A8_SINT:
617         return 32;
618     case DXGI_FORMAT_R16G16_TYPELESS:
619     case DXGI_FORMAT_R16G16_FLOAT:
620     case DXGI_FORMAT_R16G16_UNORM:
621     case DXGI_FORMAT_R16G16_UINT:
622     case DXGI_FORMAT_R16G16_SNORM:
623     case DXGI_FORMAT_R16G16_SINT:
624         return 32;
625     case DXGI_FORMAT_R32_TYPELESS:
626     case DXGI_FORMAT_D32_FLOAT:
627     case DXGI_FORMAT_R32_FLOAT:
628     case DXGI_FORMAT_R32_UINT:
629     case DXGI_FORMAT_R32_SINT:
630         return 32;
631     case DXGI_FORMAT_R24G8_TYPELESS:
632     case DXGI_FORMAT_D24_UNORM_S8_UINT:
633     case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
634     case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
635         return 32;
636     case DXGI_FORMAT_R8G8_TYPELESS:
637     case DXGI_FORMAT_R8G8_UNORM:
638     case DXGI_FORMAT_R8G8_UINT:
639     case DXGI_FORMAT_R8G8_SNORM:
640     case DXGI_FORMAT_R8G8_SINT:
641         return 16;
642     case DXGI_FORMAT_R16_TYPELESS:
643     case DXGI_FORMAT_R16_FLOAT:
644     case DXGI_FORMAT_D16_UNORM:
645     case DXGI_FORMAT_R16_UNORM:
646     case DXGI_FORMAT_R16_UINT:
647     case DXGI_FORMAT_R16_SNORM:
648     case DXGI_FORMAT_R16_SINT:
649         return 16;
650     case DXGI_FORMAT_R8_TYPELESS:
651     case DXGI_FORMAT_R8_UNORM:
652     case DXGI_FORMAT_R8_UINT:
653     case DXGI_FORMAT_R8_SNORM:
654     case DXGI_FORMAT_R8_SINT:
655     case DXGI_FORMAT_A8_UNORM:
656         return 8;
657     case DXGI_FORMAT_BC1_TYPELESS:
658     case DXGI_FORMAT_BC1_UNORM:
659     case DXGI_FORMAT_BC1_UNORM_SRGB:
660         return 4;
661     case DXGI_FORMAT_BC2_TYPELESS:
662     case DXGI_FORMAT_BC2_UNORM:
663     case DXGI_FORMAT_BC2_UNORM_SRGB:
664         return 8;
665     case DXGI_FORMAT_BC3_TYPELESS:
666     case DXGI_FORMAT_BC3_UNORM:
667     case DXGI_FORMAT_BC3_UNORM_SRGB:
668         return 8;
669     case DXGI_FORMAT_BC4_TYPELESS:
670     case DXGI_FORMAT_BC4_UNORM:
671     case DXGI_FORMAT_BC4_SNORM:
672         return 4;
673     case DXGI_FORMAT_BC5_TYPELESS:
674     case DXGI_FORMAT_BC5_UNORM:
675     case DXGI_FORMAT_BC5_SNORM:
676         return 8;
677     case DXGI_FORMAT_BC6H_TYPELESS:
678     case DXGI_FORMAT_BC6H_UF16:
679     case DXGI_FORMAT_BC6H_SF16:
680         return 8;
681     case DXGI_FORMAT_BC7_TYPELESS:
682     case DXGI_FORMAT_BC7_UNORM:
683     case DXGI_FORMAT_BC7_UNORM_SRGB:
684         return 8;
685     default:
686         return 0;
687     }
688 }
689 
690 // This algorithm is overly conservative.
CanUseSmallAlignment(const D3D12_RESOURCE_DESC & resourceDesc)691 static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC& resourceDesc)
692 {
693     if(resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
694         return false;
695     if((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)
696         return false;
697     if(resourceDesc.SampleDesc.Count > 1)
698         return false;
699     if(resourceDesc.DepthOrArraySize != 1)
700         return false;
701 
702     UINT sizeX = (UINT)resourceDesc.Width;
703     UINT sizeY = resourceDesc.Height;
704     UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);
705     if(bitsPerPixel == 0)
706         return false;
707 
708     if(IsFormatCompressed(resourceDesc.Format))
709     {
710         sizeX = DivideRoudingUp(sizeX / 4, 1u);
711         sizeY = DivideRoudingUp(sizeY / 4, 1u);
712         bitsPerPixel *= 16;
713     }
714 
715     UINT tileSizeX = 0, tileSizeY = 0;
716     switch(bitsPerPixel)
717     {
718     case   8: tileSizeX = 64; tileSizeY = 64; break;
719     case  16: tileSizeX = 64; tileSizeY = 32; break;
720     case  32: tileSizeX = 32; tileSizeY = 32; break;
721     case  64: tileSizeX = 32; tileSizeY = 16; break;
722     case 128: tileSizeX = 16; tileSizeY = 16; break;
723     default: return false;
724     }
725 
726     const UINT tileCount = DivideRoudingUp(sizeX, tileSizeX) * DivideRoudingUp(sizeY, tileSizeY);
727     return tileCount <= 16;
728 }
729 
GetExtraHeapFlagsToIgnore()730 static D3D12_HEAP_FLAGS GetExtraHeapFlagsToIgnore()
731 {
732     D3D12_HEAP_FLAGS result =
733         D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
734     return result;
735 }
736 
IsHeapTypeValid(D3D12_HEAP_TYPE type)737 static inline bool IsHeapTypeValid(D3D12_HEAP_TYPE type)
738 {
739     return type == D3D12_HEAP_TYPE_DEFAULT ||
740         type == D3D12_HEAP_TYPE_UPLOAD ||
741         type == D3D12_HEAP_TYPE_READBACK;
742 }
743 
744 ////////////////////////////////////////////////////////////////////////////////
745 // Private class Vector
746 
747 /*
748 Dynamically resizing continuous array. Class with interface similar to std::vector.
749 T must be POD because constructors and destructors are not called and memcpy is
750 used for these objects.
751 */
752 template<typename T>
753 class Vector
754 {
755 public:
756     typedef T value_type;
757 
758     // allocationCallbacks externally owned, must outlive this object.
Vector(const ALLOCATION_CALLBACKS & allocationCallbacks)759     Vector(const ALLOCATION_CALLBACKS& allocationCallbacks) :
760         m_AllocationCallbacks(allocationCallbacks),
761         m_pArray(NULL),
762         m_Count(0),
763         m_Capacity(0)
764     {
765     }
766 
Vector(size_t count,const ALLOCATION_CALLBACKS & allocationCallbacks)767     Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks) :
768         m_AllocationCallbacks(allocationCallbacks),
769         m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),
770         m_Count(count),
771         m_Capacity(count)
772     {
773     }
774 
Vector(const Vector<T> & src)775     Vector(const Vector<T>& src) :
776         m_AllocationCallbacks(src.m_AllocationCallbacks),
777         m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),
778         m_Count(src.m_Count),
779         m_Capacity(src.m_Count)
780     {
781         if(m_Count > 0)
782         {
783             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
784         }
785     }
786 
~Vector()787     ~Vector()
788     {
789         Free(m_AllocationCallbacks, m_pArray);
790     }
791 
operator =(const Vector<T> & rhs)792     Vector& operator=(const Vector<T>& rhs)
793     {
794         if(&rhs != this)
795         {
796             resize(rhs.m_Count);
797             if(m_Count != 0)
798             {
799                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
800             }
801         }
802         return *this;
803     }
804 
empty() const805     bool empty() const { return m_Count == 0; }
size() const806     size_t size() const { return m_Count; }
data()807     T* data() { return m_pArray; }
data() const808     const T* data() const { return m_pArray; }
809 
operator [](size_t index)810     T& operator[](size_t index)
811     {
812         D3D12MA_HEAVY_ASSERT(index < m_Count);
813         return m_pArray[index];
814     }
operator [](size_t index) const815     const T& operator[](size_t index) const
816     {
817         D3D12MA_HEAVY_ASSERT(index < m_Count);
818         return m_pArray[index];
819     }
820 
front()821     T& front()
822     {
823         D3D12MA_HEAVY_ASSERT(m_Count > 0);
824         return m_pArray[0];
825     }
front() const826     const T& front() const
827     {
828         D3D12MA_HEAVY_ASSERT(m_Count > 0);
829         return m_pArray[0];
830     }
back()831     T& back()
832     {
833         D3D12MA_HEAVY_ASSERT(m_Count > 0);
834         return m_pArray[m_Count - 1];
835     }
back() const836     const T& back() const
837     {
838         D3D12MA_HEAVY_ASSERT(m_Count > 0);
839         return m_pArray[m_Count - 1];
840     }
841 
reserve(size_t newCapacity,bool freeMemory=false)842     void reserve(size_t newCapacity, bool freeMemory = false)
843     {
844         newCapacity = D3D12MA_MAX(newCapacity, m_Count);
845 
846         if((newCapacity < m_Capacity) && !freeMemory)
847         {
848             newCapacity = m_Capacity;
849         }
850 
851         if(newCapacity != m_Capacity)
852         {
853             T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
854             if(m_Count != 0)
855             {
856                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
857             }
858             Free(m_AllocationCallbacks, m_pArray);
859             m_Capacity = newCapacity;
860             m_pArray = newArray;
861         }
862     }
863 
resize(size_t newCount,bool freeMemory=false)864     void resize(size_t newCount, bool freeMemory = false)
865     {
866         size_t newCapacity = m_Capacity;
867         if(newCount > m_Capacity)
868         {
869             newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));
870         }
871         else if(freeMemory)
872         {
873             newCapacity = newCount;
874         }
875 
876         if(newCapacity != m_Capacity)
877         {
878             T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
879             const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);
880             if(elementsToCopy != 0)
881             {
882                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
883             }
884             Free(m_AllocationCallbacks, m_pArray);
885             m_Capacity = newCapacity;
886             m_pArray = newArray;
887         }
888 
889         m_Count = newCount;
890     }
891 
clear(bool freeMemory=false)892     void clear(bool freeMemory = false)
893     {
894         resize(0, freeMemory);
895     }
896 
insert(size_t index,const T & src)897     void insert(size_t index, const T& src)
898     {
899         D3D12MA_HEAVY_ASSERT(index <= m_Count);
900         const size_t oldCount = size();
901         resize(oldCount + 1);
902         if(index < oldCount)
903         {
904             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
905         }
906         m_pArray[index] = src;
907     }
908 
remove(size_t index)909     void remove(size_t index)
910     {
911         D3D12MA_HEAVY_ASSERT(index < m_Count);
912         const size_t oldCount = size();
913         if(index < oldCount - 1)
914         {
915             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
916         }
917         resize(oldCount - 1);
918     }
919 
push_back(const T & src)920     void push_back(const T& src)
921     {
922         const size_t newIndex = size();
923         resize(newIndex + 1);
924         m_pArray[newIndex] = src;
925     }
926 
pop_back()927     void pop_back()
928     {
929         D3D12MA_HEAVY_ASSERT(m_Count > 0);
930         resize(size() - 1);
931     }
932 
push_front(const T & src)933     void push_front(const T& src)
934     {
935         insert(0, src);
936     }
937 
pop_front()938     void pop_front()
939     {
940         D3D12MA_HEAVY_ASSERT(m_Count > 0);
941         remove(0);
942     }
943 
944     typedef T* iterator;
945 
begin()946     iterator begin() { return m_pArray; }
end()947     iterator end() { return m_pArray + m_Count; }
948 
949     template<typename CmpLess>
InsertSorted(const T & value,const CmpLess & cmp)950     size_t InsertSorted(const T& value, const CmpLess& cmp)
951     {
952         const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(
953             m_pArray,
954             m_pArray + m_Count,
955             value,
956             cmp) - m_pArray;
957         insert(indexToInsert, value);
958         return indexToInsert;
959     }
960 
961     template<typename CmpLess>
RemoveSorted(const T & value,const CmpLess & cmp)962     bool RemoveSorted(const T& value, const CmpLess& cmp)
963     {
964         const iterator it = BinaryFindFirstNotLess(
965             m_pArray,
966             m_pArray + m_Count,
967             value,
968             cmp);
969         if((it != end()) && !cmp(*it, value) && !cmp(value, *it))
970         {
971             size_t indexToRemove = it - begin();
972             remove(indexToRemove);
973             return true;
974         }
975         return false;
976     }
977 
978 private:
979     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
980     T* m_pArray;
981     size_t m_Count;
982     size_t m_Capacity;
983 };
984 
985 ////////////////////////////////////////////////////////////////////////////////
986 // Private class StringBuilder
987 
988 class StringBuilder
989 {
990 public:
StringBuilder(const ALLOCATION_CALLBACKS & allocationCallbacks)991     StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) { }
992 
GetLength() const993     size_t GetLength() const { return m_Data.size(); }
GetData() const994     LPCWSTR GetData() const { return m_Data.data(); }
995 
Add(WCHAR ch)996     void Add(WCHAR ch) { m_Data.push_back(ch); }
997     void Add(LPCWSTR str);
AddNewLine()998     void AddNewLine() { Add(L'\n'); }
999     void AddNumber(UINT num);
1000     void AddNumber(UINT64 num);
1001 
1002 private:
1003     Vector<WCHAR> m_Data;
1004 };
1005 
Add(LPCWSTR str)1006 void StringBuilder::Add(LPCWSTR str)
1007 {
1008     const size_t len = wcslen(str);
1009     if (len > 0)
1010     {
1011         const size_t oldCount = m_Data.size();
1012         m_Data.resize(oldCount + len);
1013         memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));
1014     }
1015 }
1016 
AddNumber(UINT num)1017 void StringBuilder::AddNumber(UINT num)
1018 {
1019     WCHAR buf[11];
1020     buf[10] = L'\0';
1021     WCHAR *p = &buf[10];
1022     do
1023     {
1024         *--p = L'0' + (num % 10);
1025         num /= 10;
1026     }
1027     while (num);
1028     Add(p);
1029 }
1030 
AddNumber(UINT64 num)1031 void StringBuilder::AddNumber(UINT64 num)
1032 {
1033     WCHAR buf[21];
1034     buf[20] = L'\0';
1035     WCHAR *p = &buf[20];
1036     do
1037     {
1038         *--p = L'0' + (num % 10);
1039         num /= 10;
1040     }
1041     while (num);
1042     Add(p);
1043 }
1044 
1045 ////////////////////////////////////////////////////////////////////////////////
1046 // Private class JsonWriter
1047 class JsonWriter
1048 {
1049 public:
1050     JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);
1051     ~JsonWriter();
1052 
1053     void BeginObject(bool singleLine = false);
1054     void EndObject();
1055 
1056     void BeginArray(bool singleLine = false);
1057     void EndArray();
1058 
1059     void WriteString(LPCWSTR pStr);
1060     void BeginString(LPCWSTR pStr = NULL);
1061     void ContinueString(LPCWSTR pStr);
1062     void ContinueString(UINT num);
1063     void ContinueString(UINT64 num);
1064     void AddAllocationToObject(const Allocation& alloc);
1065     // void ContinueString_Pointer(const void* ptr);
1066     void EndString(LPCWSTR pStr = NULL);
1067 
1068     void WriteNumber(UINT num);
1069     void WriteNumber(UINT64 num);
1070     void WriteBool(bool b);
1071     void WriteNull();
1072 
1073 private:
1074     static const WCHAR* const INDENT;
1075 
1076     enum CollectionType
1077     {
1078         COLLECTION_TYPE_OBJECT,
1079         COLLECTION_TYPE_ARRAY,
1080     };
1081     struct StackItem
1082     {
1083         CollectionType type;
1084         UINT valueCount;
1085         bool singleLineMode;
1086     };
1087 
1088     StringBuilder& m_SB;
1089     Vector<StackItem> m_Stack;
1090     bool m_InsideString;
1091 
1092     void BeginValue(bool isString);
1093     void WriteIndent(bool oneLess = false);
1094 };
1095 
1096 const WCHAR* const JsonWriter::INDENT = L"  ";
1097 
JsonWriter(const ALLOCATION_CALLBACKS & allocationCallbacks,StringBuilder & stringBuilder)1098 JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder) :
1099     m_SB(stringBuilder),
1100     m_Stack(allocationCallbacks),
1101     m_InsideString(false)
1102 {
1103 }
1104 
~JsonWriter()1105 JsonWriter::~JsonWriter()
1106 {
1107     D3D12MA_ASSERT(!m_InsideString);
1108     D3D12MA_ASSERT(m_Stack.empty());
1109 }
1110 
BeginObject(bool singleLine)1111 void JsonWriter::BeginObject(bool singleLine)
1112 {
1113     D3D12MA_ASSERT(!m_InsideString);
1114 
1115     BeginValue(false);
1116     m_SB.Add(L'{');
1117 
1118     StackItem stackItem;
1119     stackItem.type = COLLECTION_TYPE_OBJECT;
1120     stackItem.valueCount = 0;
1121     stackItem.singleLineMode = singleLine;
1122     m_Stack.push_back(stackItem);
1123 }
1124 
EndObject()1125 void JsonWriter::EndObject()
1126 {
1127     D3D12MA_ASSERT(!m_InsideString);
1128     D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
1129     D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);
1130 
1131     WriteIndent(true);
1132     m_SB.Add(L'}');
1133 
1134     m_Stack.pop_back();
1135 }
1136 
BeginArray(bool singleLine)1137 void JsonWriter::BeginArray(bool singleLine)
1138 {
1139     D3D12MA_ASSERT(!m_InsideString);
1140 
1141     BeginValue(false);
1142     m_SB.Add(L'[');
1143 
1144     StackItem stackItem;
1145     stackItem.type = COLLECTION_TYPE_ARRAY;
1146     stackItem.valueCount = 0;
1147     stackItem.singleLineMode = singleLine;
1148     m_Stack.push_back(stackItem);
1149 }
1150 
EndArray()1151 void JsonWriter::EndArray()
1152 {
1153     D3D12MA_ASSERT(!m_InsideString);
1154     D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
1155 
1156     WriteIndent(true);
1157     m_SB.Add(L']');
1158 
1159     m_Stack.pop_back();
1160 }
1161 
WriteString(LPCWSTR pStr)1162 void JsonWriter::WriteString(LPCWSTR pStr)
1163 {
1164     BeginString(pStr);
1165     EndString();
1166 }
1167 
BeginString(LPCWSTR pStr)1168 void JsonWriter::BeginString(LPCWSTR pStr)
1169 {
1170     D3D12MA_ASSERT(!m_InsideString);
1171 
1172     BeginValue(true);
1173     m_InsideString = true;
1174     m_SB.Add(L'"');
1175     if (pStr != NULL)
1176     {
1177         ContinueString(pStr);
1178     }
1179 }
1180 
ContinueString(LPCWSTR pStr)1181 void JsonWriter::ContinueString(LPCWSTR pStr)
1182 {
1183     D3D12MA_ASSERT(m_InsideString);
1184     D3D12MA_ASSERT(pStr);
1185 
1186     for (const WCHAR *p = pStr; *p; ++p)
1187     {
1188         // the strings we encode are assumed to be in UTF-16LE format, the native
1189         // windows wide character unicode format. In this encoding unicode code
1190         // points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,
1191         // and everything else takes more than two bytes. We will reject any
1192         // multi wchar character encodings for simplicity.
1193         UINT val = (UINT)*p;
1194         D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&
1195             "Character not currently supported.");
1196         switch (*p)
1197         {
1198         case L'"':  m_SB.Add(L'\\'); m_SB.Add(L'"');  break;
1199         case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;
1200         case L'/':  m_SB.Add(L'\\'); m_SB.Add(L'/');  break;
1201         case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b');  break;
1202         case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f');  break;
1203         case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n');  break;
1204         case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r');  break;
1205         case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't');  break;
1206         default:
1207             // conservatively use encoding \uXXXX for any unicode character
1208             // requiring more than one byte.
1209             if (32 <= val && val < 256)
1210                 m_SB.Add(*p);
1211             else
1212             {
1213                 m_SB.Add(L'\\');
1214                 m_SB.Add(L'u');
1215                 for (UINT i = 0; i < 4; ++i)
1216                 {
1217                     UINT hexDigit = (val & 0xF000) >> 12;
1218                     val <<= 4;
1219                     if (hexDigit < 10)
1220                         m_SB.Add(L'0' + (WCHAR)hexDigit);
1221                     else
1222                         m_SB.Add(L'A' + (WCHAR)hexDigit);
1223                 }
1224             }
1225             break;
1226         }
1227     }
1228 }
1229 
ContinueString(UINT num)1230 void JsonWriter::ContinueString(UINT num)
1231 {
1232     D3D12MA_ASSERT(m_InsideString);
1233     m_SB.AddNumber(num);
1234 }
1235 
ContinueString(UINT64 num)1236 void JsonWriter::ContinueString(UINT64 num)
1237 {
1238     D3D12MA_ASSERT(m_InsideString);
1239     m_SB.AddNumber(num);
1240 }
1241 
EndString(LPCWSTR pStr)1242 void JsonWriter::EndString(LPCWSTR pStr)
1243 {
1244     D3D12MA_ASSERT(m_InsideString);
1245 
1246     if (pStr)
1247         ContinueString(pStr);
1248     m_SB.Add(L'"');
1249     m_InsideString = false;
1250 }
1251 
WriteNumber(UINT num)1252 void JsonWriter::WriteNumber(UINT num)
1253 {
1254     D3D12MA_ASSERT(!m_InsideString);
1255     BeginValue(false);
1256     m_SB.AddNumber(num);
1257 }
1258 
WriteNumber(UINT64 num)1259 void JsonWriter::WriteNumber(UINT64 num)
1260 {
1261     D3D12MA_ASSERT(!m_InsideString);
1262     BeginValue(false);
1263     m_SB.AddNumber(num);
1264 }
1265 
WriteBool(bool b)1266 void JsonWriter::WriteBool(bool b)
1267 {
1268     D3D12MA_ASSERT(!m_InsideString);
1269     BeginValue(false);
1270     if (b)
1271         m_SB.Add(L"true");
1272     else
1273         m_SB.Add(L"false");
1274 }
1275 
WriteNull()1276 void JsonWriter::WriteNull()
1277 {
1278     D3D12MA_ASSERT(!m_InsideString);
1279     BeginValue(false);
1280     m_SB.Add(L"null");
1281 }
1282 
BeginValue(bool isString)1283 void JsonWriter::BeginValue(bool isString)
1284 {
1285     if (!m_Stack.empty())
1286     {
1287         StackItem& currItem = m_Stack.back();
1288         if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)
1289         {
1290             D3D12MA_ASSERT(isString);
1291         }
1292 
1293         if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)
1294         {
1295             m_SB.Add(L':'); m_SB.Add(L' ');
1296         }
1297         else if (currItem.valueCount > 0)
1298         {
1299             m_SB.Add(L','); m_SB.Add(L' ');
1300             WriteIndent();
1301         }
1302         else
1303         {
1304             WriteIndent();
1305         }
1306         ++currItem.valueCount;
1307     }
1308 }
1309 
WriteIndent(bool oneLess)1310 void JsonWriter::WriteIndent(bool oneLess)
1311 {
1312     if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
1313     {
1314         m_SB.AddNewLine();
1315 
1316         size_t count = m_Stack.size();
1317         if (count > 0 && oneLess)
1318         {
1319             --count;
1320         }
1321         for (size_t i = 0; i < count; ++i)
1322         {
1323             m_SB.Add(INDENT);
1324         }
1325     }
1326 }
1327 
AddAllocationToObject(const Allocation & alloc)1328 void JsonWriter::AddAllocationToObject(const Allocation& alloc)
1329 {
1330     WriteString(L"Type");
1331     switch (alloc.m_PackedData.GetResourceDimension()) {
1332     case D3D12_RESOURCE_DIMENSION_UNKNOWN:
1333         WriteString(L"UNKNOWN");
1334         break;
1335     case D3D12_RESOURCE_DIMENSION_BUFFER:
1336         WriteString(L"BUFFER");
1337         break;
1338     case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
1339         WriteString(L"TEXTURE1D");
1340         break;
1341     case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
1342         WriteString(L"TEXTURE2D");
1343         break;
1344     case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
1345         WriteString(L"TEXTURE3D");
1346         break;
1347     default: D3D12MA_ASSERT(0); break;
1348     }
1349     WriteString(L"Size");
1350     WriteNumber(alloc.GetSize());
1351     LPCWSTR name = alloc.GetName();
1352     if(name != NULL)
1353     {
1354         WriteString(L"Name");
1355         WriteString(name);
1356     }
1357     if(alloc.m_PackedData.GetResourceFlags())
1358     {
1359         WriteString(L"Flags");
1360         WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());
1361     }
1362     if(alloc.m_PackedData.GetTextureLayout())
1363     {
1364         WriteString(L"Layout");
1365         WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());
1366     }
1367     if(alloc.m_CreationFrameIndex)
1368     {
1369         WriteString(L"CreationFrameIndex");
1370         WriteNumber(alloc.m_CreationFrameIndex);
1371     }
1372 }
1373 
1374 ////////////////////////////////////////////////////////////////////////////////
1375 // Private class PoolAllocator
1376 
1377 /*
1378 Allocator for objects of type T using a list of arrays (pools) to speed up
1379 allocation. Number of elements that can be allocated is not bounded because
1380 allocator can create multiple blocks.
1381 T should be POD because constructor and destructor is not called in Alloc or
1382 Free.
1383 */
1384 template<typename T>
1385 class PoolAllocator
1386 {
1387     D3D12MA_CLASS_NO_COPY(PoolAllocator)
1388 public:
1389     // allocationCallbacks externally owned, must outlive this object.
1390     PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);
~PoolAllocator()1391     ~PoolAllocator() { Clear(); }
1392     void Clear();
1393     template<typename... Types> T* Alloc(Types... args);
1394     void Free(T* ptr);
1395 
1396 private:
1397     union Item
1398     {
1399         UINT NextFreeIndex; // UINT32_MAX means end of list.
1400         alignas(T) char Value[sizeof(T)];
1401     };
1402 
1403     struct ItemBlock
1404     {
1405         Item* pItems;
1406         UINT Capacity;
1407         UINT FirstFreeIndex;
1408     };
1409 
1410     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
1411     const UINT m_FirstBlockCapacity;
1412     Vector<ItemBlock> m_ItemBlocks;
1413 
1414     ItemBlock& CreateNewBlock();
1415 };
1416 
1417 template<typename T>
PoolAllocator(const ALLOCATION_CALLBACKS & allocationCallbacks,UINT firstBlockCapacity)1418 PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity) :
1419     m_AllocationCallbacks(allocationCallbacks),
1420     m_FirstBlockCapacity(firstBlockCapacity),
1421     m_ItemBlocks(allocationCallbacks)
1422 {
1423     D3D12MA_ASSERT(m_FirstBlockCapacity > 1);
1424 }
1425 
1426 template<typename T>
Clear()1427 void PoolAllocator<T>::Clear()
1428 {
1429     for(size_t i = m_ItemBlocks.size(); i--; )
1430     {
1431         D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
1432     }
1433     m_ItemBlocks.clear(true);
1434 }
1435 
1436 template<typename T>
Alloc(Types...args)1437 template<typename... Types> T* PoolAllocator<T>::Alloc(Types... args)
1438 {
1439     for(size_t i = m_ItemBlocks.size(); i--; )
1440     {
1441         ItemBlock& block = m_ItemBlocks[i];
1442         // This block has some free items: Use first one.
1443         if(block.FirstFreeIndex != UINT32_MAX)
1444         {
1445             Item* const pItem = &block.pItems[block.FirstFreeIndex];
1446             block.FirstFreeIndex = pItem->NextFreeIndex;
1447             T* result = (T*)&pItem->Value;
1448             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
1449             return result;
1450         }
1451     }
1452 
1453     // No block has free item: Create new one and use it.
1454     ItemBlock& newBlock = CreateNewBlock();
1455     Item* const pItem = &newBlock.pItems[0];
1456     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
1457     T* result = (T*)pItem->Value;
1458     new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
1459     return result;
1460 }
1461 
1462 template<typename T>
Free(T * ptr)1463 void PoolAllocator<T>::Free(T* ptr)
1464 {
1465     // Search all memory blocks to find ptr.
1466     for(size_t i = m_ItemBlocks.size(); i--; )
1467     {
1468         ItemBlock& block = m_ItemBlocks[i];
1469 
1470         Item* pItemPtr;
1471         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
1472 
1473         // Check if pItemPtr is in address range of this block.
1474         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
1475         {
1476             ptr->~T(); // Explicit destructor call.
1477             const UINT index = static_cast<UINT>(pItemPtr - block.pItems);
1478             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
1479             block.FirstFreeIndex = index;
1480             return;
1481         }
1482     }
1483     D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
1484 }
1485 
1486 template<typename T>
CreateNewBlock()1487 typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()
1488 {
1489     const UINT newBlockCapacity = m_ItemBlocks.empty() ?
1490         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
1491 
1492     const ItemBlock newBlock = {
1493         D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),
1494         newBlockCapacity,
1495         0 };
1496 
1497     m_ItemBlocks.push_back(newBlock);
1498 
1499     // Setup singly-linked list of all free items in this block.
1500     for(UINT i = 0; i < newBlockCapacity - 1; ++i)
1501     {
1502         newBlock.pItems[i].NextFreeIndex = i + 1;
1503     }
1504     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
1505     return m_ItemBlocks.back();
1506 }
1507 
1508 ////////////////////////////////////////////////////////////////////////////////
1509 // Private class List
1510 
1511 /*
1512 Doubly linked list, with elements allocated out of PoolAllocator.
1513 Has custom interface, as well as STL-style interface, including iterator and
1514 const_iterator.
1515 */
1516 template<typename T>
1517 class List
1518 {
1519     D3D12MA_CLASS_NO_COPY(List)
1520 public:
1521     struct Item
1522     {
1523         Item* pPrev;
1524         Item* pNext;
1525         T Value;
1526     };
1527 
1528     // allocationCallbacks externally owned, must outlive this object.
1529     List(const ALLOCATION_CALLBACKS& allocationCallbacks);
1530     ~List();
1531     void Clear();
1532 
GetCount() const1533     size_t GetCount() const { return m_Count; }
IsEmpty() const1534     bool IsEmpty() const { return m_Count == 0; }
1535 
Front()1536     Item* Front() { return m_pFront; }
Front() const1537     const Item* Front() const { return m_pFront; }
Back()1538     Item* Back() { return m_pBack; }
Back() const1539     const Item* Back() const { return m_pBack; }
1540 
1541     Item* PushBack();
1542     Item* PushFront();
1543     Item* PushBack(const T& value);
1544     Item* PushFront(const T& value);
1545     void PopBack();
1546     void PopFront();
1547 
1548     // Item can be null - it means PushBack.
1549     Item* InsertBefore(Item* pItem);
1550     // Item can be null - it means PushFront.
1551     Item* InsertAfter(Item* pItem);
1552 
1553     Item* InsertBefore(Item* pItem, const T& value);
1554     Item* InsertAfter(Item* pItem, const T& value);
1555 
1556     void Remove(Item* pItem);
1557 
1558     class iterator
1559     {
1560     public:
iterator()1561         iterator() :
1562             m_pList(NULL),
1563             m_pItem(NULL)
1564         {
1565         }
1566 
operator *() const1567         T& operator*() const
1568         {
1569             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1570             return m_pItem->Value;
1571         }
operator ->() const1572         T* operator->() const
1573         {
1574             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1575             return &m_pItem->Value;
1576         }
1577 
operator ++()1578         iterator& operator++()
1579         {
1580             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1581             m_pItem = m_pItem->pNext;
1582             return *this;
1583         }
operator --()1584         iterator& operator--()
1585         {
1586             if(m_pItem != NULL)
1587             {
1588                 m_pItem = m_pItem->pPrev;
1589             }
1590             else
1591             {
1592                 D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
1593                 m_pItem = m_pList->Back();
1594             }
1595             return *this;
1596         }
1597 
operator ++(int)1598         iterator operator++(int)
1599         {
1600             iterator result = *this;
1601             ++*this;
1602             return result;
1603         }
operator --(int)1604         iterator operator--(int)
1605         {
1606             iterator result = *this;
1607             --*this;
1608             return result;
1609         }
1610 
operator ==(const iterator & rhs) const1611         bool operator==(const iterator& rhs) const
1612         {
1613             D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
1614             return m_pItem == rhs.m_pItem;
1615         }
operator !=(const iterator & rhs) const1616         bool operator!=(const iterator& rhs) const
1617         {
1618             D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
1619             return m_pItem != rhs.m_pItem;
1620         }
1621 
1622     private:
1623         List<T>* m_pList;
1624         Item* m_pItem;
1625 
iterator(List<T> * pList,Item * pItem)1626         iterator(List<T>* pList, Item* pItem) :
1627             m_pList(pList),
1628             m_pItem(pItem)
1629         {
1630         }
1631 
1632         friend class List<T>;
1633     };
1634 
1635     class const_iterator
1636     {
1637     public:
const_iterator()1638         const_iterator() :
1639             m_pList(NULL),
1640             m_pItem(NULL)
1641         {
1642         }
1643 
const_iterator(const iterator & src)1644         const_iterator(const iterator& src) :
1645             m_pList(src.m_pList),
1646             m_pItem(src.m_pItem)
1647         {
1648         }
1649 
operator *() const1650         const T& operator*() const
1651         {
1652             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1653             return m_pItem->Value;
1654         }
operator ->() const1655         const T* operator->() const
1656         {
1657             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1658             return &m_pItem->Value;
1659         }
1660 
operator ++()1661         const_iterator& operator++()
1662         {
1663             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
1664             m_pItem = m_pItem->pNext;
1665             return *this;
1666         }
operator --()1667         const_iterator& operator--()
1668         {
1669             if(m_pItem != NULL)
1670             {
1671                 m_pItem = m_pItem->pPrev;
1672             }
1673             else
1674             {
1675                 D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
1676                 m_pItem = m_pList->Back();
1677             }
1678             return *this;
1679         }
1680 
operator ++(int)1681         const_iterator operator++(int)
1682         {
1683             const_iterator result = *this;
1684             ++*this;
1685             return result;
1686         }
operator --(int)1687         const_iterator operator--(int)
1688         {
1689             const_iterator result = *this;
1690             --*this;
1691             return result;
1692         }
1693 
operator ==(const const_iterator & rhs) const1694         bool operator==(const const_iterator& rhs) const
1695         {
1696             D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
1697             return m_pItem == rhs.m_pItem;
1698         }
operator !=(const const_iterator & rhs) const1699         bool operator!=(const const_iterator& rhs) const
1700         {
1701             D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
1702             return m_pItem != rhs.m_pItem;
1703         }
1704 
1705     private:
const_iterator(const List<T> * pList,const Item * pItem)1706         const_iterator(const List<T>* pList, const Item* pItem) :
1707             m_pList(pList),
1708             m_pItem(pItem)
1709         {
1710         }
1711 
1712         const List<T>* m_pList;
1713         const Item* m_pItem;
1714 
1715         friend class List<T>;
1716     };
1717 
empty() const1718     bool empty() const { return IsEmpty(); }
size() const1719     size_t size() const { return GetCount(); }
1720 
begin()1721     iterator begin() { return iterator(this, Front()); }
end()1722     iterator end() { return iterator(this, NULL); }
1723 
cbegin() const1724     const_iterator cbegin() const { return const_iterator(this, Front()); }
cend() const1725     const_iterator cend() const { return const_iterator(this, NULL); }
1726 
clear()1727     void clear() { Clear(); }
push_back(const T & value)1728     void push_back(const T& value) { PushBack(value); }
erase(iterator it)1729     void erase(iterator it) { Remove(it.m_pItem); }
insert(iterator it,const T & value)1730     iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }
1731 
1732 private:
1733     const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
1734     PoolAllocator<Item> m_ItemAllocator;
1735     Item* m_pFront;
1736     Item* m_pBack;
1737     size_t m_Count;
1738 };
1739 
1740 template<typename T>
List(const ALLOCATION_CALLBACKS & allocationCallbacks)1741 List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks) :
1742     m_AllocationCallbacks(allocationCallbacks),
1743     m_ItemAllocator(allocationCallbacks, 128),
1744     m_pFront(NULL),
1745     m_pBack(NULL),
1746     m_Count(0)
1747 {
1748 }
1749 
1750 template<typename T>
~List()1751 List<T>::~List()
1752 {
1753     // Intentionally not calling Clear, because that would be unnecessary
1754     // computations to return all items to m_ItemAllocator as free.
1755 }
1756 
1757 template<typename T>
Clear()1758 void List<T>::Clear()
1759 {
1760     if(!IsEmpty())
1761     {
1762         Item* pItem = m_pBack;
1763         while(pItem != NULL)
1764         {
1765             Item* const pPrevItem = pItem->pPrev;
1766             m_ItemAllocator.Free(pItem);
1767             pItem = pPrevItem;
1768         }
1769         m_pFront = NULL;
1770         m_pBack = NULL;
1771         m_Count = 0;
1772     }
1773 }
1774 
1775 template<typename T>
PushBack()1776 typename List<T>::Item* List<T>::PushBack()
1777 {
1778     Item* const pNewItem = m_ItemAllocator.Alloc();
1779     pNewItem->pNext = NULL;
1780     if(IsEmpty())
1781     {
1782         pNewItem->pPrev = NULL;
1783         m_pFront = pNewItem;
1784         m_pBack = pNewItem;
1785         m_Count = 1;
1786     }
1787     else
1788     {
1789         pNewItem->pPrev = m_pBack;
1790         m_pBack->pNext = pNewItem;
1791         m_pBack = pNewItem;
1792         ++m_Count;
1793     }
1794     return pNewItem;
1795 }
1796 
1797 template<typename T>
PushFront()1798 typename List<T>::Item* List<T>::PushFront()
1799 {
1800     Item* const pNewItem = m_ItemAllocator.Alloc();
1801     pNewItem->pPrev = NULL;
1802     if(IsEmpty())
1803     {
1804         pNewItem->pNext = NULL;
1805         m_pFront = pNewItem;
1806         m_pBack = pNewItem;
1807         m_Count = 1;
1808     }
1809     else
1810     {
1811         pNewItem->pNext = m_pFront;
1812         m_pFront->pPrev = pNewItem;
1813         m_pFront = pNewItem;
1814         ++m_Count;
1815     }
1816     return pNewItem;
1817 }
1818 
1819 template<typename T>
PushBack(const T & value)1820 typename List<T>::Item* List<T>::PushBack(const T& value)
1821 {
1822     Item* const pNewItem = PushBack();
1823     pNewItem->Value = value;
1824     return pNewItem;
1825 }
1826 
1827 template<typename T>
PushFront(const T & value)1828 typename List<T>::Item* List<T>::PushFront(const T& value)
1829 {
1830     Item* const pNewItem = PushFront();
1831     pNewItem->Value = value;
1832     return pNewItem;
1833 }
1834 
1835 template<typename T>
PopBack()1836 void List<T>::PopBack()
1837 {
1838     D3D12MA_HEAVY_ASSERT(m_Count > 0);
1839     Item* const pBackItem = m_pBack;
1840     Item* const pPrevItem = pBackItem->pPrev;
1841     if(pPrevItem != NULL)
1842     {
1843         pPrevItem->pNext = NULL;
1844     }
1845     m_pBack = pPrevItem;
1846     m_ItemAllocator.Free(pBackItem);
1847     --m_Count;
1848 }
1849 
1850 template<typename T>
PopFront()1851 void List<T>::PopFront()
1852 {
1853     D3D12MA_HEAVY_ASSERT(m_Count > 0);
1854     Item* const pFrontItem = m_pFront;
1855     Item* const pNextItem = pFrontItem->pNext;
1856     if(pNextItem != NULL)
1857     {
1858         pNextItem->pPrev = NULL;
1859     }
1860     m_pFront = pNextItem;
1861     m_ItemAllocator.Free(pFrontItem);
1862     --m_Count;
1863 }
1864 
1865 template<typename T>
Remove(Item * pItem)1866 void List<T>::Remove(Item* pItem)
1867 {
1868     D3D12MA_HEAVY_ASSERT(pItem != NULL);
1869     D3D12MA_HEAVY_ASSERT(m_Count > 0);
1870 
1871     if(pItem->pPrev != NULL)
1872     {
1873         pItem->pPrev->pNext = pItem->pNext;
1874     }
1875     else
1876     {
1877         D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
1878         m_pFront = pItem->pNext;
1879     }
1880 
1881     if(pItem->pNext != NULL)
1882     {
1883         pItem->pNext->pPrev = pItem->pPrev;
1884     }
1885     else
1886     {
1887         D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
1888         m_pBack = pItem->pPrev;
1889     }
1890 
1891     m_ItemAllocator.Free(pItem);
1892     --m_Count;
1893 }
1894 
1895 template<typename T>
InsertBefore(Item * pItem)1896 typename List<T>::Item* List<T>::InsertBefore(Item* pItem)
1897 {
1898     if(pItem != NULL)
1899     {
1900         Item* const prevItem = pItem->pPrev;
1901         Item* const newItem = m_ItemAllocator.Alloc();
1902         newItem->pPrev = prevItem;
1903         newItem->pNext = pItem;
1904         pItem->pPrev = newItem;
1905         if(prevItem != NULL)
1906         {
1907             prevItem->pNext = newItem;
1908         }
1909         else
1910         {
1911             D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
1912             m_pFront = newItem;
1913         }
1914         ++m_Count;
1915         return newItem;
1916     }
1917     else
1918     {
1919         return PushBack();
1920     }
1921 }
1922 
1923 template<typename T>
InsertAfter(Item * pItem)1924 typename List<T>::Item* List<T>::InsertAfter(Item* pItem)
1925 {
1926     if(pItem != NULL)
1927     {
1928         Item* const nextItem = pItem->pNext;
1929         Item* const newItem = m_ItemAllocator.Alloc();
1930         newItem->pNext = nextItem;
1931         newItem->pPrev = pItem;
1932         pItem->pNext = newItem;
1933         if(nextItem != NULL)
1934         {
1935             nextItem->pPrev = newItem;
1936         }
1937         else
1938         {
1939             D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
1940             m_pBack = newItem;
1941         }
1942         ++m_Count;
1943         return newItem;
1944     }
1945     else
1946         return PushFront();
1947 }
1948 
1949 template<typename T>
InsertBefore(Item * pItem,const T & value)1950 typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)
1951 {
1952     Item* const newItem = InsertBefore(pItem);
1953     newItem->Value = value;
1954     return newItem;
1955 }
1956 
1957 template<typename T>
InsertAfter(Item * pItem,const T & value)1958 typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)
1959 {
1960     Item* const newItem = InsertAfter(pItem);
1961     newItem->Value = value;
1962     return newItem;
1963 }
1964 
1965 ////////////////////////////////////////////////////////////////////////////////
1966 // Private class AllocationObjectAllocator definition
1967 
1968 /*
1969 Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.
1970 */
1971 class AllocationObjectAllocator
1972 {
1973     D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);
1974 public:
1975     AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks);
1976 
1977     template<typename... Types> Allocation* Allocate(Types... args);
1978     void Free(Allocation* alloc);
1979 
1980 private:
1981     D3D12MA_MUTEX m_Mutex;
1982     PoolAllocator<Allocation> m_Allocator;
1983 };
1984 
1985 ////////////////////////////////////////////////////////////////////////////////
1986 // Private class BlockMetadata and derived classes - declarations
1987 
1988 enum SuballocationType
1989 {
1990     SUBALLOCATION_TYPE_FREE = 0,
1991     SUBALLOCATION_TYPE_ALLOCATION = 1,
1992 };
1993 
1994 /*
1995 Represents a region of NormalBlock that is either assigned and returned as
1996 allocated memory block or free.
1997 */
1998 struct Suballocation
1999 {
2000     UINT64 offset;
2001     UINT64 size;
2002     void* userData;
2003     SuballocationType type;
2004 };
2005 
2006 // Comparator for offsets.
2007 struct SuballocationOffsetLess
2008 {
operator ()D3D12MA::SuballocationOffsetLess2009     bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
2010     {
2011         return lhs.offset < rhs.offset;
2012     }
2013 };
2014 struct SuballocationOffsetGreater
2015 {
operator ()D3D12MA::SuballocationOffsetGreater2016     bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
2017     {
2018         return lhs.offset > rhs.offset;
2019     }
2020 };
2021 
2022 typedef List<Suballocation> SuballocationList;
2023 
2024 struct SuballocationItemSizeLess
2025 {
operator ()D3D12MA::SuballocationItemSizeLess2026     bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const
2027     {
2028         return lhs->size < rhs->size;
2029     }
operator ()D3D12MA::SuballocationItemSizeLess2030     bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const
2031     {
2032         return lhs->size < rhsSize;
2033     }
2034 };
2035 
2036 /*
2037 Parameters of planned allocation inside a NormalBlock.
2038 */
2039 struct AllocationRequest
2040 {
2041     UINT64 offset;
2042     UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.
2043     UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
2044     SuballocationList::iterator item;
2045     BOOL zeroInitialized;
2046 };
2047 
2048 /*
2049 Keeps track of the range of bytes that are surely initialized with zeros.
2050 Everything outside of it is considered uninitialized memory that may contain
2051 garbage data.
2052 
2053 The range is left-inclusive.
2054 */
2055 class ZeroInitializedRange
2056 {
2057 public:
Reset(UINT64 size)2058     void Reset(UINT64 size)
2059     {
2060         D3D12MA_ASSERT(size > 0);
2061         m_ZeroBeg = 0;
2062         m_ZeroEnd = size;
2063     }
2064 
IsRangeZeroInitialized(UINT64 beg,UINT64 end) const2065     BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const
2066     {
2067         D3D12MA_ASSERT(beg < end);
2068         return m_ZeroBeg <= beg && end <= m_ZeroEnd;
2069     }
2070 
MarkRangeAsUsed(UINT64 usedBeg,UINT64 usedEnd)2071     void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd)
2072     {
2073         D3D12MA_ASSERT(usedBeg < usedEnd);
2074         // No new bytes marked.
2075         if(usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg)
2076         {
2077             return;
2078         }
2079         // All bytes marked.
2080         if(usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd)
2081         {
2082             m_ZeroBeg = m_ZeroEnd = 0;
2083         }
2084         // Some bytes marked.
2085         else
2086         {
2087             const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0;
2088             const UINT64 remainingZeroAfter  = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0;
2089             D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0);
2090             if(remainingZeroBefore > remainingZeroAfter)
2091             {
2092                 m_ZeroEnd = usedBeg;
2093             }
2094             else
2095             {
2096                 m_ZeroBeg = usedEnd;
2097             }
2098         }
2099     }
2100 
2101 private:
2102     UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0;
2103 };
2104 
2105 /*
2106 Data structure used for bookkeeping of allocations and unused ranges of memory
2107 in a single ID3D12Heap memory block.
2108 */
2109 class BlockMetadata
2110 {
2111 public:
2112     BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
~BlockMetadata()2113     virtual ~BlockMetadata() { }
Init(UINT64 size)2114     virtual void Init(UINT64 size) { m_Size = size; }
2115 
2116     // Validates all data structures inside this object. If not valid, returns false.
2117     virtual bool Validate() const = 0;
GetSize() const2118     UINT64 GetSize() const { return m_Size; }
IsVirtual() const2119     bool IsVirtual() const { return m_IsVirtual; }
2120     virtual size_t GetAllocationCount() const = 0;
2121     virtual UINT64 GetSumFreeSize() const = 0;
2122     virtual UINT64 GetUnusedRangeSizeMax() const = 0;
2123     // Returns true if this block is empty - contains only single free suballocation.
2124     virtual bool IsEmpty() const = 0;
2125 
2126     virtual void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;
2127 
2128     // Tries to find a place for suballocation with given parameters inside this block.
2129     // If succeeded, fills pAllocationRequest and returns true.
2130     // If failed, returns false.
2131     virtual bool CreateAllocationRequest(
2132         UINT64 allocSize,
2133         UINT64 allocAlignment,
2134         AllocationRequest* pAllocationRequest) = 0;
2135 
2136     // Makes actual allocation based on request. Request must already be checked and valid.
2137     virtual void Alloc(
2138         const AllocationRequest& request,
2139         UINT64 allocSize,
2140         void* userData) = 0;
2141 
2142     virtual void FreeAtOffset(UINT64 offset) = 0;
2143     // Frees all allocations.
2144     // Careful! Don't call it if there are Allocation objects owned by pUserData of of cleared allocations!
2145     virtual void Clear() = 0;
2146 
2147     virtual void SetAllocationUserData(UINT64 offset, void* userData) = 0;
2148 
2149     virtual void CalcAllocationStatInfo(StatInfo& outInfo) const = 0;
2150     virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;
2151 
2152 protected:
GetAllocs() const2153     const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; }
2154 
2155 private:
2156     UINT64 m_Size;
2157     bool m_IsVirtual;
2158     const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;
2159 
2160     D3D12MA_CLASS_NO_COPY(BlockMetadata);
2161 };
2162 
2163 class BlockMetadata_Generic : public BlockMetadata
2164 {
2165 public:
2166     BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
2167     virtual ~BlockMetadata_Generic();
2168     virtual void Init(UINT64 size);
2169 
2170     virtual bool Validate() const;
GetAllocationCount() const2171     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize() const2172     virtual UINT64 GetSumFreeSize() const { return m_SumFreeSize; }
2173     virtual UINT64 GetUnusedRangeSizeMax() const;
2174     virtual bool IsEmpty() const;
2175 
2176     virtual void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const;
2177 
2178     virtual bool CreateAllocationRequest(
2179         UINT64 allocSize,
2180         UINT64 allocAlignment,
2181         AllocationRequest* pAllocationRequest);
2182 
2183     virtual void Alloc(
2184         const AllocationRequest& request,
2185         UINT64 allocSize,
2186         void* userData);
2187 
2188     virtual void FreeAtOffset(UINT64 offset);
2189     virtual void Clear();
2190 
2191     virtual void SetAllocationUserData(UINT64 offset, void* userData);
2192 
2193     virtual void CalcAllocationStatInfo(StatInfo& outInfo) const;
2194     virtual void WriteAllocationInfoToJson(JsonWriter& json) const;
2195 
2196 private:
2197     UINT m_FreeCount;
2198     UINT64 m_SumFreeSize;
2199     SuballocationList m_Suballocations;
2200     // Suballocations that are free and have size greater than certain threshold.
2201     // Sorted by size, ascending.
2202     Vector<SuballocationList::iterator> m_FreeSuballocationsBySize;
2203     ZeroInitializedRange m_ZeroInitializedRange;
2204 
2205     bool ValidateFreeSuballocationList() const;
2206 
2207     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
2208     // If yes, fills pOffset and returns true. If no, returns false.
2209     bool CheckAllocation(
2210         UINT64 allocSize,
2211         UINT64 allocAlignment,
2212         SuballocationList::const_iterator suballocItem,
2213         UINT64* pOffset,
2214         UINT64* pSumFreeSize,
2215         UINT64* pSumItemSize,
2216         BOOL *pZeroInitialized) const;
2217     // Given free suballocation, it merges it with following one, which must also be free.
2218     void MergeFreeWithNext(SuballocationList::iterator item);
2219     // Releases given suballocation, making it free.
2220     // Merges it with adjacent free suballocations if applicable.
2221     // Returns iterator to new free suballocation at this place.
2222     SuballocationList::iterator FreeSuballocation(SuballocationList::iterator suballocItem);
2223     // Given free suballocation, it inserts it into sorted list of
2224     // m_FreeSuballocationsBySize if it's suitable.
2225     void RegisterFreeSuballocation(SuballocationList::iterator item);
2226     // Given free suballocation, it removes it from sorted list of
2227     // m_FreeSuballocationsBySize if it's suitable.
2228     void UnregisterFreeSuballocation(SuballocationList::iterator item);
2229 
2230     D3D12MA_CLASS_NO_COPY(BlockMetadata_Generic)
2231 };
2232 
2233 ////////////////////////////////////////////////////////////////////////////////
2234 // Private class MemoryBlock definition
2235 
2236 /*
2237 Represents a single block of device memory (heap).
2238 Base class for inheritance.
2239 Thread-safety: This class must be externally synchronized.
2240 */
2241 class MemoryBlock
2242 {
2243 public:
2244     MemoryBlock(
2245         AllocatorPimpl* allocator,
2246         D3D12_HEAP_TYPE heapType,
2247         D3D12_HEAP_FLAGS heapFlags,
2248         UINT64 size,
2249         UINT id);
2250     virtual ~MemoryBlock();
2251     // Creates the ID3D12Heap.
2252 
GetHeapType() const2253     D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; }
GetHeapFlags() const2254     D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }
GetSize() const2255     UINT64 GetSize() const { return m_Size; }
GetId() const2256     UINT GetId() const { return m_Id; }
GetHeap() const2257     ID3D12Heap* GetHeap() const { return m_Heap; }
2258 
2259 protected:
2260     AllocatorPimpl* const m_Allocator;
2261     const D3D12_HEAP_TYPE m_HeapType;
2262     const D3D12_HEAP_FLAGS m_HeapFlags;
2263     const UINT64 m_Size;
2264     const UINT m_Id;
2265 
2266     HRESULT Init();
2267 
2268 private:
2269     ID3D12Heap* m_Heap = NULL;
2270 
2271     D3D12MA_CLASS_NO_COPY(MemoryBlock)
2272 };
2273 
2274 ////////////////////////////////////////////////////////////////////////////////
2275 // Private class NormalBlock definition
2276 
2277 /*
2278 Represents a single block of device memory (heap) with all the data about its
2279 regions (aka suballocations, Allocation), assigned and free.
2280 Thread-safety: This class must be externally synchronized.
2281 */
2282 class NormalBlock : public MemoryBlock
2283 {
2284 public:
2285     BlockMetadata* m_pMetadata;
2286 
2287     NormalBlock(
2288         AllocatorPimpl* allocator,
2289         BlockVector* blockVector,
2290         D3D12_HEAP_TYPE heapType,
2291         D3D12_HEAP_FLAGS heapFlags,
2292         UINT64 size,
2293         UINT id);
2294     virtual ~NormalBlock();
2295     HRESULT Init();
2296 
GetBlockVector() const2297     BlockVector* GetBlockVector() const { return m_BlockVector; }
2298 
2299     // Validates all data structures inside this object. If not valid, returns false.
2300     bool Validate() const;
2301 
2302 private:
2303     BlockVector* m_BlockVector;
2304 
2305     D3D12MA_CLASS_NO_COPY(NormalBlock)
2306 };
2307 
2308 ////////////////////////////////////////////////////////////////////////////////
2309 // Private class BlockVector definition
2310 
2311 /*
2312 Sequence of NormalBlock. Represents memory blocks allocated for a specific
2313 heap type and possibly resource type (if only Tier 1 is supported).
2314 
2315 Synchronized internally with a mutex.
2316 */
2317 class BlockVector
2318 {
2319     D3D12MA_CLASS_NO_COPY(BlockVector)
2320 public:
2321     BlockVector(
2322         AllocatorPimpl* hAllocator,
2323         D3D12_HEAP_TYPE heapType,
2324         D3D12_HEAP_FLAGS heapFlags,
2325         UINT64 preferredBlockSize,
2326         size_t minBlockCount,
2327         size_t maxBlockCount,
2328         bool explicitBlockSize);
2329     ~BlockVector();
2330 
2331     HRESULT CreateMinBlocks();
2332 
GetHeapType() const2333     UINT GetHeapType() const { return m_HeapType; }
GetPreferredBlockSize() const2334     UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }
2335 
2336     bool IsEmpty();
2337 
2338     HRESULT Allocate(
2339         UINT64 size,
2340         UINT64 alignment,
2341         const ALLOCATION_DESC& allocDesc,
2342         size_t allocationCount,
2343         Allocation** pAllocations);
2344 
2345     void Free(
2346         Allocation* hAllocation);
2347 
2348     HRESULT CreateResource(
2349         UINT64 size,
2350         UINT64 alignment,
2351         const ALLOCATION_DESC& allocDesc,
2352         const D3D12_RESOURCE_DESC& resourceDesc,
2353         D3D12_RESOURCE_STATES InitialResourceState,
2354         const D3D12_CLEAR_VALUE *pOptimizedClearValue,
2355         Allocation** ppAllocation,
2356         REFIID riidResource,
2357         void** ppvResource);
2358 
2359     HRESULT SetMinBytes(UINT64 minBytes);
2360 
2361     void AddStats(StatInfo& outStats);
2362     void AddStats(Stats& outStats);
2363 
2364     void WriteBlockInfoToJson(JsonWriter& json);
2365 
2366 private:
2367     AllocatorPimpl* const m_hAllocator;
2368     const D3D12_HEAP_TYPE m_HeapType;
2369     const D3D12_HEAP_FLAGS m_HeapFlags;
2370     const UINT64 m_PreferredBlockSize;
2371     const size_t m_MinBlockCount;
2372     const size_t m_MaxBlockCount;
2373     const bool m_ExplicitBlockSize;
2374     UINT64 m_MinBytes;
2375     /* There can be at most one allocation that is completely empty - a
2376     hysteresis to avoid pessimistic case of alternating creation and destruction
2377     of a VkDeviceMemory. */
2378     bool m_HasEmptyBlock;
2379     D3D12MA_RW_MUTEX m_Mutex;
2380     // Incrementally sorted by sumFreeSize, ascending.
2381     Vector<NormalBlock*> m_Blocks;
2382     UINT m_NextBlockId;
2383 
2384     UINT64 CalcSumBlockSize() const;
2385     UINT64 CalcMaxBlockSize() const;
2386 
2387     // Finds and removes given block from vector.
2388     void Remove(NormalBlock* pBlock);
2389 
2390     // Performs single step in sorting m_Blocks. They may not be fully sorted
2391     // after this call.
2392     void IncrementallySortBlocks();
2393 
2394     HRESULT AllocatePage(
2395         UINT64 size,
2396         UINT64 alignment,
2397         const ALLOCATION_DESC& allocDesc,
2398         Allocation** pAllocation);
2399 
2400     HRESULT AllocateFromBlock(
2401         NormalBlock* pBlock,
2402         UINT64 size,
2403         UINT64 alignment,
2404         ALLOCATION_FLAGS allocFlags,
2405         Allocation** pAllocation);
2406 
2407     HRESULT CreateBlock(UINT64 blockSize, size_t* pNewBlockIndex);
2408 };
2409 
2410 ////////////////////////////////////////////////////////////////////////////////
2411 // Private class AllocatorPimpl definition
2412 
2413 static const UINT DEFAULT_POOL_MAX_COUNT = 9;
2414 
2415 struct CurrentBudgetData
2416 {
2417     D3D12MA_ATOMIC_UINT64 m_BlockBytes[HEAP_TYPE_COUNT];
2418     D3D12MA_ATOMIC_UINT64 m_AllocationBytes[HEAP_TYPE_COUNT];
2419 
2420     D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
2421     D3D12MA_RW_MUTEX m_BudgetMutex;
2422     UINT64 m_D3D12UsageLocal, m_D3D12UsageNonLocal;
2423     UINT64 m_D3D12BudgetLocal, m_D3D12BudgetNonLocal;
2424     UINT64 m_BlockBytesAtBudgetFetch[HEAP_TYPE_COUNT];
2425 
CurrentBudgetDataD3D12MA::CurrentBudgetData2426     CurrentBudgetData()
2427     {
2428         for(UINT i = 0; i < HEAP_TYPE_COUNT; ++i)
2429         {
2430             m_BlockBytes[i] = 0;
2431             m_AllocationBytes[i] = 0;
2432             m_BlockBytesAtBudgetFetch[i] = 0;
2433         }
2434 
2435         m_D3D12UsageLocal = 0;
2436         m_D3D12UsageNonLocal = 0;
2437         m_D3D12BudgetLocal = 0;
2438         m_D3D12BudgetNonLocal = 0;
2439         m_OperationsSinceBudgetFetch = 0;
2440     }
2441 
AddAllocationD3D12MA::CurrentBudgetData2442     void AddAllocation(UINT heapTypeIndex, UINT64 allocationSize)
2443     {
2444         m_AllocationBytes[heapTypeIndex] += allocationSize;
2445         ++m_OperationsSinceBudgetFetch;
2446     }
2447 
RemoveAllocationD3D12MA::CurrentBudgetData2448     void RemoveAllocation(UINT heapTypeIndex, UINT64 allocationSize)
2449     {
2450         m_AllocationBytes[heapTypeIndex] -= allocationSize;
2451         ++m_OperationsSinceBudgetFetch;
2452     }
2453 };
2454 
2455 class AllocatorPimpl
2456 {
2457 public:
2458     CurrentBudgetData m_Budget;
2459 
2460     AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);
2461     HRESULT Init(const ALLOCATOR_DESC& desc);
2462     ~AllocatorPimpl();
2463 
GetDevice() const2464     ID3D12Device* GetDevice() const { return m_Device; }
2465     // Shortcut for "Allocation Callbacks", because this function is called so often.
GetAllocs() const2466     const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }
GetD3D12Options() const2467     const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; }
SupportsResourceHeapTier2() const2468     bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }
UseMutex() const2469     bool UseMutex() const { return m_UseMutex; }
GetAllocationObjectAllocator()2470     AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }
2471     bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;
2472 
2473     HRESULT CreateResource(
2474         const ALLOCATION_DESC* pAllocDesc,
2475         const D3D12_RESOURCE_DESC* pResourceDesc,
2476         D3D12_RESOURCE_STATES InitialResourceState,
2477         const D3D12_CLEAR_VALUE *pOptimizedClearValue,
2478         Allocation** ppAllocation,
2479         REFIID riidResource,
2480         void** ppvResource);
2481 
2482     HRESULT AllocateMemory(
2483         const ALLOCATION_DESC* pAllocDesc,
2484         const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
2485         Allocation** ppAllocation);
2486 
2487     HRESULT CreateAliasingResource(
2488         Allocation* pAllocation,
2489         UINT64 AllocationLocalOffset,
2490         const D3D12_RESOURCE_DESC* pResourceDesc,
2491         D3D12_RESOURCE_STATES InitialResourceState,
2492         const D3D12_CLEAR_VALUE *pOptimizedClearValue,
2493         REFIID riidResource,
2494         void** ppvResource);
2495 
2496     HRESULT SetDefaultHeapMinBytes(
2497         D3D12_HEAP_TYPE heapType,
2498         D3D12_HEAP_FLAGS heapFlags,
2499         UINT64 minBytes);
2500 
2501     // Unregisters allocation from the collection of dedicated allocations.
2502     // Allocation object must be deleted externally afterwards.
2503     void FreeCommittedMemory(Allocation* allocation);
2504     // Unregisters allocation from the collection of placed allocations.
2505     // Allocation object must be deleted externally afterwards.
2506     void FreePlacedMemory(Allocation* allocation);
2507     // Unregisters allocation from the collection of dedicated allocations and destroys associated heap.
2508     // Allocation object must be deleted externally afterwards.
2509     void FreeHeapMemory(Allocation* allocation);
2510 
2511     void SetCurrentFrameIndex(UINT frameIndex);
2512 
GetCurrentFrameIndex() const2513     UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
2514 
2515     void CalculateStats(Stats& outStats);
2516 
2517     void GetBudget(Budget* outGpuBudget, Budget* outCpuBudget);
2518     void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);
2519 
2520     void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap);
2521 
2522     void FreeStatsString(WCHAR* pStatsString);
2523 
2524 private:
2525     friend class Allocator;
2526     friend class Pool;
2527 
2528     /*
2529     Heuristics that decides whether a resource should better be placed in its own,
2530     dedicated allocation (committed resource rather than placed resource).
2531     */
2532     static bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC& resourceDesc);
2533 
2534     const bool m_UseMutex;
2535     const bool m_AlwaysCommitted;
2536     ID3D12Device* m_Device; // AddRef
2537     IDXGIAdapter* m_Adapter; // AddRef
2538 #if D3D12MA_DXGI_1_4
2539     IDXGIAdapter3* m_Adapter3; // AddRef, optional
2540 #endif
2541     UINT64 m_PreferredBlockSize;
2542     ALLOCATION_CALLBACKS m_AllocationCallbacks;
2543     D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;
2544     DXGI_ADAPTER_DESC m_AdapterDesc;
2545     D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;
2546     AllocationObjectAllocator m_AllocationObjectAllocator;
2547 
2548     typedef Vector<Allocation*> AllocationVectorType;
2549     AllocationVectorType* m_pCommittedAllocations[HEAP_TYPE_COUNT];
2550     D3D12MA_RW_MUTEX m_CommittedAllocationsMutex[HEAP_TYPE_COUNT];
2551 
2552     typedef Vector<Pool*> PoolVectorType;
2553     PoolVectorType* m_pPools[HEAP_TYPE_COUNT];
2554     D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT];
2555 
2556     // Default pools.
2557     BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT];
2558 
2559     // # Used only when ResourceHeapTier = 1
2560     UINT64 m_DefaultPoolTier1MinBytes[DEFAULT_POOL_MAX_COUNT]; // Default 0
2561     UINT64 m_DefaultPoolHeapTypeMinBytes[HEAP_TYPE_COUNT]; // Default UINT64_MAX, meaning not set
2562     D3D12MA_RW_MUTEX m_DefaultPoolMinBytesMutex;
2563 
2564     // Allocates and registers new committed resource with implicit heap, as dedicated allocation.
2565     // Creates and returns Allocation object.
2566     HRESULT AllocateCommittedResource(
2567         const ALLOCATION_DESC* pAllocDesc,
2568         const D3D12_RESOURCE_DESC* pResourceDesc,
2569         const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
2570         D3D12_RESOURCE_STATES InitialResourceState,
2571         const D3D12_CLEAR_VALUE *pOptimizedClearValue,
2572         Allocation** ppAllocation,
2573         REFIID riidResource,
2574         void** ppvResource);
2575 
2576     // Allocates and registers new heap without any resources placed in it, as dedicated allocation.
2577     // Creates and returns Allocation object.
2578     HRESULT AllocateHeap(
2579         const ALLOCATION_DESC* pAllocDesc,
2580         const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo,
2581         Allocation** ppAllocation);
2582 
2583     /*
2584     If SupportsResourceHeapTier2():
2585         0: D3D12_HEAP_TYPE_DEFAULT
2586         1: D3D12_HEAP_TYPE_UPLOAD
2587         2: D3D12_HEAP_TYPE_READBACK
2588     else:
2589         0: D3D12_HEAP_TYPE_DEFAULT + buffer
2590         1: D3D12_HEAP_TYPE_DEFAULT + texture
2591         2: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS
2592         3: D3D12_HEAP_TYPE_UPLOAD + buffer
2593         4: D3D12_HEAP_TYPE_UPLOAD + texture
2594         5: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS
2595         6: D3D12_HEAP_TYPE_READBACK + buffer
2596         7: D3D12_HEAP_TYPE_READBACK + texture
2597         8: D3D12_HEAP_TYPE_READBACK + texture RT or DS
2598     */
2599     UINT CalcDefaultPoolCount() const;
2600     UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_RESOURCE_DESC& resourceDesc) const;
2601     // This one returns UINT32_MAX if nonstandard heap flags are used and index cannot be calculcated.
2602     static UINT CalcDefaultPoolIndex(D3D12_HEAP_TYPE heapType, D3D12_HEAP_FLAGS heapFlags, bool supportsResourceHeapTier2);
CalcDefaultPoolIndex(D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags) const2603     UINT CalcDefaultPoolIndex(D3D12_HEAP_TYPE heapType, D3D12_HEAP_FLAGS heapFlags) const
2604     {
2605         return CalcDefaultPoolIndex(heapType, heapFlags, SupportsResourceHeapTier2());
2606     }
CalcDefaultPoolIndex(const ALLOCATION_DESC & allocDesc) const2607     UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc) const
2608     {
2609         return CalcDefaultPoolIndex(allocDesc.HeapType, allocDesc.ExtraHeapFlags);
2610     }
2611     void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;
2612 
2613     // Registers Allocation object in m_pCommittedAllocations.
2614     void RegisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType);
2615     // Unregisters Allocation object from m_pCommittedAllocations.
2616     void UnregisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType);
2617 
2618     // Registers Pool object in m_pPools.
2619     void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);
2620     // Unregisters Pool object from m_pPools.
2621     void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);
2622 
2623     HRESULT UpdateD3D12Budget();
2624 
2625     D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC& inOutResourceDesc) const;
2626 
2627     // Writes object { } with data of given budget.
2628     static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);
2629 };
2630 
2631 ////////////////////////////////////////////////////////////////////////////////
2632 // Private class BlockMetadata implementation
2633 
BlockMetadata(const ALLOCATION_CALLBACKS * allocationCallbacks,bool isVirtual)2634 BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) :
2635     m_Size(0),
2636     m_IsVirtual(isVirtual),
2637     m_pAllocationCallbacks(allocationCallbacks)
2638 {
2639     D3D12MA_ASSERT(allocationCallbacks);
2640 }
2641 
2642 ////////////////////////////////////////////////////////////////////////////////
2643 // Private class BlockMetadata_Generic implementation
2644 
BlockMetadata_Generic(const ALLOCATION_CALLBACKS * allocationCallbacks,bool isVirtual)2645 BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual) :
2646     BlockMetadata(allocationCallbacks, isVirtual),
2647     m_FreeCount(0),
2648     m_SumFreeSize(0),
2649     m_Suballocations(*allocationCallbacks),
2650     m_FreeSuballocationsBySize(*allocationCallbacks)
2651 {
2652     D3D12MA_ASSERT(allocationCallbacks);
2653 }
2654 
~BlockMetadata_Generic()2655 BlockMetadata_Generic::~BlockMetadata_Generic()
2656 {
2657 }
2658 
Init(UINT64 size)2659 void BlockMetadata_Generic::Init(UINT64 size)
2660 {
2661     BlockMetadata::Init(size);
2662     m_ZeroInitializedRange.Reset(size);
2663 
2664     m_FreeCount = 1;
2665     m_SumFreeSize = size;
2666 
2667     Suballocation suballoc = {};
2668     suballoc.offset = 0;
2669     suballoc.size = size;
2670     suballoc.type = SUBALLOCATION_TYPE_FREE;
2671     suballoc.userData = NULL;
2672 
2673     D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
2674     m_Suballocations.push_back(suballoc);
2675     SuballocationList::iterator suballocItem = m_Suballocations.end();
2676     --suballocItem;
2677     m_FreeSuballocationsBySize.push_back(suballocItem);
2678 }
2679 
Validate() const2680 bool BlockMetadata_Generic::Validate() const
2681 {
2682     D3D12MA_VALIDATE(!m_Suballocations.empty());
2683 
2684     // Expected offset of new suballocation as calculated from previous ones.
2685     UINT64 calculatedOffset = 0;
2686     // Expected number of free suballocations as calculated from traversing their list.
2687     UINT calculatedFreeCount = 0;
2688     // Expected sum size of free suballocations as calculated from traversing their list.
2689     UINT64 calculatedSumFreeSize = 0;
2690     // Expected number of free suballocations that should be registered in
2691     // m_FreeSuballocationsBySize calculated from traversing their list.
2692     size_t freeSuballocationsToRegister = 0;
2693     // True if previous visited suballocation was free.
2694     bool prevFree = false;
2695 
2696     for(SuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
2697         suballocItem != m_Suballocations.cend();
2698         ++suballocItem)
2699     {
2700         const Suballocation& subAlloc = *suballocItem;
2701 
2702         // Actual offset of this suballocation doesn't match expected one.
2703         D3D12MA_VALIDATE(subAlloc.offset == calculatedOffset);
2704 
2705         const bool currFree = (subAlloc.type == SUBALLOCATION_TYPE_FREE);
2706         // Two adjacent free suballocations are invalid. They should be merged.
2707         D3D12MA_VALIDATE(!prevFree || !currFree);
2708 
2709         const Allocation* const alloc = (Allocation*)subAlloc.userData;
2710         if(!IsVirtual())
2711         {
2712             D3D12MA_VALIDATE(currFree == (alloc == NULL));
2713         }
2714 
2715         if(currFree)
2716         {
2717             calculatedSumFreeSize += subAlloc.size;
2718             ++calculatedFreeCount;
2719             if(subAlloc.size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
2720             {
2721                 ++freeSuballocationsToRegister;
2722             }
2723 
2724             // Margin required between allocations - every free space must be at least that large.
2725             D3D12MA_VALIDATE(subAlloc.size >= D3D12MA_DEBUG_MARGIN);
2726         }
2727         else
2728         {
2729             if(!IsVirtual())
2730             {
2731                 D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset);
2732                 D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size);
2733             }
2734 
2735             // Margin required between allocations - previous allocation must be free.
2736             D3D12MA_VALIDATE(D3D12MA_DEBUG_MARGIN == 0 || prevFree);
2737         }
2738 
2739         calculatedOffset += subAlloc.size;
2740         prevFree = currFree;
2741     }
2742 
2743     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
2744     // match expected one.
2745     D3D12MA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
2746 
2747     UINT64 lastSize = 0;
2748     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
2749     {
2750         SuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
2751 
2752         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
2753         D3D12MA_VALIDATE(suballocItem->type == SUBALLOCATION_TYPE_FREE);
2754         // They must be sorted by size ascending.
2755         D3D12MA_VALIDATE(suballocItem->size >= lastSize);
2756 
2757         lastSize = suballocItem->size;
2758     }
2759 
2760     // Check if totals match calculacted values.
2761     D3D12MA_VALIDATE(ValidateFreeSuballocationList());
2762     D3D12MA_VALIDATE(calculatedOffset == GetSize());
2763     D3D12MA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
2764     D3D12MA_VALIDATE(calculatedFreeCount == m_FreeCount);
2765 
2766     return true;
2767 }
2768 
GetUnusedRangeSizeMax() const2769 UINT64 BlockMetadata_Generic::GetUnusedRangeSizeMax() const
2770 {
2771     if(!m_FreeSuballocationsBySize.empty())
2772     {
2773         return m_FreeSuballocationsBySize.back()->size;
2774     }
2775     else
2776     {
2777         return 0;
2778     }
2779 }
2780 
IsEmpty() const2781 bool BlockMetadata_Generic::IsEmpty() const
2782 {
2783     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
2784 }
2785 
GetAllocationInfo(UINT64 offset,VIRTUAL_ALLOCATION_INFO & outInfo) const2786 void BlockMetadata_Generic::GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const
2787 {
2788     for(SuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
2789         suballocItem != m_Suballocations.cend();
2790         ++suballocItem)
2791     {
2792         const Suballocation& suballoc = *suballocItem;
2793         if(suballoc.offset == offset)
2794         {
2795             outInfo.size = suballoc.size;
2796             outInfo.pUserData = suballoc.userData;
2797             return;
2798         }
2799     }
2800     D3D12MA_ASSERT(0 && "Not found!");
2801 }
2802 
CreateAllocationRequest(UINT64 allocSize,UINT64 allocAlignment,AllocationRequest * pAllocationRequest)2803 bool BlockMetadata_Generic::CreateAllocationRequest(
2804     UINT64 allocSize,
2805     UINT64 allocAlignment,
2806     AllocationRequest* pAllocationRequest)
2807 {
2808     D3D12MA_ASSERT(allocSize > 0);
2809     D3D12MA_ASSERT(pAllocationRequest != NULL);
2810     D3D12MA_HEAVY_ASSERT(Validate());
2811 
2812     // There is not enough total free space in this block to fullfill the request: Early return.
2813     if(m_SumFreeSize < allocSize + 2 * D3D12MA_DEBUG_MARGIN)
2814     {
2815         return false;
2816     }
2817 
2818     // New algorithm, efficiently searching freeSuballocationsBySize.
2819     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
2820     if(freeSuballocCount > 0)
2821     {
2822         // Find first free suballocation with size not less than allocSize + 2 * D3D12MA_DEBUG_MARGIN.
2823         SuballocationList::iterator* const it = BinaryFindFirstNotLess(
2824             m_FreeSuballocationsBySize.data(),
2825             m_FreeSuballocationsBySize.data() + freeSuballocCount,
2826             allocSize + 2 * D3D12MA_DEBUG_MARGIN,
2827             SuballocationItemSizeLess());
2828         size_t index = it - m_FreeSuballocationsBySize.data();
2829         for(; index < freeSuballocCount; ++index)
2830         {
2831             if(CheckAllocation(
2832                 allocSize,
2833                 allocAlignment,
2834                 m_FreeSuballocationsBySize[index],
2835                 &pAllocationRequest->offset,
2836                 &pAllocationRequest->sumFreeSize,
2837                 &pAllocationRequest->sumItemSize,
2838                 &pAllocationRequest->zeroInitialized))
2839             {
2840                 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
2841                 return true;
2842             }
2843         }
2844     }
2845 
2846     return false;
2847 }
2848 
Alloc(const AllocationRequest & request,UINT64 allocSize,void * userData)2849 void BlockMetadata_Generic::Alloc(
2850     const AllocationRequest& request,
2851     UINT64 allocSize,
2852     void* userData)
2853 {
2854     D3D12MA_ASSERT(request.item != m_Suballocations.end());
2855     Suballocation& suballoc = *request.item;
2856     // Given suballocation is a free block.
2857     D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);
2858     // Given offset is inside this suballocation.
2859     D3D12MA_ASSERT(request.offset >= suballoc.offset);
2860     const UINT64 paddingBegin = request.offset - suballoc.offset;
2861     D3D12MA_ASSERT(suballoc.size >= paddingBegin + allocSize);
2862     const UINT64 paddingEnd = suballoc.size - paddingBegin - allocSize;
2863 
2864     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
2865     // it to become used.
2866     UnregisterFreeSuballocation(request.item);
2867 
2868     suballoc.offset = request.offset;
2869     suballoc.size = allocSize;
2870     suballoc.type = SUBALLOCATION_TYPE_ALLOCATION;
2871     suballoc.userData = userData;
2872 
2873     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
2874     if(paddingEnd)
2875     {
2876         Suballocation paddingSuballoc = {};
2877         paddingSuballoc.offset = request.offset + allocSize;
2878         paddingSuballoc.size = paddingEnd;
2879         paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;
2880         SuballocationList::iterator next = request.item;
2881         ++next;
2882         const SuballocationList::iterator paddingEndItem =
2883             m_Suballocations.insert(next, paddingSuballoc);
2884         RegisterFreeSuballocation(paddingEndItem);
2885     }
2886 
2887     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
2888     if(paddingBegin)
2889     {
2890         Suballocation paddingSuballoc = {};
2891         paddingSuballoc.offset = request.offset - paddingBegin;
2892         paddingSuballoc.size = paddingBegin;
2893         paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;
2894         const SuballocationList::iterator paddingBeginItem =
2895             m_Suballocations.insert(request.item, paddingSuballoc);
2896         RegisterFreeSuballocation(paddingBeginItem);
2897     }
2898 
2899     // Update totals.
2900     m_FreeCount = m_FreeCount - 1;
2901     if(paddingBegin > 0)
2902     {
2903         ++m_FreeCount;
2904     }
2905     if(paddingEnd > 0)
2906     {
2907         ++m_FreeCount;
2908     }
2909     m_SumFreeSize -= allocSize;
2910 
2911     m_ZeroInitializedRange.MarkRangeAsUsed(request.offset, request.offset + allocSize);
2912 }
2913 
FreeAtOffset(UINT64 offset)2914 void BlockMetadata_Generic::FreeAtOffset(UINT64 offset)
2915 {
2916     for(SuballocationList::iterator suballocItem = m_Suballocations.begin();
2917         suballocItem != m_Suballocations.end();
2918         ++suballocItem)
2919     {
2920         Suballocation& suballoc = *suballocItem;
2921         if(suballoc.offset == offset)
2922         {
2923             FreeSuballocation(suballocItem);
2924             return;
2925         }
2926     }
2927     D3D12MA_ASSERT(0 && "Not found!");
2928 }
2929 
Clear()2930 void BlockMetadata_Generic::Clear()
2931 {
2932     m_FreeCount = 1;
2933     m_SumFreeSize = GetSize();
2934 
2935     m_Suballocations.clear();
2936     Suballocation suballoc = {};
2937     suballoc.offset = 0;
2938     suballoc.size = GetSize();
2939     suballoc.type = SUBALLOCATION_TYPE_FREE;
2940     m_Suballocations.push_back(suballoc);
2941 
2942     m_FreeSuballocationsBySize.clear();
2943     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
2944 }
2945 
ValidateFreeSuballocationList() const2946 bool BlockMetadata_Generic::ValidateFreeSuballocationList() const
2947 {
2948     UINT64 lastSize = 0;
2949     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
2950     {
2951         const SuballocationList::iterator it = m_FreeSuballocationsBySize[i];
2952 
2953         D3D12MA_VALIDATE(it->type == SUBALLOCATION_TYPE_FREE);
2954         D3D12MA_VALIDATE(it->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
2955         D3D12MA_VALIDATE(it->size >= lastSize);
2956         lastSize = it->size;
2957     }
2958     return true;
2959 }
2960 
CheckAllocation(UINT64 allocSize,UINT64 allocAlignment,SuballocationList::const_iterator suballocItem,UINT64 * pOffset,UINT64 * pSumFreeSize,UINT64 * pSumItemSize,BOOL * pZeroInitialized) const2961 bool BlockMetadata_Generic::CheckAllocation(
2962     UINT64 allocSize,
2963     UINT64 allocAlignment,
2964     SuballocationList::const_iterator suballocItem,
2965     UINT64* pOffset,
2966     UINT64* pSumFreeSize,
2967     UINT64* pSumItemSize,
2968     BOOL *pZeroInitialized) const
2969 {
2970     D3D12MA_ASSERT(allocSize > 0);
2971     D3D12MA_ASSERT(suballocItem != m_Suballocations.cend());
2972     D3D12MA_ASSERT(pOffset != NULL && pZeroInitialized != NULL);
2973 
2974     *pSumFreeSize = 0;
2975     *pSumItemSize = 0;
2976     *pZeroInitialized = FALSE;
2977 
2978     const Suballocation& suballoc = *suballocItem;
2979     D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);
2980 
2981     *pSumFreeSize = suballoc.size;
2982 
2983     // Size of this suballocation is too small for this request: Early return.
2984     if(suballoc.size < allocSize)
2985     {
2986         return false;
2987     }
2988 
2989     // Start from offset equal to beginning of this suballocation.
2990     *pOffset = suballoc.offset;
2991 
2992     // Apply D3D12MA_DEBUG_MARGIN at the beginning.
2993     if(D3D12MA_DEBUG_MARGIN > 0)
2994     {
2995         *pOffset += D3D12MA_DEBUG_MARGIN;
2996     }
2997 
2998     // Apply alignment.
2999     *pOffset = AlignUp(*pOffset, allocAlignment);
3000 
3001     // Calculate padding at the beginning based on current offset.
3002     const UINT64 paddingBegin = *pOffset - suballoc.offset;
3003 
3004     // Calculate required margin at the end.
3005     const UINT64 requiredEndMargin = D3D12MA_DEBUG_MARGIN;
3006 
3007     // Fail if requested size plus margin before and after is bigger than size of this suballocation.
3008     if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
3009     {
3010         return false;
3011     }
3012 
3013     // All tests passed: Success. pOffset is already filled.
3014     *pZeroInitialized = m_ZeroInitializedRange.IsRangeZeroInitialized(*pOffset, *pOffset + allocSize);
3015     return true;
3016 }
3017 
MergeFreeWithNext(SuballocationList::iterator item)3018 void BlockMetadata_Generic::MergeFreeWithNext(SuballocationList::iterator item)
3019 {
3020     D3D12MA_ASSERT(item != m_Suballocations.end());
3021     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
3022 
3023     SuballocationList::iterator nextItem = item;
3024     ++nextItem;
3025     D3D12MA_ASSERT(nextItem != m_Suballocations.end());
3026     D3D12MA_ASSERT(nextItem->type == SUBALLOCATION_TYPE_FREE);
3027 
3028     item->size += nextItem->size;
3029     --m_FreeCount;
3030     m_Suballocations.erase(nextItem);
3031 }
3032 
FreeSuballocation(SuballocationList::iterator suballocItem)3033 SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(SuballocationList::iterator suballocItem)
3034 {
3035     // Change this suballocation to be marked as free.
3036     Suballocation& suballoc = *suballocItem;
3037     suballoc.type = SUBALLOCATION_TYPE_FREE;
3038     suballoc.userData = NULL;
3039 
3040     // Update totals.
3041     ++m_FreeCount;
3042     m_SumFreeSize += suballoc.size;
3043 
3044     // Merge with previous and/or next suballocation if it's also free.
3045     bool mergeWithNext = false;
3046     bool mergeWithPrev = false;
3047 
3048     SuballocationList::iterator nextItem = suballocItem;
3049     ++nextItem;
3050     if((nextItem != m_Suballocations.end()) && (nextItem->type == SUBALLOCATION_TYPE_FREE))
3051     {
3052         mergeWithNext = true;
3053     }
3054 
3055     SuballocationList::iterator prevItem = suballocItem;
3056     if(suballocItem != m_Suballocations.begin())
3057     {
3058         --prevItem;
3059         if(prevItem->type == SUBALLOCATION_TYPE_FREE)
3060         {
3061             mergeWithPrev = true;
3062         }
3063     }
3064 
3065     if(mergeWithNext)
3066     {
3067         UnregisterFreeSuballocation(nextItem);
3068         MergeFreeWithNext(suballocItem);
3069     }
3070 
3071     if(mergeWithPrev)
3072     {
3073         UnregisterFreeSuballocation(prevItem);
3074         MergeFreeWithNext(prevItem);
3075         RegisterFreeSuballocation(prevItem);
3076         return prevItem;
3077     }
3078     else
3079     {
3080         RegisterFreeSuballocation(suballocItem);
3081         return suballocItem;
3082     }
3083 }
3084 
RegisterFreeSuballocation(SuballocationList::iterator item)3085 void BlockMetadata_Generic::RegisterFreeSuballocation(SuballocationList::iterator item)
3086 {
3087     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
3088     D3D12MA_ASSERT(item->size > 0);
3089 
3090     // You may want to enable this validation at the beginning or at the end of
3091     // this function, depending on what do you want to check.
3092     D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
3093 
3094     if(item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
3095     {
3096         if(m_FreeSuballocationsBySize.empty())
3097         {
3098             m_FreeSuballocationsBySize.push_back(item);
3099         }
3100         else
3101         {
3102             m_FreeSuballocationsBySize.InsertSorted(item, SuballocationItemSizeLess());
3103         }
3104     }
3105 
3106     //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
3107 }
3108 
3109 
UnregisterFreeSuballocation(SuballocationList::iterator item)3110 void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::iterator item)
3111 {
3112     D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);
3113     D3D12MA_ASSERT(item->size > 0);
3114 
3115     // You may want to enable this validation at the beginning or at the end of
3116     // this function, depending on what do you want to check.
3117     D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
3118 
3119     if(item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
3120     {
3121         SuballocationList::iterator* const it = BinaryFindFirstNotLess(
3122             m_FreeSuballocationsBySize.data(),
3123             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
3124             item,
3125             SuballocationItemSizeLess());
3126         for(size_t index = it - m_FreeSuballocationsBySize.data();
3127             index < m_FreeSuballocationsBySize.size();
3128             ++index)
3129         {
3130             if(m_FreeSuballocationsBySize[index] == item)
3131             {
3132                 m_FreeSuballocationsBySize.remove(index);
3133                 return;
3134             }
3135             D3D12MA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
3136         }
3137         D3D12MA_ASSERT(0 && "Not found.");
3138     }
3139 
3140     //D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());
3141 }
3142 
SetAllocationUserData(UINT64 offset,void * userData)3143 void BlockMetadata_Generic::SetAllocationUserData(UINT64 offset, void* userData)
3144 {
3145     for(SuballocationList::iterator suballocItem = m_Suballocations.begin();
3146         suballocItem != m_Suballocations.end();
3147         ++suballocItem)
3148     {
3149         Suballocation& suballoc = *suballocItem;
3150         if(suballoc.offset == offset)
3151         {
3152             suballoc.userData = userData;
3153             return;
3154         }
3155     }
3156     D3D12MA_ASSERT(0 && "Not found!");
3157 }
3158 
CalcAllocationStatInfo(StatInfo & outInfo) const3159 void BlockMetadata_Generic::CalcAllocationStatInfo(StatInfo& outInfo) const
3160 {
3161     outInfo.BlockCount = 1;
3162 
3163     const UINT rangeCount = (UINT)m_Suballocations.size();
3164     outInfo.AllocationCount = rangeCount - m_FreeCount;
3165     outInfo.UnusedRangeCount = m_FreeCount;
3166 
3167     outInfo.UsedBytes = GetSize() - m_SumFreeSize;
3168     outInfo.UnusedBytes = m_SumFreeSize;
3169 
3170     outInfo.AllocationSizeMin = UINT64_MAX;
3171     outInfo.AllocationSizeMax = 0;
3172     outInfo.UnusedRangeSizeMin = UINT64_MAX;
3173     outInfo.UnusedRangeSizeMax = 0;
3174 
3175     for(SuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
3176         suballocItem != m_Suballocations.cend();
3177         ++suballocItem)
3178     {
3179         const Suballocation& suballoc = *suballocItem;
3180         if(suballoc.type == SUBALLOCATION_TYPE_FREE)
3181         {
3182             outInfo.UnusedRangeSizeMin = D3D12MA_MIN(suballoc.size, outInfo.UnusedRangeSizeMin);
3183             outInfo.UnusedRangeSizeMax = D3D12MA_MAX(suballoc.size, outInfo.UnusedRangeSizeMax);
3184         }
3185         else
3186         {
3187             outInfo.AllocationSizeMin = D3D12MA_MIN(suballoc.size, outInfo.AllocationSizeMin);
3188             outInfo.AllocationSizeMax = D3D12MA_MAX(suballoc.size, outInfo.AllocationSizeMax);
3189         }
3190     }
3191 }
3192 
WriteAllocationInfoToJson(JsonWriter & json) const3193 void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const
3194 {
3195     json.BeginObject();
3196     json.WriteString(L"TotalBytes");
3197     json.WriteNumber(GetSize());
3198     json.WriteString(L"UnusuedBytes");
3199     json.WriteNumber(GetSumFreeSize());
3200     json.WriteString(L"Allocations");
3201     json.WriteNumber(GetAllocationCount());
3202     json.WriteString(L"UnusedRanges");
3203     json.WriteNumber(m_FreeCount);
3204     json.WriteString(L"Suballocations");
3205     json.BeginArray();
3206     for(SuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
3207         suballocItem != m_Suballocations.cend();
3208         ++suballocItem)
3209     {
3210         const Suballocation& suballoc = *suballocItem;
3211         json.BeginObject(true);
3212         json.WriteString(L"Offset");
3213         json.WriteNumber(suballoc.offset);
3214         if(suballoc.type == SUBALLOCATION_TYPE_FREE)
3215         {
3216             json.WriteString(L"Type");
3217             json.WriteString(L"FREE");
3218             json.WriteString(L"Size");
3219             json.WriteNumber(suballoc.size);
3220         }
3221         else if(IsVirtual())
3222         {
3223             json.WriteString(L"Type");
3224             json.WriteString(L"ALLOCATION");
3225             json.WriteString(L"Size");
3226             json.WriteNumber(suballoc.size);
3227             if(suballoc.userData)
3228             {
3229                 json.WriteString(L"UserData");
3230                 json.WriteNumber((uintptr_t)suballoc.userData);
3231             }
3232         }
3233         else
3234         {
3235             const Allocation* const alloc = (const Allocation*)suballoc.userData;
3236             D3D12MA_ASSERT(alloc);
3237             json.AddAllocationToObject(*alloc);
3238         }
3239         json.EndObject();
3240     }
3241     json.EndArray();
3242     json.EndObject();
3243 }
3244 
3245 ////////////////////////////////////////////////////////////////////////////////
3246 // Private class NormalBlock implementation
3247 
NormalBlock(AllocatorPimpl * allocator,BlockVector * blockVector,D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,UINT64 size,UINT id)3248 NormalBlock::NormalBlock(
3249     AllocatorPimpl* allocator,
3250     BlockVector* blockVector,
3251     D3D12_HEAP_TYPE heapType,
3252     D3D12_HEAP_FLAGS heapFlags,
3253     UINT64 size,
3254     UINT id) :
3255     MemoryBlock(allocator, heapType, heapFlags, size, id),
3256     m_pMetadata(NULL),
3257     m_BlockVector(blockVector)
3258 {
3259 }
3260 
~NormalBlock()3261 NormalBlock::~NormalBlock()
3262 {
3263     if(m_pMetadata != NULL)
3264     {
3265         // THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!
3266         // Hitting it means you have some memory leak - unreleased Allocation objects.
3267         D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
3268 
3269         D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);
3270     }
3271 }
3272 
Init()3273 HRESULT NormalBlock::Init()
3274 {
3275     HRESULT hr = MemoryBlock::Init();
3276     if(FAILED(hr))
3277     {
3278         return hr;
3279     }
3280 
3281     m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Generic)(&m_Allocator->GetAllocs(), false);
3282     m_pMetadata->Init(m_Size);
3283 
3284     return hr;
3285 }
3286 
Validate() const3287 bool NormalBlock::Validate() const
3288 {
3289     D3D12MA_VALIDATE(GetHeap() &&
3290         m_pMetadata &&
3291         m_pMetadata->GetSize() != 0 &&
3292         m_pMetadata->GetSize() == GetSize());
3293     return m_pMetadata->Validate();
3294 }
3295 
3296 ////////////////////////////////////////////////////////////////////////////////
3297 // Private class MemoryBlock definition
3298 
MemoryBlock(AllocatorPimpl * allocator,D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,UINT64 size,UINT id)3299 MemoryBlock::MemoryBlock(
3300     AllocatorPimpl* allocator,
3301     D3D12_HEAP_TYPE heapType,
3302     D3D12_HEAP_FLAGS heapFlags,
3303     UINT64 size,
3304     UINT id) :
3305     m_Allocator(allocator),
3306     m_HeapType(heapType),
3307     m_HeapFlags(heapFlags),
3308     m_Size(size),
3309     m_Id(id)
3310 {
3311 }
3312 
~MemoryBlock()3313 MemoryBlock::~MemoryBlock()
3314 {
3315     if(m_Heap)
3316     {
3317         m_Allocator->m_Budget.m_BlockBytes[HeapTypeToIndex(m_HeapType)] -= m_Size;
3318         m_Heap->Release();
3319     }
3320 }
3321 
Init()3322 HRESULT MemoryBlock::Init()
3323 {
3324     D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0);
3325 
3326     D3D12_HEAP_DESC heapDesc = {};
3327     heapDesc.SizeInBytes = m_Size;
3328     heapDesc.Properties.Type = m_HeapType;
3329     heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags);
3330     heapDesc.Flags = m_HeapFlags;
3331 
3332     HRESULT hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, __uuidof(*m_Heap), (void**)&m_Heap);
3333     if(SUCCEEDED(hr))
3334     {
3335         m_Allocator->m_Budget.m_BlockBytes[HeapTypeToIndex(m_HeapType)] += m_Size;
3336     }
3337     return hr;
3338 }
3339 
3340 ////////////////////////////////////////////////////////////////////////////////
3341 // Private class BlockVector implementation
3342 
BlockVector(AllocatorPimpl * hAllocator,D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,UINT64 preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,bool explicitBlockSize)3343 BlockVector::BlockVector(
3344     AllocatorPimpl* hAllocator,
3345     D3D12_HEAP_TYPE heapType,
3346     D3D12_HEAP_FLAGS heapFlags,
3347     UINT64 preferredBlockSize,
3348     size_t minBlockCount,
3349     size_t maxBlockCount,
3350     bool explicitBlockSize) :
3351     m_hAllocator(hAllocator),
3352     m_HeapType(heapType),
3353     m_HeapFlags(heapFlags),
3354     m_PreferredBlockSize(preferredBlockSize),
3355     m_MinBlockCount(minBlockCount),
3356     m_MaxBlockCount(maxBlockCount),
3357     m_ExplicitBlockSize(explicitBlockSize),
3358     m_MinBytes(0),
3359     m_HasEmptyBlock(false),
3360     m_Blocks(hAllocator->GetAllocs()),
3361     m_NextBlockId(0)
3362 {
3363 }
3364 
~BlockVector()3365 BlockVector::~BlockVector()
3366 {
3367     for(size_t i = m_Blocks.size(); i--; )
3368     {
3369         D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]);
3370     }
3371 }
3372 
CreateMinBlocks()3373 HRESULT BlockVector::CreateMinBlocks()
3374 {
3375     for(size_t i = 0; i < m_MinBlockCount; ++i)
3376     {
3377         HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL);
3378         if(FAILED(hr))
3379         {
3380             return hr;
3381         }
3382     }
3383     return S_OK;
3384 }
3385 
IsEmpty()3386 bool BlockVector::IsEmpty()
3387 {
3388     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
3389     return m_Blocks.empty();
3390 }
3391 
Allocate(UINT64 size,UINT64 alignment,const ALLOCATION_DESC & allocDesc,size_t allocationCount,Allocation ** pAllocations)3392 HRESULT BlockVector::Allocate(
3393     UINT64 size,
3394     UINT64 alignment,
3395     const ALLOCATION_DESC& allocDesc,
3396     size_t allocationCount,
3397     Allocation** pAllocations)
3398 {
3399     size_t allocIndex;
3400     HRESULT hr = S_OK;
3401 
3402     {
3403         MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
3404         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
3405         {
3406             hr = AllocatePage(
3407                 size,
3408                 alignment,
3409                 allocDesc,
3410                 pAllocations + allocIndex);
3411             if(FAILED(hr))
3412             {
3413                 break;
3414             }
3415         }
3416     }
3417 
3418     if(FAILED(hr))
3419     {
3420         // Free all already created allocations.
3421         while(allocIndex--)
3422         {
3423             Free(pAllocations[allocIndex]);
3424         }
3425         ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount);
3426     }
3427 
3428     return hr;
3429 }
3430 
AllocatePage(UINT64 size,UINT64 alignment,const ALLOCATION_DESC & allocDesc,Allocation ** pAllocation)3431 HRESULT BlockVector::AllocatePage(
3432     UINT64 size,
3433     UINT64 alignment,
3434     const ALLOCATION_DESC& allocDesc,
3435     Allocation** pAllocation)
3436 {
3437     // Early reject: requested allocation size is larger that maximum block size for this block vector.
3438     if(size + 2 * D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize)
3439     {
3440         return E_OUTOFMEMORY;
3441     }
3442 
3443     UINT64 freeMemory;
3444     {
3445         Budget budget = {};
3446         m_hAllocator->GetBudgetForHeapType(budget, m_HeapType);
3447         freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0;
3448     }
3449 
3450     const bool canCreateNewBlock =
3451         ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&
3452         (m_Blocks.size() < m_MaxBlockCount) &&
3453         // Even if we don't have to stay within budget with this allocation, when the
3454         // budget would be exceeded, we don't want to allocate new blocks, but always
3455         // create resources as committed.
3456         freeMemory >= size;
3457 
3458     // 1. Search existing allocations
3459     {
3460         // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
3461         for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
3462         {
3463             NormalBlock* const pCurrBlock = m_Blocks[blockIndex];
3464             D3D12MA_ASSERT(pCurrBlock);
3465             HRESULT hr = AllocateFromBlock(
3466                 pCurrBlock,
3467                 size,
3468                 alignment,
3469                 allocDesc.Flags,
3470                 pAllocation);
3471             if(SUCCEEDED(hr))
3472             {
3473                 return hr;
3474             }
3475         }
3476     }
3477 
3478     // 2. Try to create new block.
3479     if(canCreateNewBlock)
3480     {
3481         // Calculate optimal size for new block.
3482         UINT64 newBlockSize = m_PreferredBlockSize;
3483         UINT newBlockSizeShift = 0;
3484 
3485         if(!m_ExplicitBlockSize)
3486         {
3487             // Allocate 1/8, 1/4, 1/2 as first blocks.
3488             const UINT64 maxExistingBlockSize = CalcMaxBlockSize();
3489             for(UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
3490             {
3491                 const UINT64 smallerNewBlockSize = newBlockSize / 2;
3492                 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
3493                 {
3494                     newBlockSize = smallerNewBlockSize;
3495                     ++newBlockSizeShift;
3496                 }
3497                 else
3498                 {
3499                     break;
3500                 }
3501             }
3502         }
3503 
3504         size_t newBlockIndex = 0;
3505         HRESULT hr = newBlockSize <= freeMemory ?
3506             CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
3507         // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
3508         if(!m_ExplicitBlockSize)
3509         {
3510             while(FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
3511             {
3512                 const UINT64 smallerNewBlockSize = newBlockSize / 2;
3513                 if(smallerNewBlockSize >= size)
3514                 {
3515                     newBlockSize = smallerNewBlockSize;
3516                     ++newBlockSizeShift;
3517                     hr = newBlockSize <= freeMemory ?
3518                         CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;
3519                 }
3520                 else
3521                 {
3522                     break;
3523                 }
3524             }
3525         }
3526 
3527         if(SUCCEEDED(hr))
3528         {
3529             NormalBlock* const pBlock = m_Blocks[newBlockIndex];
3530             D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
3531 
3532             hr = AllocateFromBlock(
3533                 pBlock,
3534                 size,
3535                 alignment,
3536                 allocDesc.Flags,
3537                 pAllocation);
3538             if(SUCCEEDED(hr))
3539             {
3540                 return hr;
3541             }
3542             else
3543             {
3544                 // Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment.
3545                 return E_OUTOFMEMORY;
3546             }
3547         }
3548     }
3549 
3550     return E_OUTOFMEMORY;
3551 }
3552 
Free(Allocation * hAllocation)3553 void BlockVector::Free(Allocation* hAllocation)
3554 {
3555     NormalBlock* pBlockToDelete = NULL;
3556 
3557     bool budgetExceeded = false;
3558     {
3559         Budget budget = {};
3560         m_hAllocator->GetBudgetForHeapType(budget, m_HeapType);
3561         budgetExceeded = budget.UsageBytes >= budget.BudgetBytes;
3562     }
3563 
3564     // Scope for lock.
3565     {
3566         MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
3567 
3568         NormalBlock* pBlock = hAllocation->m_Placed.block;
3569 
3570         pBlock->m_pMetadata->FreeAtOffset(hAllocation->GetOffset());
3571         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
3572 
3573         const size_t blockCount = m_Blocks.size();
3574         const UINT64 sumBlockSize = CalcSumBlockSize();
3575         // pBlock became empty after this deallocation.
3576         if(pBlock->m_pMetadata->IsEmpty())
3577         {
3578             // Already has empty Allocation. We don't want to have two, so delete this one.
3579             if((m_HasEmptyBlock || budgetExceeded) &&
3580                 blockCount > m_MinBlockCount &&
3581                 sumBlockSize - pBlock->m_pMetadata->GetSize() >= m_MinBytes)
3582             {
3583                 pBlockToDelete = pBlock;
3584                 Remove(pBlock);
3585             }
3586             // We now have first empty block.
3587             else
3588             {
3589                 m_HasEmptyBlock = true;
3590             }
3591         }
3592         // pBlock didn't become empty, but we have another empty block - find and free that one.
3593         // (This is optional, heuristics.)
3594         else if(m_HasEmptyBlock && blockCount > m_MinBlockCount)
3595         {
3596             NormalBlock* pLastBlock = m_Blocks.back();
3597             if(pLastBlock->m_pMetadata->IsEmpty() &&
3598                 sumBlockSize - pLastBlock->m_pMetadata->GetSize() >= m_MinBytes)
3599             {
3600                 pBlockToDelete = pLastBlock;
3601                 m_Blocks.pop_back();
3602                 m_HasEmptyBlock = false;
3603             }
3604         }
3605 
3606         IncrementallySortBlocks();
3607     }
3608 
3609     // Destruction of a free Allocation. Deferred until this point, outside of mutex
3610     // lock, for performance reason.
3611     if(pBlockToDelete != NULL)
3612     {
3613         D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete);
3614     }
3615 }
3616 
CreateResource(UINT64 size,UINT64 alignment,const ALLOCATION_DESC & allocDesc,const D3D12_RESOURCE_DESC & resourceDesc,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,Allocation ** ppAllocation,REFIID riidResource,void ** ppvResource)3617 HRESULT BlockVector::CreateResource(
3618     UINT64 size,
3619     UINT64 alignment,
3620     const ALLOCATION_DESC& allocDesc,
3621     const D3D12_RESOURCE_DESC& resourceDesc,
3622     D3D12_RESOURCE_STATES InitialResourceState,
3623     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
3624     Allocation** ppAllocation,
3625     REFIID riidResource,
3626     void** ppvResource)
3627 {
3628     HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);
3629     if(SUCCEEDED(hr))
3630     {
3631         ID3D12Resource* res = NULL;
3632         hr = m_hAllocator->GetDevice()->CreatePlacedResource(
3633             (*ppAllocation)->m_Placed.block->GetHeap(),
3634             (*ppAllocation)->GetOffset(),
3635             &resourceDesc,
3636             InitialResourceState,
3637             pOptimizedClearValue,
3638             IID_PPV_ARGS(&res));
3639         if(SUCCEEDED(hr))
3640         {
3641             if(ppvResource != NULL)
3642             {
3643                 hr = res->QueryInterface(riidResource, ppvResource);
3644             }
3645             if(SUCCEEDED(hr))
3646             {
3647                 (*ppAllocation)->SetResource(res, &resourceDesc);
3648             }
3649             else
3650             {
3651                 res->Release();
3652                 SAFE_RELEASE(*ppAllocation);
3653             }
3654         }
3655         else
3656         {
3657             SAFE_RELEASE(*ppAllocation);
3658         }
3659     }
3660     return hr;
3661 }
3662 
CalcSumBlockSize() const3663 UINT64 BlockVector::CalcSumBlockSize() const
3664 {
3665     UINT64 result = 0;
3666     for(size_t i = m_Blocks.size(); i--; )
3667     {
3668         result += m_Blocks[i]->m_pMetadata->GetSize();
3669     }
3670     return result;
3671 }
3672 
CalcMaxBlockSize() const3673 UINT64 BlockVector::CalcMaxBlockSize() const
3674 {
3675     UINT64 result = 0;
3676     for(size_t i = m_Blocks.size(); i--; )
3677     {
3678         result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
3679         if(result >= m_PreferredBlockSize)
3680         {
3681             break;
3682         }
3683     }
3684     return result;
3685 }
3686 
Remove(NormalBlock * pBlock)3687 void BlockVector::Remove(NormalBlock* pBlock)
3688 {
3689     for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
3690     {
3691         if(m_Blocks[blockIndex] == pBlock)
3692         {
3693             m_Blocks.remove(blockIndex);
3694             return;
3695         }
3696     }
3697     D3D12MA_ASSERT(0);
3698 }
3699 
IncrementallySortBlocks()3700 void BlockVector::IncrementallySortBlocks()
3701 {
3702     // Bubble sort only until first swap.
3703     for(size_t i = 1; i < m_Blocks.size(); ++i)
3704     {
3705         if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
3706         {
3707             D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
3708             return;
3709         }
3710     }
3711 }
3712 
AllocateFromBlock(NormalBlock * pBlock,UINT64 size,UINT64 alignment,ALLOCATION_FLAGS allocFlags,Allocation ** pAllocation)3713 HRESULT BlockVector::AllocateFromBlock(
3714     NormalBlock* pBlock,
3715     UINT64 size,
3716     UINT64 alignment,
3717     ALLOCATION_FLAGS allocFlags,
3718     Allocation** pAllocation)
3719 {
3720     AllocationRequest currRequest = {};
3721     if(pBlock->m_pMetadata->CreateAllocationRequest(
3722         size,
3723         alignment,
3724         &currRequest))
3725     {
3726         // We no longer have an empty Allocation.
3727         if(pBlock->m_pMetadata->IsEmpty())
3728         {
3729             m_HasEmptyBlock = false;
3730         }
3731 
3732         *pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, currRequest.zeroInitialized);
3733         pBlock->m_pMetadata->Alloc(currRequest, size, *pAllocation);
3734         (*pAllocation)->InitPlaced(currRequest.offset, alignment, pBlock);
3735         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
3736         m_hAllocator->m_Budget.AddAllocation(HeapTypeToIndex(m_HeapType), size);
3737         return S_OK;
3738     }
3739     return E_OUTOFMEMORY;
3740 }
3741 
CreateBlock(UINT64 blockSize,size_t * pNewBlockIndex)3742 HRESULT BlockVector::CreateBlock(UINT64 blockSize, size_t* pNewBlockIndex)
3743 {
3744     NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)(
3745         m_hAllocator,
3746         this,
3747         m_HeapType,
3748         m_HeapFlags,
3749         blockSize,
3750         m_NextBlockId++);
3751     HRESULT hr = pBlock->Init();
3752     if(FAILED(hr))
3753     {
3754         D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock);
3755         return hr;
3756     }
3757 
3758     m_Blocks.push_back(pBlock);
3759     if(pNewBlockIndex != NULL)
3760     {
3761         *pNewBlockIndex = m_Blocks.size() - 1;
3762     }
3763 
3764     return hr;
3765 }
3766 
SetMinBytes(UINT64 minBytes)3767 HRESULT BlockVector::SetMinBytes(UINT64 minBytes)
3768 {
3769     MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());
3770 
3771     if(minBytes == m_MinBytes)
3772     {
3773         return S_OK;
3774     }
3775 
3776     HRESULT hr = S_OK;
3777     UINT64 sumBlockSize = CalcSumBlockSize();
3778     size_t blockCount = m_Blocks.size();
3779 
3780     // New minBytes is smaller - may be able to free some blocks.
3781     if(minBytes < m_MinBytes)
3782     {
3783         m_HasEmptyBlock = false; // Will recalculate this value from scratch.
3784         for(size_t blockIndex = blockCount; blockIndex--; )
3785         {
3786             NormalBlock* const block = m_Blocks[blockIndex];
3787             const UINT64 size = block->m_pMetadata->GetSize();
3788             const bool isEmpty = block->m_pMetadata->IsEmpty();
3789             if(isEmpty &&
3790                 sumBlockSize - size >= minBytes &&
3791                 blockCount - 1 >= m_MinBlockCount)
3792             {
3793                 D3D12MA_DELETE(m_hAllocator->GetAllocs(), block);
3794                 m_Blocks.remove(blockIndex);
3795                 sumBlockSize -= size;
3796                 --blockCount;
3797             }
3798             else
3799             {
3800                 if(isEmpty)
3801                 {
3802                     m_HasEmptyBlock = true;
3803                 }
3804             }
3805         }
3806     }
3807     // New minBytes is larger - may need to allocate some blocks.
3808     else
3809     {
3810         const UINT64 minBlockSize = m_PreferredBlockSize >> NEW_BLOCK_SIZE_SHIFT_MAX;
3811         while(SUCCEEDED(hr) && sumBlockSize < minBytes)
3812         {
3813             if(blockCount < m_MaxBlockCount)
3814             {
3815                 UINT64 newBlockSize = m_PreferredBlockSize;
3816                 if(!m_ExplicitBlockSize)
3817                 {
3818                     if(sumBlockSize + newBlockSize > minBytes)
3819                     {
3820                         newBlockSize = minBytes - sumBlockSize;
3821                     }
3822                     // Next one would be the last block to create and its size would be smaller than
3823                     // the smallest block size we want to use here, so make this one smaller.
3824                     else if(blockCount + 1 < m_MaxBlockCount &&
3825                         sumBlockSize + newBlockSize + minBlockSize > minBytes)
3826                     {
3827                         newBlockSize -= minBlockSize + sumBlockSize + m_PreferredBlockSize - minBytes;
3828                     }
3829                 }
3830 
3831                 hr = CreateBlock(newBlockSize, NULL);
3832                 if(SUCCEEDED(hr))
3833                 {
3834                     m_HasEmptyBlock = true;
3835                     sumBlockSize += newBlockSize;
3836                     ++blockCount;
3837                 }
3838             }
3839             else
3840             {
3841                 hr = E_INVALIDARG;
3842             }
3843         }
3844     }
3845 
3846     m_MinBytes = minBytes;
3847     return hr;
3848 }
3849 
AddStats(StatInfo & outStats)3850 void BlockVector::AddStats(StatInfo& outStats)
3851 {
3852     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
3853 
3854     for(size_t i = 0; i < m_Blocks.size(); ++i)
3855     {
3856         const NormalBlock* const pBlock = m_Blocks[i];
3857         D3D12MA_ASSERT(pBlock);
3858         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
3859         StatInfo blockStatInfo;
3860         pBlock->m_pMetadata->CalcAllocationStatInfo(blockStatInfo);
3861         AddStatInfo(outStats, blockStatInfo);
3862     }
3863 }
3864 
AddStats(Stats & outStats)3865 void BlockVector::AddStats(Stats& outStats)
3866 {
3867     const UINT heapTypeIndex = HeapTypeToIndex(m_HeapType);
3868     StatInfo* const pStatInfo = &outStats.HeapType[heapTypeIndex];
3869 
3870     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
3871 
3872     for(size_t i = 0; i < m_Blocks.size(); ++i)
3873     {
3874         const NormalBlock* const pBlock = m_Blocks[i];
3875         D3D12MA_ASSERT(pBlock);
3876         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
3877         StatInfo blockStatInfo;
3878         pBlock->m_pMetadata->CalcAllocationStatInfo(blockStatInfo);
3879         AddStatInfo(outStats.Total, blockStatInfo);
3880         AddStatInfo(*pStatInfo, blockStatInfo);
3881     }
3882 }
3883 
WriteBlockInfoToJson(JsonWriter & json)3884 void BlockVector::WriteBlockInfoToJson(JsonWriter& json)
3885 {
3886     MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());
3887 
3888     json.BeginObject();
3889 
3890     for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)
3891     {
3892         const NormalBlock* const pBlock = m_Blocks[i];
3893         D3D12MA_ASSERT(pBlock);
3894         D3D12MA_HEAVY_ASSERT(pBlock->Validate());
3895         json.BeginString();
3896         json.ContinueString(pBlock->GetId());
3897         json.EndString();
3898 
3899         pBlock->m_pMetadata->WriteAllocationInfoToJson(json);
3900     }
3901 
3902     json.EndObject();
3903 }
3904 
3905 ////////////////////////////////////////////////////////////////////////////////
3906 // Private class PoolPimpl
3907 
3908 class PoolPimpl
3909 {
3910 public:
3911     PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc);
3912     HRESULT Init();
3913     ~PoolPimpl();
3914 
GetAllocator() const3915     AllocatorPimpl* GetAllocator() const { return m_Allocator; }
GetDesc() const3916     const POOL_DESC& GetDesc() const { return m_Desc; }
GetBlockVector()3917     BlockVector* GetBlockVector() { return m_BlockVector; }
3918 
SetMinBytes(UINT64 minBytes)3919     HRESULT SetMinBytes(UINT64 minBytes) { return m_BlockVector->SetMinBytes(minBytes); }
3920 
3921     void CalculateStats(StatInfo& outStats);
3922 
3923     void SetName(LPCWSTR Name);
GetName() const3924     LPCWSTR GetName() const { return m_Name; }
3925 
3926 private:
3927     friend class Allocator;
3928 
3929     AllocatorPimpl* m_Allocator; // Externally owned object.
3930     POOL_DESC m_Desc;
3931     BlockVector* m_BlockVector; // Owned object.
3932     wchar_t* m_Name;
3933 
3934     void FreeName();
3935 };
3936 
PoolPimpl(AllocatorPimpl * allocator,const POOL_DESC & desc)3937 PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc) :
3938     m_Allocator(allocator),
3939     m_Desc(desc),
3940     m_BlockVector(NULL),
3941     m_Name(NULL)
3942 {
3943     const bool explicitBlockSize = desc.BlockSize != 0;
3944     const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE;
3945 
3946     D3D12_HEAP_FLAGS heapFlags = desc.HeapFlags;
3947 
3948     UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX;
3949 
3950     m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(
3951         allocator, desc.HeapType, heapFlags,
3952         preferredBlockSize,
3953         desc.MinBlockCount, maxBlockCount,
3954         explicitBlockSize);
3955 }
3956 
Init()3957 HRESULT PoolPimpl::Init()
3958 {
3959     return m_BlockVector->CreateMinBlocks();
3960 }
3961 
~PoolPimpl()3962 PoolPimpl::~PoolPimpl()
3963 {
3964     FreeName();
3965     D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);
3966 }
3967 
CalculateStats(StatInfo & outStats)3968 void PoolPimpl::CalculateStats(StatInfo& outStats)
3969 {
3970     ZeroMemory(&outStats, sizeof(outStats));
3971     outStats.AllocationSizeMin = UINT64_MAX;
3972     outStats.UnusedRangeSizeMin = UINT64_MAX;
3973 
3974     m_BlockVector->AddStats(outStats);
3975 
3976     PostProcessStatInfo(outStats);
3977 }
3978 
SetName(LPCWSTR Name)3979 void PoolPimpl::SetName(LPCWSTR Name)
3980 {
3981     FreeName();
3982 
3983     if(Name)
3984     {
3985         const size_t nameCharCount = wcslen(Name) + 1;
3986         m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);
3987         memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));
3988     }
3989 }
3990 
FreeName()3991 void PoolPimpl::FreeName()
3992 {
3993     if(m_Name)
3994     {
3995         const size_t nameCharCount = wcslen(m_Name) + 1;
3996         D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);
3997         m_Name = NULL;
3998     }
3999 }
4000 
4001 ////////////////////////////////////////////////////////////////////////////////
4002 // Public class Pool implementation
4003 
Release()4004 void Pool::Release()
4005 {
4006     if(this == NULL)
4007     {
4008         return;
4009     }
4010 
4011     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
4012 
4013     D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);
4014 }
4015 
GetDesc() const4016 POOL_DESC Pool::GetDesc() const
4017 {
4018     return m_Pimpl->GetDesc();
4019 }
4020 
SetMinBytes(UINT64 minBytes)4021 HRESULT Pool::SetMinBytes(UINT64 minBytes)
4022 {
4023     return m_Pimpl->SetMinBytes(minBytes);
4024 }
4025 
CalculateStats(StatInfo * pStats)4026 void Pool::CalculateStats(StatInfo* pStats)
4027 {
4028     D3D12MA_ASSERT(pStats);
4029     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
4030     m_Pimpl->CalculateStats(*pStats);
4031 }
4032 
SetName(LPCWSTR Name)4033 void Pool::SetName(LPCWSTR Name)
4034 {
4035     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
4036     m_Pimpl->SetName(Name);
4037 }
4038 
GetName() const4039 LPCWSTR Pool::GetName() const
4040 {
4041     return m_Pimpl->GetName();
4042 }
4043 
Pool(Allocator * allocator,const POOL_DESC & desc)4044 Pool::Pool(Allocator* allocator, const POOL_DESC &desc) :
4045     m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc))
4046 {
4047 }
4048 
~Pool()4049 Pool::~Pool()
4050 {
4051     m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapType);
4052 
4053     D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);
4054 }
4055 
4056 ////////////////////////////////////////////////////////////////////////////////
4057 // Private class AllocatorPimpl implementation
4058 
AllocatorPimpl(const ALLOCATION_CALLBACKS & allocationCallbacks,const ALLOCATOR_DESC & desc)4059 AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) :
4060     m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),
4061     m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),
4062     m_Device(desc.pDevice),
4063     m_Adapter(desc.pAdapter),
4064 #if D3D12MA_DXGI_1_4
4065     m_Adapter3(NULL),
4066 #endif
4067     m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),
4068     m_AllocationCallbacks(allocationCallbacks),
4069     m_CurrentFrameIndex(0),
4070     // Below this line don't use allocationCallbacks but m_AllocationCallbacks!!!
4071     m_AllocationObjectAllocator(m_AllocationCallbacks)
4072 {
4073     // desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator.
4074     ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options));
4075 
4076     ZeroMemory(m_pCommittedAllocations, sizeof(m_pCommittedAllocations));
4077     ZeroMemory(m_pPools, sizeof(m_pPools));
4078     ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors));
4079     ZeroMemory(m_DefaultPoolTier1MinBytes, sizeof(m_DefaultPoolTier1MinBytes));
4080 
4081     for(UINT i = 0; i < HEAP_TYPE_COUNT; ++i)
4082     {
4083         m_DefaultPoolHeapTypeMinBytes[i] = UINT64_MAX;
4084     }
4085 
4086     for(UINT heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)
4087     {
4088         m_pCommittedAllocations[heapTypeIndex] = D3D12MA_NEW(GetAllocs(), AllocationVectorType)(GetAllocs());
4089         m_pPools[heapTypeIndex] = D3D12MA_NEW(GetAllocs(), PoolVectorType)(GetAllocs());
4090     }
4091 
4092     m_Device->AddRef();
4093     m_Adapter->AddRef();
4094 }
4095 
Init(const ALLOCATOR_DESC & desc)4096 HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)
4097 {
4098 #if D3D12MA_DXGI_1_4
4099     desc.pAdapter->QueryInterface<IDXGIAdapter3>(&m_Adapter3);
4100 #endif
4101 
4102     HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);
4103     if(FAILED(hr))
4104     {
4105         return hr;
4106     }
4107 
4108     hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));
4109     if(FAILED(hr))
4110     {
4111         return hr;
4112     }
4113 
4114     const UINT defaultPoolCount = CalcDefaultPoolCount();
4115     for(UINT i = 0; i < defaultPoolCount; ++i)
4116     {
4117         D3D12_HEAP_TYPE heapType;
4118         D3D12_HEAP_FLAGS heapFlags;
4119         CalcDefaultPoolParams(heapType, heapFlags, i);
4120 
4121         m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)(
4122             this, // hAllocator
4123             heapType, // heapType
4124             heapFlags, // heapFlags
4125             m_PreferredBlockSize,
4126             0, // minBlockCount
4127             SIZE_MAX, // maxBlockCount
4128             false); // explicitBlockSize
4129         // No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.
4130     }
4131 
4132 #if D3D12MA_DXGI_1_4
4133     if(m_Adapter3)
4134     {
4135         UpdateD3D12Budget();
4136     }
4137 #endif
4138 
4139     return S_OK;
4140 }
4141 
~AllocatorPimpl()4142 AllocatorPimpl::~AllocatorPimpl()
4143 {
4144 #if D3D12MA_DXGI_1_4
4145     SAFE_RELEASE(m_Adapter3);
4146 #endif
4147     SAFE_RELEASE(m_Adapter);
4148     SAFE_RELEASE(m_Device);
4149 
4150     for(UINT i = DEFAULT_POOL_MAX_COUNT; i--; )
4151     {
4152         D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);
4153     }
4154 
4155     for(UINT i = HEAP_TYPE_COUNT; i--; )
4156     {
4157         if(m_pPools[i] && !m_pPools[i]->empty())
4158         {
4159             D3D12MA_ASSERT(0 && "Unfreed pools found!");
4160         }
4161 
4162         D3D12MA_DELETE(GetAllocs(), m_pPools[i]);
4163     }
4164 
4165     for(UINT i = HEAP_TYPE_COUNT; i--; )
4166     {
4167         if(m_pCommittedAllocations[i] && !m_pCommittedAllocations[i]->empty())
4168         {
4169             D3D12MA_ASSERT(0 && "Unfreed committed allocations found!");
4170         }
4171 
4172         D3D12MA_DELETE(GetAllocs(), m_pCommittedAllocations[i]);
4173     }
4174 }
4175 
HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const4176 bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const
4177 {
4178     if(SupportsResourceHeapTier2())
4179     {
4180         return true;
4181     }
4182     else
4183     {
4184         const bool allowBuffers         = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS           ) == 0;
4185         const bool allowRtDsTextures    = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES    ) == 0;
4186         const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;
4187         const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);
4188         return allowedGroupCount == 1;
4189     }
4190 }
4191 
CreateResource(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_DESC * pResourceDesc,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,Allocation ** ppAllocation,REFIID riidResource,void ** ppvResource)4192 HRESULT AllocatorPimpl::CreateResource(
4193     const ALLOCATION_DESC* pAllocDesc,
4194     const D3D12_RESOURCE_DESC* pResourceDesc,
4195     D3D12_RESOURCE_STATES InitialResourceState,
4196     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
4197     Allocation** ppAllocation,
4198     REFIID riidResource,
4199     void** ppvResource)
4200 {
4201     *ppAllocation = NULL;
4202     if(ppvResource)
4203     {
4204         *ppvResource = NULL;
4205     }
4206 
4207     if(pAllocDesc->CustomPool == NULL)
4208     {
4209         if(!IsHeapTypeValid(pAllocDesc->HeapType))
4210         {
4211             return E_INVALIDARG;
4212         }
4213     }
4214 
4215     ALLOCATION_DESC finalAllocDesc = *pAllocDesc;
4216 
4217     D3D12_RESOURCE_DESC finalResourceDesc = *pResourceDesc;
4218     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);
4219     resAllocInfo.Alignment = D3D12MA_MAX<UINT64>(resAllocInfo.Alignment, D3D12MA_DEBUG_ALIGNMENT);
4220     D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));
4221     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);
4222 
4223     if(pAllocDesc->CustomPool != NULL)
4224     {
4225         if((finalAllocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0)
4226         {
4227             return E_INVALIDARG;
4228         }
4229 
4230         BlockVector* blockVector = pAllocDesc->CustomPool->m_Pimpl->GetBlockVector();
4231         D3D12MA_ASSERT(blockVector);
4232         return blockVector->CreateResource(
4233             resAllocInfo.SizeInBytes,
4234             resAllocInfo.Alignment,
4235             finalAllocDesc,
4236             finalResourceDesc,
4237             InitialResourceState,
4238             pOptimizedClearValue,
4239             ppAllocation,
4240             riidResource,
4241             ppvResource);
4242     }
4243     else
4244     {
4245         const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc, finalResourceDesc);
4246         const bool requireCommittedMemory = defaultPoolIndex == UINT32_MAX;
4247         if(requireCommittedMemory)
4248         {
4249             return AllocateCommittedResource(
4250                 &finalAllocDesc,
4251                 &finalResourceDesc,
4252                 resAllocInfo,
4253                 InitialResourceState,
4254                 pOptimizedClearValue,
4255                 ppAllocation,
4256                 riidResource,
4257                 ppvResource);
4258         }
4259 
4260         BlockVector* const blockVector = m_BlockVectors[defaultPoolIndex];
4261         D3D12MA_ASSERT(blockVector);
4262 
4263         const UINT64 preferredBlockSize = blockVector->GetPreferredBlockSize();
4264         bool preferCommittedMemory =
4265             m_AlwaysCommitted ||
4266             PrefersCommittedAllocation(finalResourceDesc) ||
4267             // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.
4268             resAllocInfo.SizeInBytes > preferredBlockSize / 2;
4269         if(preferCommittedMemory &&
4270             (finalAllocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0)
4271         {
4272             finalAllocDesc.Flags |= ALLOCATION_FLAG_COMMITTED;
4273         }
4274 
4275         if((finalAllocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0)
4276         {
4277             return AllocateCommittedResource(
4278                 &finalAllocDesc,
4279                 &finalResourceDesc,
4280                 resAllocInfo,
4281                 InitialResourceState,
4282                 pOptimizedClearValue,
4283                 ppAllocation,
4284                 riidResource,
4285                 ppvResource);
4286         }
4287         else
4288         {
4289             HRESULT hr = blockVector->CreateResource(
4290                 resAllocInfo.SizeInBytes,
4291                 resAllocInfo.Alignment,
4292                 finalAllocDesc,
4293                 finalResourceDesc,
4294                 InitialResourceState,
4295                 pOptimizedClearValue,
4296                 ppAllocation,
4297                 riidResource,
4298                 ppvResource);
4299             if(SUCCEEDED(hr))
4300             {
4301                 return hr;
4302             }
4303 
4304             return AllocateCommittedResource(
4305                 &finalAllocDesc,
4306                 &finalResourceDesc,
4307                 resAllocInfo,
4308                 InitialResourceState,
4309                 pOptimizedClearValue,
4310                 ppAllocation,
4311                 riidResource,
4312                 ppvResource);
4313         }
4314     }
4315 }
4316 
AllocateMemory(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_ALLOCATION_INFO * pAllocInfo,Allocation ** ppAllocation)4317 HRESULT AllocatorPimpl::AllocateMemory(
4318     const ALLOCATION_DESC* pAllocDesc,
4319     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
4320     Allocation** ppAllocation)
4321 {
4322     *ppAllocation = NULL;
4323 
4324     if(pAllocDesc->CustomPool != NULL)
4325     {
4326         BlockVector* const blockVector = pAllocDesc->CustomPool->m_Pimpl->GetBlockVector();
4327         D3D12MA_ASSERT(blockVector);
4328         return blockVector->Allocate(
4329             pAllocInfo->SizeInBytes,
4330             pAllocInfo->Alignment,
4331             *pAllocDesc,
4332             1,
4333             (Allocation**)ppAllocation);
4334     }
4335     else
4336     {
4337         if(!IsHeapTypeValid(pAllocDesc->HeapType))
4338         {
4339             return E_INVALIDARG;
4340         }
4341 
4342         ALLOCATION_DESC finalAllocDesc = *pAllocDesc;
4343 
4344         const UINT defaultPoolIndex = CalcDefaultPoolIndex(*pAllocDesc);
4345         bool requireCommittedMemory = (defaultPoolIndex == UINT32_MAX);
4346         if(requireCommittedMemory)
4347         {
4348             return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);
4349         }
4350 
4351         BlockVector* blockVector = m_BlockVectors[defaultPoolIndex];
4352         D3D12MA_ASSERT(blockVector);
4353 
4354         const UINT64 preferredBlockSize = blockVector->GetPreferredBlockSize();
4355         const bool preferCommittedMemory =
4356             m_AlwaysCommitted ||
4357             // Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.
4358             pAllocInfo->SizeInBytes > preferredBlockSize / 2;
4359         if(preferCommittedMemory &&
4360             (finalAllocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0)
4361         {
4362             finalAllocDesc.Flags |= ALLOCATION_FLAG_COMMITTED;
4363         }
4364 
4365         if((finalAllocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0)
4366         {
4367             return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);
4368         }
4369         else
4370         {
4371             HRESULT hr = blockVector->Allocate(
4372                 pAllocInfo->SizeInBytes,
4373                 pAllocInfo->Alignment,
4374                 finalAllocDesc,
4375                 1,
4376                 (Allocation**)ppAllocation);
4377             if(SUCCEEDED(hr))
4378             {
4379                 return hr;
4380             }
4381 
4382             return AllocateHeap(&finalAllocDesc, *pAllocInfo, ppAllocation);
4383         }
4384     }
4385 }
4386 
CreateAliasingResource(Allocation * pAllocation,UINT64 AllocationLocalOffset,const D3D12_RESOURCE_DESC * pResourceDesc,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,REFIID riidResource,void ** ppvResource)4387 HRESULT AllocatorPimpl::CreateAliasingResource(
4388     Allocation* pAllocation,
4389     UINT64 AllocationLocalOffset,
4390     const D3D12_RESOURCE_DESC* pResourceDesc,
4391     D3D12_RESOURCE_STATES InitialResourceState,
4392     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
4393     REFIID riidResource,
4394     void** ppvResource)
4395 {
4396     *ppvResource = NULL;
4397 
4398     D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc;
4399     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2);
4400     resAllocInfo.Alignment = D3D12MA_MAX<UINT64>(resAllocInfo.Alignment, D3D12MA_DEBUG_ALIGNMENT);
4401     D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));
4402     D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);
4403 
4404     ID3D12Heap* const existingHeap = pAllocation->GetHeap();
4405     const UINT64 existingOffset = pAllocation->GetOffset();
4406     const UINT64 existingSize = pAllocation->GetSize();
4407     const UINT64 newOffset = existingOffset + AllocationLocalOffset;
4408 
4409     if(existingHeap == NULL ||
4410         AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||
4411         newOffset % resAllocInfo.Alignment != 0)
4412     {
4413         return E_INVALIDARG;
4414     }
4415 
4416     return m_Device->CreatePlacedResource(
4417         existingHeap,
4418         newOffset,
4419         &resourceDesc2,
4420         InitialResourceState,
4421         pOptimizedClearValue,
4422         riidResource,
4423         ppvResource);
4424 }
4425 
SetDefaultHeapMinBytes(D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,UINT64 minBytes)4426 HRESULT AllocatorPimpl::SetDefaultHeapMinBytes(
4427     D3D12_HEAP_TYPE heapType,
4428     D3D12_HEAP_FLAGS heapFlags,
4429     UINT64 minBytes)
4430 {
4431     if(!IsHeapTypeValid(heapType))
4432     {
4433         D3D12MA_ASSERT(0 && "Allocator::SetDefaultHeapMinBytes: Invalid heapType passed.");
4434         return E_INVALIDARG;
4435     }
4436 
4437     if(SupportsResourceHeapTier2())
4438     {
4439         if(heapFlags != D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES &&
4440             heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS &&
4441             heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES &&
4442             heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES)
4443         {
4444             D3D12MA_ASSERT(0 && "Allocator::SetDefaultHeapMinBytes: Invalid heapFlags passed.");
4445             return E_INVALIDARG;
4446         }
4447 
4448         UINT64 newMinBytes = UINT64_MAX;
4449 
4450         {
4451             MutexLockWrite lock(m_DefaultPoolMinBytesMutex, m_UseMutex);
4452 
4453             if(heapFlags == D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES)
4454             {
4455                 m_DefaultPoolHeapTypeMinBytes[HeapTypeToIndex(heapType)] = minBytes;
4456                 newMinBytes = minBytes;
4457             }
4458             else
4459             {
4460                 const UINT defaultPoolTier1Index = CalcDefaultPoolIndex(heapType, heapFlags, false);
4461                 m_DefaultPoolTier1MinBytes[defaultPoolTier1Index] = minBytes;
4462 
4463                 newMinBytes = m_DefaultPoolHeapTypeMinBytes[HeapTypeToIndex(heapType)];
4464                 if(newMinBytes == UINT64_MAX)
4465                 {
4466                     newMinBytes = m_DefaultPoolTier1MinBytes[CalcDefaultPoolIndex(heapType, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, false)] +
4467                         m_DefaultPoolTier1MinBytes[CalcDefaultPoolIndex(heapType, D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, false)] +
4468                         m_DefaultPoolTier1MinBytes[CalcDefaultPoolIndex(heapType, D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, false)];
4469                 }
4470             }
4471         }
4472 
4473         const UINT defaultPoolIndex = CalcDefaultPoolIndex(heapType, D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES);
4474         return m_BlockVectors[defaultPoolIndex]->SetMinBytes(newMinBytes);
4475     }
4476     else
4477     {
4478         if(heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS &&
4479             heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES &&
4480             heapFlags != D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES)
4481         {
4482             D3D12MA_ASSERT(0 && "Allocator::SetDefaultHeapMinBytes: Invalid heapFlags passed.");
4483             return E_INVALIDARG;
4484         }
4485 
4486         const UINT defaultPoolIndex = CalcDefaultPoolIndex(heapType, heapFlags);
4487         return m_BlockVectors[defaultPoolIndex]->SetMinBytes(minBytes);
4488     }
4489 }
4490 
PrefersCommittedAllocation(const D3D12_RESOURCE_DESC & resourceDesc)4491 bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC& resourceDesc)
4492 {
4493     // Intentional. It may change in the future.
4494     return false;
4495 }
4496 
AllocateCommittedResource(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_DESC * pResourceDesc,const D3D12_RESOURCE_ALLOCATION_INFO & resAllocInfo,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,Allocation ** ppAllocation,REFIID riidResource,void ** ppvResource)4497 HRESULT AllocatorPimpl::AllocateCommittedResource(
4498     const ALLOCATION_DESC* pAllocDesc,
4499     const D3D12_RESOURCE_DESC* pResourceDesc,
4500     const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo,
4501     D3D12_RESOURCE_STATES InitialResourceState,
4502     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
4503     Allocation** ppAllocation,
4504     REFIID riidResource,
4505     void** ppvResource)
4506 {
4507     if((pAllocDesc->Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)
4508     {
4509         return E_OUTOFMEMORY;
4510     }
4511 
4512     if((pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0)
4513     {
4514         Budget budget = {};
4515         GetBudgetForHeapType(budget, pAllocDesc->HeapType);
4516         if(budget.UsageBytes + resAllocInfo.SizeInBytes > budget.BudgetBytes)
4517         {
4518             return E_OUTOFMEMORY;
4519         }
4520     }
4521 
4522     D3D12_HEAP_PROPERTIES heapProps = {};
4523     heapProps.Type = pAllocDesc->HeapType;
4524 
4525     const D3D12_HEAP_FLAGS heapFlags = pAllocDesc->ExtraHeapFlags;
4526 
4527     ID3D12Resource* res = NULL;
4528     HRESULT hr = m_Device->CreateCommittedResource(
4529         &heapProps, heapFlags, pResourceDesc, InitialResourceState,
4530         pOptimizedClearValue, IID_PPV_ARGS(&res));
4531     if(SUCCEEDED(hr))
4532     {
4533         if(ppvResource != NULL)
4534         {
4535             hr = res->QueryInterface(riidResource, ppvResource);
4536         }
4537         if(SUCCEEDED(hr))
4538         {
4539             const BOOL wasZeroInitialized = TRUE;
4540             Allocation* alloc = m_AllocationObjectAllocator.Allocate(this, resAllocInfo.SizeInBytes, wasZeroInitialized);
4541             alloc->InitCommitted(pAllocDesc->HeapType);
4542             alloc->SetResource(res, pResourceDesc);
4543 
4544             *ppAllocation = alloc;
4545 
4546             RegisterCommittedAllocation(*ppAllocation, pAllocDesc->HeapType);
4547 
4548             const UINT heapTypeIndex = HeapTypeToIndex(pAllocDesc->HeapType);
4549             m_Budget.AddAllocation(heapTypeIndex, resAllocInfo.SizeInBytes);
4550             m_Budget.m_BlockBytes[heapTypeIndex] += resAllocInfo.SizeInBytes;
4551         }
4552         else
4553         {
4554             res->Release();
4555         }
4556     }
4557     return hr;
4558 }
4559 
AllocateHeap(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_ALLOCATION_INFO & allocInfo,Allocation ** ppAllocation)4560 HRESULT AllocatorPimpl::AllocateHeap(
4561     const ALLOCATION_DESC* pAllocDesc,
4562     const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo,
4563     Allocation** ppAllocation)
4564 {
4565     *ppAllocation = nullptr;
4566 
4567     if((pAllocDesc->Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)
4568     {
4569         return E_OUTOFMEMORY;
4570     }
4571 
4572     if((pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0)
4573     {
4574         Budget budget = {};
4575         GetBudgetForHeapType(budget, pAllocDesc->HeapType);
4576         if(budget.UsageBytes + allocInfo.SizeInBytes > budget.BudgetBytes)
4577         {
4578             return E_OUTOFMEMORY;
4579         }
4580     }
4581 
4582     D3D12_HEAP_FLAGS heapFlags = pAllocDesc->ExtraHeapFlags;
4583 
4584     D3D12_HEAP_DESC heapDesc = {};
4585     heapDesc.SizeInBytes = allocInfo.SizeInBytes;
4586     heapDesc.Properties.Type = pAllocDesc->HeapType;
4587     heapDesc.Alignment = allocInfo.Alignment;
4588     heapDesc.Flags = heapFlags;
4589 
4590     ID3D12Heap* heap = nullptr;
4591     HRESULT hr = m_Device->CreateHeap(&heapDesc, __uuidof(*heap), (void**)&heap);
4592     if(SUCCEEDED(hr))
4593     {
4594         const BOOL wasZeroInitialized = TRUE;
4595         (*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, wasZeroInitialized);
4596         (*ppAllocation)->InitHeap(pAllocDesc->HeapType, heap);
4597         RegisterCommittedAllocation(*ppAllocation, pAllocDesc->HeapType);
4598 
4599         const UINT heapTypeIndex = HeapTypeToIndex(pAllocDesc->HeapType);
4600         m_Budget.AddAllocation(heapTypeIndex, allocInfo.SizeInBytes);
4601         m_Budget.m_BlockBytes[heapTypeIndex] += allocInfo.SizeInBytes;
4602     }
4603     return hr;
4604 }
4605 
CalcDefaultPoolCount() const4606 UINT AllocatorPimpl::CalcDefaultPoolCount() const
4607 {
4608     if(SupportsResourceHeapTier2())
4609     {
4610         return 3;
4611     }
4612     else
4613     {
4614         return 9;
4615     }
4616 }
4617 
CalcDefaultPoolIndex(const ALLOCATION_DESC & allocDesc,const D3D12_RESOURCE_DESC & resourceDesc) const4618 UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, const D3D12_RESOURCE_DESC& resourceDesc) const
4619 {
4620     const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~GetExtraHeapFlagsToIgnore();
4621     if(extraHeapFlags != 0)
4622     {
4623         return UINT32_MAX;
4624     }
4625 
4626     UINT poolIndex = UINT_MAX;
4627     switch(allocDesc.HeapType)
4628     {
4629     case D3D12_HEAP_TYPE_DEFAULT:  poolIndex = 0; break;
4630     case D3D12_HEAP_TYPE_UPLOAD:   poolIndex = 1; break;
4631     case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;
4632     default: D3D12MA_ASSERT(0);
4633     }
4634 
4635     if(!SupportsResourceHeapTier2())
4636     {
4637         poolIndex *= 3;
4638         if(resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
4639         {
4640             ++poolIndex;
4641             const bool isRenderTargetOrDepthStencil =
4642                 (resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
4643             if(isRenderTargetOrDepthStencil)
4644             {
4645                 ++poolIndex;
4646             }
4647         }
4648     }
4649 
4650     return poolIndex;
4651 }
4652 
CalcDefaultPoolIndex(D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,bool supportsResourceHeapTier2)4653 UINT AllocatorPimpl::CalcDefaultPoolIndex(D3D12_HEAP_TYPE heapType, D3D12_HEAP_FLAGS heapFlags, bool supportsResourceHeapTier2)
4654 {
4655     const D3D12_HEAP_FLAGS extraHeapFlags = heapFlags & ~GetExtraHeapFlagsToIgnore();
4656     if(extraHeapFlags != 0)
4657     {
4658         return UINT32_MAX;
4659     }
4660 
4661     UINT poolIndex = UINT_MAX;
4662     switch(heapType)
4663     {
4664     case D3D12_HEAP_TYPE_DEFAULT:  poolIndex = 0; break;
4665     case D3D12_HEAP_TYPE_UPLOAD:   poolIndex = 1; break;
4666     case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;
4667     default: D3D12MA_ASSERT(0);
4668     }
4669 
4670     if(!supportsResourceHeapTier2)
4671     {
4672         poolIndex *= 3;
4673 
4674         const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;
4675         const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;
4676         const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;
4677 
4678         const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);
4679         if(allowedGroupCount != 1)
4680         {
4681             return UINT32_MAX;
4682         }
4683 
4684         if(!allowBuffers)
4685         {
4686             ++poolIndex;
4687             if(allowRtDsTextures)
4688             {
4689                 ++poolIndex;
4690             }
4691         }
4692     }
4693 
4694     return poolIndex;
4695 }
4696 
CalcDefaultPoolParams(D3D12_HEAP_TYPE & outHeapType,D3D12_HEAP_FLAGS & outHeapFlags,UINT index) const4697 void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const
4698 {
4699     outHeapType = D3D12_HEAP_TYPE_DEFAULT;
4700     outHeapFlags = D3D12_HEAP_FLAG_NONE;
4701 
4702     if(!SupportsResourceHeapTier2())
4703     {
4704         switch(index % 3)
4705         {
4706         case 0:
4707             outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
4708             break;
4709         case 1:
4710             outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
4711             break;
4712         case 2:
4713             outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
4714             break;
4715         }
4716 
4717         index /= 3;
4718     }
4719 
4720     switch(index)
4721     {
4722     case 0:
4723         outHeapType = D3D12_HEAP_TYPE_DEFAULT;
4724         break;
4725     case 1:
4726         outHeapType = D3D12_HEAP_TYPE_UPLOAD;
4727         break;
4728     case 2:
4729         outHeapType = D3D12_HEAP_TYPE_READBACK;
4730         break;
4731     default:
4732         D3D12MA_ASSERT(0);
4733     }
4734 }
4735 
RegisterCommittedAllocation(Allocation * alloc,D3D12_HEAP_TYPE heapType)4736 void AllocatorPimpl::RegisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType)
4737 {
4738     const UINT heapTypeIndex = HeapTypeToIndex(heapType);
4739 
4740     MutexLockWrite lock(m_CommittedAllocationsMutex[heapTypeIndex], m_UseMutex);
4741     AllocationVectorType* const committedAllocations = m_pCommittedAllocations[heapTypeIndex];
4742     D3D12MA_ASSERT(committedAllocations);
4743     committedAllocations->InsertSorted(alloc, PointerLess());
4744 }
4745 
UnregisterCommittedAllocation(Allocation * alloc,D3D12_HEAP_TYPE heapType)4746 void AllocatorPimpl::UnregisterCommittedAllocation(Allocation* alloc, D3D12_HEAP_TYPE heapType)
4747 {
4748     const UINT heapTypeIndex = HeapTypeToIndex(heapType);
4749 
4750     MutexLockWrite lock(m_CommittedAllocationsMutex[heapTypeIndex], m_UseMutex);
4751     AllocationVectorType* const committedAllocations = m_pCommittedAllocations[heapTypeIndex];
4752     D3D12MA_ASSERT(committedAllocations);
4753     bool success = committedAllocations->RemoveSorted(alloc, PointerLess());
4754     D3D12MA_ASSERT(success);
4755 }
4756 
RegisterPool(Pool * pool,D3D12_HEAP_TYPE heapType)4757 void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)
4758 {
4759     const UINT heapTypeIndex = HeapTypeToIndex(heapType);
4760 
4761     MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
4762     PoolVectorType* const pools = m_pPools[heapTypeIndex];
4763     D3D12MA_ASSERT(pools);
4764     pools->InsertSorted(pool, PointerLess());
4765 }
4766 
UnregisterPool(Pool * pool,D3D12_HEAP_TYPE heapType)4767 void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)
4768 {
4769     const UINT heapTypeIndex = HeapTypeToIndex(heapType);
4770 
4771     MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
4772     PoolVectorType* const pools = m_pPools[heapTypeIndex];
4773     D3D12MA_ASSERT(pools);
4774     bool success = pools->RemoveSorted(pool, PointerLess());
4775     D3D12MA_ASSERT(success);
4776 }
4777 
FreeCommittedMemory(Allocation * allocation)4778 void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)
4779 {
4780     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED);
4781     UnregisterCommittedAllocation(allocation, allocation->m_Committed.heapType);
4782 
4783     const UINT64 allocationSize = allocation->GetSize();
4784     const UINT heapTypeIndex = HeapTypeToIndex(allocation->m_Committed.heapType);
4785     m_Budget.RemoveAllocation(heapTypeIndex, allocationSize);
4786     m_Budget.m_BlockBytes[heapTypeIndex] -= allocationSize;
4787 }
4788 
FreePlacedMemory(Allocation * allocation)4789 void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)
4790 {
4791     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED);
4792 
4793     NormalBlock* const block = allocation->m_Placed.block;
4794     D3D12MA_ASSERT(block);
4795     BlockVector* const blockVector = block->GetBlockVector();
4796     D3D12MA_ASSERT(blockVector);
4797     m_Budget.RemoveAllocation(HeapTypeToIndex(block->GetHeapType()), allocation->GetSize());
4798     blockVector->Free(allocation);
4799 }
4800 
FreeHeapMemory(Allocation * allocation)4801 void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)
4802 {
4803     D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP);
4804     UnregisterCommittedAllocation(allocation, allocation->m_Heap.heapType);
4805     SAFE_RELEASE(allocation->m_Heap.heap);
4806 
4807     const UINT heapTypeIndex = HeapTypeToIndex(allocation->m_Heap.heapType);
4808     const UINT64 allocationSize = allocation->GetSize();
4809     m_Budget.m_BlockBytes[heapTypeIndex] -= allocationSize;
4810     m_Budget.RemoveAllocation(heapTypeIndex, allocationSize);
4811 }
4812 
SetCurrentFrameIndex(UINT frameIndex)4813 void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)
4814 {
4815     m_CurrentFrameIndex.store(frameIndex);
4816 
4817 #if D3D12MA_DXGI_1_4
4818     if(m_Adapter3)
4819     {
4820         UpdateD3D12Budget();
4821     }
4822 #endif
4823 }
4824 
CalculateStats(Stats & outStats)4825 void AllocatorPimpl::CalculateStats(Stats& outStats)
4826 {
4827     // Init stats
4828     ZeroMemory(&outStats, sizeof(outStats));
4829     outStats.Total.AllocationSizeMin = UINT64_MAX;
4830     outStats.Total.UnusedRangeSizeMin = UINT64_MAX;
4831     for(size_t i = 0; i < HEAP_TYPE_COUNT; i++)
4832     {
4833         outStats.HeapType[i].AllocationSizeMin = UINT64_MAX;
4834         outStats.HeapType[i].UnusedRangeSizeMin = UINT64_MAX;
4835     }
4836 
4837     // Process deafult pools.
4838     for(size_t i = 0; i < HEAP_TYPE_COUNT; ++i)
4839     {
4840         BlockVector* const pBlockVector = m_BlockVectors[i];
4841         D3D12MA_ASSERT(pBlockVector);
4842         pBlockVector->AddStats(outStats);
4843     }
4844 
4845     // Process custom pools
4846     for(size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)
4847     {
4848         MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);
4849         const PoolVectorType* const poolVector = m_pPools[heapTypeIndex];
4850         D3D12MA_ASSERT(poolVector);
4851         for(size_t poolIndex = 0, count = poolVector->size(); poolIndex < count; ++poolIndex)
4852         {
4853             Pool* pool = (*poolVector)[poolIndex];
4854             pool->m_Pimpl->GetBlockVector()->AddStats(outStats);
4855         }
4856     }
4857 
4858     // Process committed allocations.
4859     for(size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)
4860     {
4861         StatInfo& heapStatInfo = outStats.HeapType[heapTypeIndex];
4862         MutexLockRead lock(m_CommittedAllocationsMutex[heapTypeIndex], m_UseMutex);
4863         const AllocationVectorType* const allocationVector = m_pCommittedAllocations[heapTypeIndex];
4864         D3D12MA_ASSERT(allocationVector);
4865         for(size_t allocIndex = 0, count = allocationVector->size(); allocIndex < count; ++allocIndex)
4866         {
4867             UINT64 size = (*allocationVector)[allocIndex]->GetSize();
4868             StatInfo statInfo = {};
4869             statInfo.BlockCount = 1;
4870             statInfo.AllocationCount = 1;
4871             statInfo.UnusedRangeCount = 0;
4872             statInfo.UsedBytes = size;
4873             statInfo.UnusedBytes = 0;
4874             statInfo.AllocationSizeMin = size;
4875             statInfo.AllocationSizeMax = size;
4876             statInfo.UnusedRangeSizeMin = UINT64_MAX;
4877             statInfo.UnusedRangeSizeMax = 0;
4878             AddStatInfo(outStats.Total, statInfo);
4879             AddStatInfo(heapStatInfo, statInfo);
4880         }
4881     }
4882 
4883     // Post process
4884     PostProcessStatInfo(outStats.Total);
4885     for(size_t i = 0; i < HEAP_TYPE_COUNT; ++i)
4886         PostProcessStatInfo(outStats.HeapType[i]);
4887 }
4888 
GetBudget(Budget * outGpuBudget,Budget * outCpuBudget)4889 void AllocatorPimpl::GetBudget(Budget* outGpuBudget, Budget* outCpuBudget)
4890 {
4891     if(outGpuBudget)
4892     {
4893         // Taking DEFAULT.
4894         outGpuBudget->BlockBytes = m_Budget.m_BlockBytes[0];
4895         outGpuBudget->AllocationBytes = m_Budget.m_AllocationBytes[0];
4896     }
4897     if(outCpuBudget)
4898     {
4899         // Taking UPLOAD + READBACK.
4900         outCpuBudget->BlockBytes = m_Budget.m_BlockBytes[1] + m_Budget.m_BlockBytes[2];
4901         outCpuBudget->AllocationBytes = m_Budget.m_AllocationBytes[1] + m_Budget.m_AllocationBytes[2];
4902     }
4903 
4904 #if D3D12MA_DXGI_1_4
4905     if(m_Adapter3)
4906     {
4907         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
4908         {
4909             MutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
4910             if(outGpuBudget)
4911             {
4912 
4913                 if(m_Budget.m_D3D12UsageLocal + outGpuBudget->BlockBytes > m_Budget.m_BlockBytesAtBudgetFetch[0])
4914                 {
4915                     outGpuBudget->UsageBytes = m_Budget.m_D3D12UsageLocal +
4916                         outGpuBudget->BlockBytes - m_Budget.m_BlockBytesAtBudgetFetch[0];
4917                 }
4918                 else
4919                 {
4920                     outGpuBudget->UsageBytes = 0;
4921                 }
4922                 outGpuBudget->BudgetBytes = m_Budget.m_D3D12BudgetLocal;
4923             }
4924             if(outCpuBudget)
4925             {
4926                 if(m_Budget.m_D3D12UsageNonLocal + outCpuBudget->BlockBytes > m_Budget.m_BlockBytesAtBudgetFetch[1] + m_Budget.m_BlockBytesAtBudgetFetch[2])
4927                 {
4928                     outCpuBudget->UsageBytes = m_Budget.m_D3D12UsageNonLocal +
4929                         outCpuBudget->BlockBytes - (m_Budget.m_BlockBytesAtBudgetFetch[1] + m_Budget.m_BlockBytesAtBudgetFetch[2]);
4930                 }
4931                 else
4932                 {
4933                     outCpuBudget->UsageBytes = 0;
4934                 }
4935                 outCpuBudget->BudgetBytes = m_Budget.m_D3D12BudgetNonLocal;
4936             }
4937         }
4938         else
4939         {
4940             UpdateD3D12Budget(); // Outside of mutex lock
4941             GetBudget(outGpuBudget, outCpuBudget); // Recursion
4942         }
4943     }
4944     else
4945 #endif
4946     {
4947         if(outGpuBudget)
4948         {
4949             const UINT64 gpuMemorySize = m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.DedicatedSystemMemory; // TODO: Is this right?
4950             outGpuBudget->UsageBytes = outGpuBudget->BlockBytes;
4951             outGpuBudget->BudgetBytes = gpuMemorySize * 8 / 10; // 80% heuristics.
4952         }
4953         if(outCpuBudget)
4954         {
4955             const UINT64 cpuMemorySize = m_AdapterDesc.SharedSystemMemory; // TODO: Is this right?
4956             outCpuBudget->UsageBytes = outCpuBudget->BlockBytes;
4957             outCpuBudget->BudgetBytes = cpuMemorySize * 8 / 10; // 80% heuristics.
4958         }
4959     }
4960 }
4961 
GetBudgetForHeapType(Budget & outBudget,D3D12_HEAP_TYPE heapType)4962 void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)
4963 {
4964     switch(heapType)
4965     {
4966     case D3D12_HEAP_TYPE_DEFAULT:
4967         GetBudget(&outBudget, NULL);
4968         break;
4969     case D3D12_HEAP_TYPE_UPLOAD:
4970     case D3D12_HEAP_TYPE_READBACK:
4971         GetBudget(NULL, &outBudget);
4972         break;
4973     default: D3D12MA_ASSERT(0);
4974     }
4975 }
4976 
AddStatInfoToJson(JsonWriter & json,const StatInfo & statInfo)4977 static void AddStatInfoToJson(JsonWriter& json, const StatInfo& statInfo)
4978 {
4979     json.BeginObject();
4980     json.WriteString(L"Blocks");
4981     json.WriteNumber(statInfo.BlockCount);
4982     json.WriteString(L"Allocations");
4983     json.WriteNumber(statInfo.AllocationCount);
4984     json.WriteString(L"UnusedRanges");
4985     json.WriteNumber(statInfo.UnusedRangeCount);
4986     json.WriteString(L"UsedBytes");
4987     json.WriteNumber(statInfo.UsedBytes);
4988     json.WriteString(L"UnusedBytes");
4989     json.WriteNumber(statInfo.UnusedBytes);
4990 
4991     json.WriteString(L"AllocationSize");
4992     json.BeginObject(true);
4993     json.WriteString(L"Min");
4994     json.WriteNumber(statInfo.AllocationSizeMin);
4995     json.WriteString(L"Avg");
4996     json.WriteNumber(statInfo.AllocationSizeAvg);
4997     json.WriteString(L"Max");
4998     json.WriteNumber(statInfo.AllocationSizeMax);
4999     json.EndObject();
5000 
5001     json.WriteString(L"UnusedRangeSize");
5002     json.BeginObject(true);
5003     json.WriteString(L"Min");
5004     json.WriteNumber(statInfo.UnusedRangeSizeMin);
5005     json.WriteString(L"Avg");
5006     json.WriteNumber(statInfo.UnusedRangeSizeAvg);
5007     json.WriteString(L"Max");
5008     json.WriteNumber(statInfo.UnusedRangeSizeMax);
5009     json.EndObject();
5010 
5011     json.EndObject();
5012 }
5013 
BuildStatsString(WCHAR ** ppStatsString,BOOL DetailedMap)5014 void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)
5015 {
5016     StringBuilder sb(GetAllocs());
5017     {
5018         JsonWriter json(GetAllocs(), sb);
5019 
5020         Budget gpuBudget = {}, cpuBudget = {};
5021         GetBudget(&gpuBudget, &cpuBudget);
5022 
5023         Stats stats;
5024         CalculateStats(stats);
5025 
5026         json.BeginObject();
5027 
5028         json.WriteString(L"Total");
5029         AddStatInfoToJson(json, stats.Total);
5030         for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)
5031         {
5032             json.WriteString(HeapTypeNames[heapType]);
5033             AddStatInfoToJson(json, stats.HeapType[heapType]);
5034         }
5035 
5036         json.WriteString(L"Budget");
5037         json.BeginObject();
5038         {
5039             json.WriteString(L"GPU");
5040             WriteBudgetToJson(json, gpuBudget);
5041             json.WriteString(L"CPU");
5042             WriteBudgetToJson(json, cpuBudget);
5043         }
5044         json.EndObject();
5045 
5046         if (DetailedMap)
5047         {
5048             json.WriteString(L"DetailedMap");
5049             json.BeginObject();
5050 
5051             json.WriteString(L"DefaultPools");
5052             json.BeginObject();
5053 
5054             if (SupportsResourceHeapTier2())
5055             {
5056                 for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)
5057                 {
5058                     json.WriteString(HeapTypeNames[heapType]);
5059                     json.BeginObject();
5060 
5061                     json.WriteString(L"Blocks");
5062 
5063                     BlockVector* blockVector = m_BlockVectors[heapType];
5064                     D3D12MA_ASSERT(blockVector);
5065                     blockVector->WriteBlockInfoToJson(json);
5066 
5067                     json.EndObject(); // heap name
5068                 }
5069             }
5070             else
5071             {
5072                 for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)
5073                 {
5074                     for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)
5075                     {
5076                         static const WCHAR* const heapSubTypeName[] = {
5077                             L" + buffer",
5078                             L" + texture",
5079                             L" + texture RT or DS",
5080                         };
5081                         json.BeginString();
5082                         json.ContinueString(HeapTypeNames[heapType]);
5083                         json.ContinueString(heapSubTypeName[heapSubType]);
5084                         json.EndString();
5085                         json.BeginObject();
5086 
5087                         json.WriteString(L"Blocks");
5088 
5089                         BlockVector* blockVector = m_BlockVectors[heapType * 3 + heapSubType];
5090                         D3D12MA_ASSERT(blockVector);
5091                         blockVector->WriteBlockInfoToJson(json);
5092 
5093                         json.EndObject(); // heap name
5094                     }
5095                 }
5096             }
5097 
5098             json.EndObject(); // DefaultPools
5099 
5100             json.WriteString(L"CommittedAllocations");
5101             json.BeginObject();
5102 
5103             for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)
5104             {
5105                 json.WriteString(HeapTypeNames[heapType]);
5106                 MutexLockRead lock(m_CommittedAllocationsMutex[heapType], m_UseMutex);
5107 
5108                 json.BeginArray();
5109                 const AllocationVectorType* const allocationVector = m_pCommittedAllocations[heapType];
5110                 D3D12MA_ASSERT(allocationVector);
5111                 for (size_t i = 0, count = allocationVector->size(); i < count; ++i)
5112                 {
5113                     Allocation* alloc = (*allocationVector)[i];
5114                     D3D12MA_ASSERT(alloc);
5115 
5116                     json.BeginObject(true);
5117                     json.AddAllocationToObject(*alloc);
5118                     json.EndObject();
5119                 }
5120                 json.EndArray();
5121             }
5122 
5123             json.EndObject(); // CommittedAllocations
5124 
5125             json.EndObject(); // DetailedMap
5126         }
5127         json.EndObject();
5128     }
5129 
5130     const size_t length = sb.GetLength();
5131     WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 1);
5132     memcpy(result, sb.GetData(), length * sizeof(WCHAR));
5133     result[length] = L'\0';
5134     *ppStatsString = result;
5135 }
5136 
FreeStatsString(WCHAR * pStatsString)5137 void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)
5138 {
5139     D3D12MA_ASSERT(pStatsString);
5140     Free(GetAllocs(), pStatsString);
5141 }
5142 
UpdateD3D12Budget()5143 HRESULT AllocatorPimpl::UpdateD3D12Budget()
5144 {
5145 #if D3D12MA_DXGI_1_4
5146     D3D12MA_ASSERT(m_Adapter3);
5147 
5148     DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};
5149     DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};
5150     HRESULT hrLocal = m_Adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);
5151     HRESULT hrNonLocal = m_Adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);
5152 
5153     {
5154         MutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
5155 
5156         if(SUCCEEDED(hrLocal))
5157         {
5158             m_Budget.m_D3D12UsageLocal = infoLocal.CurrentUsage;
5159             m_Budget.m_D3D12BudgetLocal = infoLocal.Budget;
5160         }
5161         if(SUCCEEDED(hrNonLocal))
5162         {
5163             m_Budget.m_D3D12UsageNonLocal = infoNonLocal.CurrentUsage;
5164             m_Budget.m_D3D12BudgetNonLocal = infoNonLocal.Budget;
5165         }
5166 
5167         for(UINT i = 0; i < HEAP_TYPE_COUNT; ++i)
5168         {
5169             m_Budget.m_BlockBytesAtBudgetFetch[i] = m_Budget.m_BlockBytes[i].load();
5170         }
5171 
5172         m_Budget.m_OperationsSinceBudgetFetch = 0;
5173     }
5174 
5175     return FAILED(hrLocal) ? hrLocal : hrNonLocal;
5176 #else
5177     return S_OK;
5178 #endif
5179 }
5180 
GetResourceAllocationInfo(D3D12_RESOURCE_DESC & inOutResourceDesc) const5181 D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC& inOutResourceDesc) const
5182 {
5183 #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
5184     if(inOutResourceDesc.Alignment == 0 &&
5185         inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
5186         (inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0
5187 #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 1
5188         && CanUseSmallAlignment(inOutResourceDesc)
5189 #endif
5190         )
5191     {
5192         /*
5193         The algorithm here is based on Microsoft sample: "Small Resources Sample"
5194         https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources
5195         */
5196         const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ?
5197             D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :
5198             D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;
5199         inOutResourceDesc.Alignment = smallAlignmentToTry;
5200         const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = m_Device->GetResourceAllocationInfo(0, 1, &inOutResourceDesc);
5201         // Check if alignment requested has been granted.
5202         if(smallAllocInfo.Alignment == smallAlignmentToTry)
5203         {
5204             return smallAllocInfo;
5205         }
5206         inOutResourceDesc.Alignment = 0; // Restore original
5207     }
5208 #endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
5209 
5210     return m_Device->GetResourceAllocationInfo(0, 1, &inOutResourceDesc);
5211 }
5212 
WriteBudgetToJson(JsonWriter & json,const Budget & budget)5213 void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)
5214 {
5215     json.BeginObject();
5216     {
5217         json.WriteString(L"BlockBytes");
5218         json.WriteNumber(budget.BlockBytes);
5219         json.WriteString(L"AllocationBytes");
5220         json.WriteNumber(budget.AllocationBytes);
5221         json.WriteString(L"UsageBytes");
5222         json.WriteNumber(budget.UsageBytes);
5223         json.WriteString(L"BudgetBytes");
5224         json.WriteNumber(budget.BudgetBytes);
5225     }
5226     json.EndObject();
5227 }
5228 
5229 ////////////////////////////////////////////////////////////////////////////////
5230 // Public class Allocation implementation
5231 
SetType(Type type)5232 void Allocation::PackedData::SetType(Type type)
5233 {
5234     const UINT u = (UINT)type;
5235     D3D12MA_ASSERT(u < (1u << 2));
5236     m_Type = u;
5237 }
5238 
SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)5239 void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)
5240 {
5241     const UINT u = (UINT)resourceDimension;
5242     D3D12MA_ASSERT(u < (1u << 3));
5243     m_ResourceDimension = u;
5244 }
5245 
SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)5246 void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)
5247 {
5248     const UINT u = (UINT)resourceFlags;
5249     D3D12MA_ASSERT(u < (1u << 24));
5250     m_ResourceFlags = u;
5251 }
5252 
SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)5253 void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)
5254 {
5255     const UINT u = (UINT)textureLayout;
5256     D3D12MA_ASSERT(u < (1u << 9));
5257     m_TextureLayout = u;
5258 }
5259 
Release()5260 void Allocation::Release()
5261 {
5262     if(this == NULL)
5263     {
5264         return;
5265     }
5266 
5267     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5268 
5269     SAFE_RELEASE(m_Resource);
5270 
5271     switch(m_PackedData.GetType())
5272     {
5273     case TYPE_COMMITTED:
5274         m_Allocator->FreeCommittedMemory(this);
5275         break;
5276     case TYPE_PLACED:
5277         m_Allocator->FreePlacedMemory(this);
5278         break;
5279     case TYPE_HEAP:
5280         m_Allocator->FreeHeapMemory(this);
5281         break;
5282     }
5283 
5284     FreeName();
5285 
5286     m_Allocator->GetAllocationObjectAllocator().Free(this);
5287 }
5288 
GetOffset() const5289 UINT64 Allocation::GetOffset() const
5290 {
5291     switch(m_PackedData.GetType())
5292     {
5293     case TYPE_COMMITTED:
5294     case TYPE_HEAP:
5295         return 0;
5296     case TYPE_PLACED:
5297         return m_Placed.offset;
5298     default:
5299         D3D12MA_ASSERT(0);
5300         return 0;
5301     }
5302 }
5303 
GetHeap() const5304 ID3D12Heap* Allocation::GetHeap() const
5305 {
5306     switch(m_PackedData.GetType())
5307     {
5308     case TYPE_COMMITTED:
5309         return NULL;
5310     case TYPE_PLACED:
5311         return m_Placed.block->GetHeap();
5312     case TYPE_HEAP:
5313         return m_Heap.heap;
5314     default:
5315         D3D12MA_ASSERT(0);
5316         return 0;
5317     }
5318 }
5319 
SetName(LPCWSTR Name)5320 void Allocation::SetName(LPCWSTR Name)
5321 {
5322     FreeName();
5323 
5324     if(Name)
5325     {
5326         const size_t nameCharCount = wcslen(Name) + 1;
5327         m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);
5328         memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));
5329     }
5330 }
5331 
Allocation(AllocatorPimpl * allocator,UINT64 size,BOOL wasZeroInitialized)5332 Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, BOOL wasZeroInitialized) :
5333     m_Allocator{allocator},
5334     m_Size{size},
5335     m_Resource{NULL},
5336     m_CreationFrameIndex{allocator->GetCurrentFrameIndex()},
5337     m_Name{NULL}
5338 {
5339     D3D12MA_ASSERT(allocator);
5340 
5341     m_PackedData.SetType(TYPE_COUNT);
5342     m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN);
5343     m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE);
5344     m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN);
5345     m_PackedData.SetWasZeroInitialized(wasZeroInitialized);
5346 }
5347 
~Allocation()5348 Allocation::~Allocation()
5349 {
5350     // Nothing here, everything already done in Release.
5351 }
5352 
InitCommitted(D3D12_HEAP_TYPE heapType)5353 void Allocation::InitCommitted(D3D12_HEAP_TYPE heapType)
5354 {
5355     m_PackedData.SetType(TYPE_COMMITTED);
5356     m_Committed.heapType = heapType;
5357 }
5358 
InitPlaced(UINT64 offset,UINT64 alignment,NormalBlock * block)5359 void Allocation::InitPlaced(UINT64 offset, UINT64 alignment, NormalBlock* block)
5360 {
5361     m_PackedData.SetType(TYPE_PLACED);
5362     m_Placed.offset = offset;
5363     m_Placed.block = block;
5364 }
5365 
InitHeap(D3D12_HEAP_TYPE heapType,ID3D12Heap * heap)5366 void Allocation::InitHeap(D3D12_HEAP_TYPE heapType, ID3D12Heap* heap)
5367 {
5368     m_PackedData.SetType(TYPE_HEAP);
5369     m_Heap.heapType = heapType;
5370     m_Heap.heap = heap;
5371 }
5372 
SetResource(ID3D12Resource * resource,const D3D12_RESOURCE_DESC * pResourceDesc)5373 void Allocation::SetResource(ID3D12Resource* resource, const D3D12_RESOURCE_DESC* pResourceDesc)
5374 {
5375     D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc);
5376     m_Resource = resource;
5377     m_PackedData.SetResourceDimension(pResourceDesc->Dimension);
5378     m_PackedData.SetResourceFlags(pResourceDesc->Flags);
5379     m_PackedData.SetTextureLayout(pResourceDesc->Layout);
5380 }
5381 
FreeName()5382 void Allocation::FreeName()
5383 {
5384     if(m_Name)
5385     {
5386         const size_t nameCharCount = wcslen(m_Name) + 1;
5387         D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);
5388         m_Name = NULL;
5389     }
5390 }
5391 
5392 ////////////////////////////////////////////////////////////////////////////////
5393 // Private class AllocationObjectAllocator implementation
5394 
AllocationObjectAllocator(const ALLOCATION_CALLBACKS & allocationCallbacks)5395 AllocationObjectAllocator::AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks) :
5396     m_Allocator(allocationCallbacks, 1024)
5397 {
5398 }
5399 
Allocate(Types...args)5400 template<typename... Types> Allocation* AllocationObjectAllocator::Allocate(Types... args)
5401 {
5402     MutexLock mutexLock(m_Mutex);
5403     return m_Allocator.Alloc(std::forward<Types>(args)...);
5404 }
5405 
Free(Allocation * alloc)5406 void AllocationObjectAllocator::Free(Allocation* alloc)
5407 {
5408     MutexLock mutexLock(m_Mutex);
5409     m_Allocator.Free(alloc);
5410 }
5411 
5412 ////////////////////////////////////////////////////////////////////////////////
5413 // Public class Allocator implementation
5414 
Allocator(const ALLOCATION_CALLBACKS & allocationCallbacks,const ALLOCATOR_DESC & desc)5415 Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) :
5416     m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc))
5417 {
5418 }
5419 
~Allocator()5420 Allocator::~Allocator()
5421 {
5422     D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);
5423 }
5424 
Release()5425 void Allocator::Release()
5426 {
5427     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5428 
5429     // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.
5430     const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs();
5431     D3D12MA_DELETE(allocationCallbacksCopy, this);
5432 }
5433 
5434 
5435 
GetD3D12Options() const5436 const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const
5437 {
5438     return m_Pimpl->GetD3D12Options();
5439 }
5440 
CreateResource(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_DESC * pResourceDesc,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,Allocation ** ppAllocation,REFIID riidResource,void ** ppvResource)5441 HRESULT Allocator::CreateResource(
5442     const ALLOCATION_DESC* pAllocDesc,
5443     const D3D12_RESOURCE_DESC* pResourceDesc,
5444     D3D12_RESOURCE_STATES InitialResourceState,
5445     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
5446     Allocation** ppAllocation,
5447     REFIID riidResource,
5448     void** ppvResource)
5449 {
5450     if(!pAllocDesc || !pResourceDesc || !ppAllocation)
5451     {
5452         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource.");
5453         return E_INVALIDARG;
5454     }
5455     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5456     return m_Pimpl->CreateResource(pAllocDesc, pResourceDesc, InitialResourceState, pOptimizedClearValue, ppAllocation, riidResource, ppvResource);
5457 }
5458 
AllocateMemory(const ALLOCATION_DESC * pAllocDesc,const D3D12_RESOURCE_ALLOCATION_INFO * pAllocInfo,Allocation ** ppAllocation)5459 HRESULT Allocator::AllocateMemory(
5460     const ALLOCATION_DESC* pAllocDesc,
5461     const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
5462     Allocation** ppAllocation)
5463 {
5464     if(!pAllocDesc ||
5465         !pAllocInfo ||
5466         !ppAllocation ||
5467         !(pAllocInfo->Alignment == 0 ||
5468             pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||
5469             pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) ||
5470         pAllocInfo->SizeInBytes == 0 ||
5471         pAllocInfo->SizeInBytes % (64ull * 1024) != 0)
5472     {
5473         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory.");
5474         return E_INVALIDARG;
5475     }
5476     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5477     return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);
5478 }
5479 
CreateAliasingResource(Allocation * pAllocation,UINT64 AllocationLocalOffset,const D3D12_RESOURCE_DESC * pResourceDesc,D3D12_RESOURCE_STATES InitialResourceState,const D3D12_CLEAR_VALUE * pOptimizedClearValue,REFIID riidResource,void ** ppvResource)5480 HRESULT Allocator::CreateAliasingResource(
5481     Allocation* pAllocation,
5482     UINT64 AllocationLocalOffset,
5483     const D3D12_RESOURCE_DESC* pResourceDesc,
5484     D3D12_RESOURCE_STATES InitialResourceState,
5485     const D3D12_CLEAR_VALUE *pOptimizedClearValue,
5486     REFIID riidResource,
5487     void** ppvResource)
5488 {
5489     if(!pAllocation || !pResourceDesc || !ppvResource)
5490     {
5491         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");
5492         return E_INVALIDARG;
5493     }
5494     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5495     return m_Pimpl->CreateAliasingResource(pAllocation, AllocationLocalOffset, pResourceDesc, InitialResourceState, pOptimizedClearValue, riidResource, ppvResource);
5496 }
5497 
CreatePool(const POOL_DESC * pPoolDesc,Pool ** ppPool)5498 HRESULT Allocator::CreatePool(
5499     const POOL_DESC* pPoolDesc,
5500     Pool** ppPool)
5501 {
5502     if(!pPoolDesc || !ppPool ||
5503         !IsHeapTypeValid(pPoolDesc->HeapType) ||
5504         (pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount))
5505     {
5506         D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");
5507         return E_INVALIDARG;
5508     }
5509     if(!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))
5510     {
5511         D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");
5512         return E_INVALIDARG;
5513     }
5514     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5515     *ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);
5516     HRESULT hr = (*ppPool)->m_Pimpl->Init();
5517     if(SUCCEEDED(hr))
5518     {
5519         m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapType);
5520     }
5521     else
5522     {
5523         D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);
5524         *ppPool = NULL;
5525     }
5526     return hr;
5527 }
5528 
SetDefaultHeapMinBytes(D3D12_HEAP_TYPE heapType,D3D12_HEAP_FLAGS heapFlags,UINT64 minBytes)5529 HRESULT Allocator::SetDefaultHeapMinBytes(
5530     D3D12_HEAP_TYPE heapType,
5531     D3D12_HEAP_FLAGS heapFlags,
5532     UINT64 minBytes)
5533 {
5534     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5535     return m_Pimpl->SetDefaultHeapMinBytes(heapType, heapFlags, minBytes);
5536 }
5537 
SetCurrentFrameIndex(UINT frameIndex)5538 void Allocator::SetCurrentFrameIndex(UINT frameIndex)
5539 {
5540     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5541     m_Pimpl->SetCurrentFrameIndex(frameIndex);
5542 }
5543 
CalculateStats(Stats * pStats)5544 void Allocator::CalculateStats(Stats* pStats)
5545 {
5546     D3D12MA_ASSERT(pStats);
5547     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5548     m_Pimpl->CalculateStats(*pStats);
5549 }
5550 
GetBudget(Budget * pGpuBudget,Budget * pCpuBudget)5551 void Allocator::GetBudget(Budget* pGpuBudget, Budget* pCpuBudget)
5552 {
5553     if(pGpuBudget == NULL && pCpuBudget == NULL)
5554     {
5555         return;
5556     }
5557     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5558     m_Pimpl->GetBudget(pGpuBudget, pCpuBudget);
5559 }
5560 
BuildStatsString(WCHAR ** ppStatsString,BOOL DetailedMap) const5561 void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const
5562 {
5563     D3D12MA_ASSERT(ppStatsString);
5564     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5565     m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);
5566 }
5567 
FreeStatsString(WCHAR * pStatsString) const5568 void Allocator::FreeStatsString(WCHAR* pStatsString) const
5569 {
5570     if (pStatsString != NULL)
5571     {
5572         D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5573         m_Pimpl->FreeStatsString(pStatsString);
5574     }
5575 }
5576 
5577 ////////////////////////////////////////////////////////////////////////////////
5578 // Private class VirtualBlockPimpl definition
5579 
5580 class VirtualBlockPimpl
5581 {
5582 public:
5583     const ALLOCATION_CALLBACKS m_AllocationCallbacks;
5584     const UINT64 m_Size;
5585     BlockMetadata_Generic m_Metadata;
5586 
5587     VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT64 size);
5588     ~VirtualBlockPimpl();
5589 };
5590 
VirtualBlockPimpl(const ALLOCATION_CALLBACKS & allocationCallbacks,UINT64 size)5591 VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT64 size) :
5592     m_AllocationCallbacks(allocationCallbacks),
5593     m_Size(size),
5594     m_Metadata(&m_AllocationCallbacks,
5595         true) // isVirtual
5596 {
5597     m_Metadata.Init(m_Size);
5598 }
5599 
~VirtualBlockPimpl()5600 VirtualBlockPimpl::~VirtualBlockPimpl()
5601 {
5602 }
5603 
5604 ////////////////////////////////////////////////////////////////////////////////
5605 // Public class VirtualBlock implementation
5606 
VirtualBlock(const ALLOCATION_CALLBACKS & allocationCallbacks,const VIRTUAL_BLOCK_DESC & desc)5607 VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc) :
5608     m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc.Size))
5609 {
5610 }
5611 
~VirtualBlock()5612 VirtualBlock::~VirtualBlock()
5613 {
5614     // THIS IS AN IMPORTANT ASSERT!
5615     // Hitting it means you have some memory leak - unreleased allocations in this virtual block.
5616     D3D12MA_ASSERT(m_Pimpl->m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");
5617 
5618     D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);
5619 }
5620 
Release()5621 void VirtualBlock::Release()
5622 {
5623     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5624 
5625     // Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.
5626     const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;
5627     D3D12MA_DELETE(allocationCallbacksCopy, this);
5628 }
5629 
IsEmpty() const5630 BOOL VirtualBlock::IsEmpty() const
5631 {
5632     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5633 
5634     return m_Pimpl->m_Metadata.IsEmpty() ? TRUE : FALSE;
5635 }
5636 
GetAllocationInfo(UINT64 offset,VIRTUAL_ALLOCATION_INFO * pInfo) const5637 void VirtualBlock::GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO* pInfo) const
5638 {
5639     D3D12MA_ASSERT(offset != UINT64_MAX && pInfo);
5640 
5641     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5642 
5643     m_Pimpl->m_Metadata.GetAllocationInfo(offset, *pInfo);
5644 }
5645 
Allocate(const VIRTUAL_ALLOCATION_DESC * pDesc,UINT64 * pOffset)5646 HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, UINT64* pOffset)
5647 {
5648     if(!pDesc || !pOffset || pDesc->Size == 0 || !IsPow2(pDesc->Alignment))
5649     {
5650         D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");
5651         return E_INVALIDARG;
5652     }
5653 
5654     *pOffset = UINT64_MAX;
5655 
5656     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5657 
5658     const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1;
5659     AllocationRequest allocRequest = {};
5660     if(m_Pimpl->m_Metadata.CreateAllocationRequest(pDesc->Size, alignment, &allocRequest))
5661     {
5662         m_Pimpl->m_Metadata.Alloc(allocRequest, pDesc->Size, pDesc->pUserData);
5663         D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
5664         *pOffset = allocRequest.offset;
5665         return S_OK;
5666     }
5667     else
5668     {
5669         return E_OUTOFMEMORY;
5670     }
5671 }
5672 
FreeAllocation(UINT64 offset)5673 void VirtualBlock::FreeAllocation(UINT64 offset)
5674 {
5675     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5676 
5677     D3D12MA_ASSERT(offset != UINT64_MAX);
5678 
5679     m_Pimpl->m_Metadata.FreeAtOffset(offset);
5680     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
5681 }
5682 
Clear()5683 void VirtualBlock::Clear()
5684 {
5685     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5686 
5687     m_Pimpl->m_Metadata.Clear();
5688     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
5689 }
5690 
SetAllocationUserData(UINT64 offset,void * pUserData)5691 void VirtualBlock::SetAllocationUserData(UINT64 offset, void* pUserData)
5692 {
5693     D3D12MA_ASSERT(offset != UINT64_MAX);
5694 
5695     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5696 
5697     m_Pimpl->m_Metadata.SetAllocationUserData(offset, pUserData);
5698 }
5699 
CalculateStats(StatInfo * pInfo) const5700 void VirtualBlock::CalculateStats(StatInfo* pInfo) const
5701 {
5702     D3D12MA_ASSERT(pInfo);
5703 
5704     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5705 
5706     D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
5707     m_Pimpl->m_Metadata.CalcAllocationStatInfo(*pInfo);
5708 }
5709 
BuildStatsString(WCHAR ** ppStatsString) const5710 void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const
5711 {
5712     D3D12MA_ASSERT(ppStatsString);
5713 
5714     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5715 
5716     StringBuilder sb(m_Pimpl->m_AllocationCallbacks);
5717     {
5718         JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);
5719         D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata.Validate());
5720         m_Pimpl->m_Metadata.WriteAllocationInfoToJson(json);
5721     } // Scope for JsonWriter
5722 
5723     const size_t length = sb.GetLength();
5724     WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);
5725     memcpy(result, sb.GetData(), length * sizeof(WCHAR));
5726     result[length] = L'\0';
5727     *ppStatsString = result;
5728 }
5729 
FreeStatsString(WCHAR * pStatsString) const5730 void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const
5731 {
5732     if (pStatsString != NULL)
5733     {
5734         D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5735         D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);
5736     }
5737 }
5738 
5739 
5740 ////////////////////////////////////////////////////////////////////////////////
5741 // Public global functions
5742 
CreateAllocator(const ALLOCATOR_DESC * pDesc,Allocator ** ppAllocator)5743 HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)
5744 {
5745     if(!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||
5746         !(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))
5747     {
5748         D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");
5749         return E_INVALIDARG;
5750     }
5751 
5752     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5753 
5754     ALLOCATION_CALLBACKS allocationCallbacks;
5755     SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
5756 
5757     *ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);
5758     HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);
5759     if(FAILED(hr))
5760     {
5761         D3D12MA_DELETE(allocationCallbacks, *ppAllocator);
5762         *ppAllocator = NULL;
5763     }
5764     return hr;
5765 }
5766 
CreateVirtualBlock(const VIRTUAL_BLOCK_DESC * pDesc,VirtualBlock ** ppVirtualBlock)5767 HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)
5768 {
5769     if(!pDesc || !ppVirtualBlock)
5770     {
5771         D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");
5772         return E_INVALIDARG;
5773     }
5774 
5775     D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
5776 
5777     ALLOCATION_CALLBACKS allocationCallbacks;
5778     SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);
5779 
5780     *ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);
5781     return S_OK;
5782 }
5783 
5784 } // namespace D3D12MA
5785