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