• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "Common.h"
24 #include "Tests.h"
25 #include <thread>
26 
27 extern ID3D12GraphicsCommandList* BeginCommandList();
28 extern void EndCommandList(ID3D12GraphicsCommandList* cmdList);
29 
30 static constexpr UINT64 MEGABYTE = 1024 * 1024;
31 
32 template<typename T>
33 struct D3d12maObjDeleter
34 {
operator ()D3d12maObjDeleter35     void operator()(T* obj) const
36     {
37         if(obj)
38         {
39             obj->Release();
40         }
41     }
42 };
43 
44 typedef std::unique_ptr<D3D12MA::Allocation, D3d12maObjDeleter<D3D12MA::Allocation>> AllocationUniquePtr;
45 typedef std::unique_ptr<D3D12MA::Pool, D3d12maObjDeleter<D3D12MA::Pool>> PoolUniquePtr;
46 typedef std::unique_ptr<D3D12MA::VirtualBlock, D3d12maObjDeleter<D3D12MA::VirtualBlock>> VirtualBlockUniquePtr;
47 
48 struct ResourceWithAllocation
49 {
50     CComPtr<ID3D12Resource> resource;
51     AllocationUniquePtr allocation;
52     UINT64 size = UINT64_MAX;
53     UINT dataSeed = 0;
54 
ResetResourceWithAllocation55     void Reset()
56     {
57         resource.Release();
58         allocation.reset();
59         size = UINT64_MAX;
60         dataSeed = 0;
61     }
62 };
63 
FillResourceDescForBuffer(D3D12_RESOURCE_DESC & outResourceDesc,UINT64 size)64 static void FillResourceDescForBuffer(D3D12_RESOURCE_DESC& outResourceDesc, UINT64 size)
65 {
66     outResourceDesc = {};
67     outResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
68     outResourceDesc.Alignment = 0;
69     outResourceDesc.Width = size;
70     outResourceDesc.Height = 1;
71     outResourceDesc.DepthOrArraySize = 1;
72     outResourceDesc.MipLevels = 1;
73     outResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
74     outResourceDesc.SampleDesc.Count = 1;
75     outResourceDesc.SampleDesc.Quality = 0;
76     outResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
77     outResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
78 }
79 
FillData(void * outPtr,const UINT64 sizeInBytes,UINT seed)80 static void FillData(void* outPtr, const UINT64 sizeInBytes, UINT seed)
81 {
82     UINT* outValues = (UINT*)outPtr;
83     const UINT64 sizeInValues = sizeInBytes / sizeof(UINT);
84     UINT value = seed;
85     for(UINT i = 0; i < sizeInValues; ++i)
86     {
87         outValues[i] = value++;
88     }
89 }
90 
ValidateData(const void * ptr,const UINT64 sizeInBytes,UINT seed)91 static bool ValidateData(const void* ptr, const UINT64 sizeInBytes, UINT seed)
92 {
93     const UINT* values = (const UINT*)ptr;
94     const UINT64 sizeInValues = sizeInBytes / sizeof(UINT);
95     UINT value = seed;
96     for(UINT i = 0; i < sizeInValues; ++i)
97     {
98         if(values[i] != value++)
99         {
100             //FAIL("ValidateData failed.");
101             return false;
102         }
103     }
104     return true;
105 }
106 
ValidateDataZero(const void * ptr,const UINT64 sizeInBytes)107 static bool ValidateDataZero(const void* ptr, const UINT64 sizeInBytes)
108 {
109     const UINT* values = (const UINT*)ptr;
110     const UINT64 sizeInValues = sizeInBytes / sizeof(UINT);
111     for(UINT i = 0; i < sizeInValues; ++i)
112     {
113         if(values[i] != 0)
114         {
115             //FAIL("ValidateData failed.");
116             return false;
117         }
118     }
119     return true;
120 }
121 
TestVirtualBlocks(const TestContext & ctx)122 static void TestVirtualBlocks(const TestContext& ctx)
123 {
124     wprintf(L"Test virtual blocks\n");
125 
126     using namespace D3D12MA;
127 
128     const UINT64 blockSize = 16 * MEGABYTE;
129     const UINT64 alignment = 256;
130 
131     // # Create block 16 MB
132 
133     VirtualBlockUniquePtr block;
134     VirtualBlock* blockPtr = nullptr;
135     VIRTUAL_BLOCK_DESC blockDesc = {};
136     blockDesc.pAllocationCallbacks = ctx.allocationCallbacks;
137     blockDesc.Size = blockSize;
138     CHECK_HR( CreateVirtualBlock(&blockDesc, &blockPtr) );
139     CHECK_BOOL( blockPtr );
140     block.reset(blockPtr);
141 
142     // # Allocate 8 MB
143 
144     VIRTUAL_ALLOCATION_DESC allocDesc = {};
145     allocDesc.Alignment = alignment;
146     allocDesc.pUserData = (void*)(uintptr_t)1;
147     allocDesc.Size = 8 * MEGABYTE;
148     UINT64 alloc0Offset;
149     CHECK_HR( block->Allocate(&allocDesc, &alloc0Offset) );
150     CHECK_BOOL( alloc0Offset < blockSize );
151 
152     // # Validate the allocation
153 
154     VIRTUAL_ALLOCATION_INFO allocInfo = {};
155     block->GetAllocationInfo(alloc0Offset, &allocInfo);
156     CHECK_BOOL( allocInfo.size == allocDesc.Size );
157     CHECK_BOOL( allocInfo.pUserData == allocDesc.pUserData );
158 
159     // # Check SetUserData
160 
161     block->SetAllocationUserData(alloc0Offset, (void*)(uintptr_t)2);
162     block->GetAllocationInfo(alloc0Offset, &allocInfo);
163     CHECK_BOOL( allocInfo.pUserData == (void*)(uintptr_t)2 );
164 
165     // # Allocate 4 MB
166 
167     allocDesc.Size = 4 * MEGABYTE;
168     allocDesc.Alignment = alignment;
169     UINT64 alloc1Offset;
170     CHECK_HR( block->Allocate(&allocDesc, &alloc1Offset) );
171     CHECK_BOOL( alloc1Offset < blockSize );
172     CHECK_BOOL( alloc1Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc1Offset ); // Check if they don't overlap.
173 
174     // # Allocate another 8 MB - it should fail
175 
176     allocDesc.Size = 8 * MEGABYTE;
177     allocDesc.Alignment = alignment;
178     UINT64 alloc2Offset;
179     CHECK_BOOL( FAILED(block->Allocate(&allocDesc, &alloc2Offset)) );
180     CHECK_BOOL( alloc2Offset == UINT64_MAX );
181 
182     // # Free the 4 MB block. Now allocation of 8 MB should succeed.
183 
184     block->FreeAllocation(alloc1Offset);
185     CHECK_HR( block->Allocate(&allocDesc, &alloc2Offset) );
186     CHECK_BOOL( alloc2Offset < blockSize );
187     CHECK_BOOL( alloc2Offset + 4 * MEGABYTE <= alloc0Offset || alloc0Offset + 8 * MEGABYTE <= alloc2Offset ); // Check if they don't overlap.
188 
189     // # Calculate statistics
190 
191     StatInfo statInfo = {};
192     block->CalculateStats(&statInfo);
193     CHECK_BOOL(statInfo.AllocationCount == 2);
194     CHECK_BOOL(statInfo.BlockCount == 1);
195     CHECK_BOOL(statInfo.UsedBytes == blockSize);
196     CHECK_BOOL(statInfo.UnusedBytes + statInfo.UsedBytes == blockSize);
197 
198     // # Generate JSON dump
199 
200     WCHAR* json = nullptr;
201     block->BuildStatsString(&json);
202     {
203         std::wstring str(json);
204         CHECK_BOOL( str.find(L"\"UserData\": 1") != std::wstring::npos );
205         CHECK_BOOL( str.find(L"\"UserData\": 2") != std::wstring::npos );
206     }
207     block->FreeStatsString(json);
208 
209     // # Free alloc0, leave alloc2 unfreed.
210 
211     block->FreeAllocation(alloc0Offset);
212 
213     // # Test alignment
214 
215     {
216         constexpr size_t allocCount = 10;
217         UINT64 allocOffset[allocCount] = {};
218         for(size_t i = 0; i < allocCount; ++i)
219         {
220             const bool alignment0 = i == allocCount - 1;
221             allocDesc.Size = i * 3 + 15;
222             allocDesc.Alignment = alignment0 ? 0 : 8;
223             CHECK_HR(block->Allocate(&allocDesc, &allocOffset[i]));
224             if(!alignment0)
225             {
226                 CHECK_BOOL(allocOffset[i] % allocDesc.Alignment == 0);
227             }
228         }
229 
230         for(size_t i = allocCount; i--; )
231         {
232             block->FreeAllocation(allocOffset[i]);
233         }
234     }
235 
236     // # Final cleanup
237 
238     block->FreeAllocation(alloc2Offset);
239 
240     //block->Clear();
241 }
242 
TestFrameIndexAndJson(const TestContext & ctx)243 static void TestFrameIndexAndJson(const TestContext& ctx)
244 {
245     const UINT64 bufSize = 32ull * 1024;
246 
247     D3D12MA::ALLOCATION_DESC allocDesc = {};
248     allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
249     allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;
250 
251     D3D12_RESOURCE_DESC resourceDesc;
252     FillResourceDescForBuffer(resourceDesc, bufSize);
253 
254     const UINT BEGIN_INDEX = 10;
255     const UINT END_INDEX = 20;
256     for (UINT frameIndex = BEGIN_INDEX; frameIndex < END_INDEX; ++frameIndex)
257     {
258         ctx.allocator->SetCurrentFrameIndex(frameIndex);
259         D3D12MA::Allocation* alloc = nullptr;
260         CHECK_HR(ctx.allocator->CreateResource(
261             &allocDesc,
262             &resourceDesc,
263             D3D12_RESOURCE_STATE_GENERIC_READ,
264             NULL,
265             &alloc,
266             __uuidof(ID3D12Resource),
267             NULL));
268 
269         WCHAR* statsString;
270         ctx.allocator->BuildStatsString(&statsString, TRUE);
271         const UINT BUFFER_SIZE = 1024;
272         WCHAR buffer[BUFFER_SIZE];
273         for (UINT testIndex = BEGIN_INDEX; testIndex < END_INDEX; ++testIndex)
274         {
275             swprintf(buffer, BUFFER_SIZE, L"\"CreationFrameIndex\": %u", testIndex);
276             if (testIndex == frameIndex)
277             {
278                 CHECK_BOOL(wcsstr(statsString, buffer) != NULL);
279             }
280             else
281             {
282                 CHECK_BOOL(wcsstr(statsString, buffer) == NULL);
283             }
284         }
285         ctx.allocator->FreeStatsString(statsString);
286         alloc->Release();
287     }
288 }
289 
TestCommittedResourcesAndJson(const TestContext & ctx)290 static void TestCommittedResourcesAndJson(const TestContext& ctx)
291 {
292     wprintf(L"Test committed resources and JSON\n");
293 
294     const UINT count = 4;
295     const UINT64 bufSize = 32ull * 1024;
296     const wchar_t* names[count] = {
297         L"Resource\nFoo\r\nBar",
298         L"Resource \"'&<>?#@!&-=_+[]{};:,./\\",
299         nullptr,
300         L"",
301     };
302 
303     ResourceWithAllocation resources[count];
304 
305     D3D12MA::ALLOCATION_DESC allocDesc = {};
306     allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
307     allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;
308 
309     D3D12_RESOURCE_DESC resourceDesc;
310     FillResourceDescForBuffer(resourceDesc, bufSize);
311 
312     for(UINT i = 0; i < count; ++i)
313     {
314         const bool receiveExplicitResource = i < 2;
315 
316         D3D12MA::Allocation* alloc = nullptr;
317         CHECK_HR( ctx.allocator->CreateResource(
318             &allocDesc,
319             &resourceDesc,
320             D3D12_RESOURCE_STATE_COPY_DEST,
321             NULL,
322             &alloc,
323             __uuidof(ID3D12Resource),
324             receiveExplicitResource ? (void**)&resources[i].resource : NULL));
325         resources[i].allocation.reset(alloc);
326 
327         if(receiveExplicitResource)
328         {
329             ID3D12Resource* res = resources[i].resource.p;
330             CHECK_BOOL(res && res == resources[i].allocation->GetResource());
331             const ULONG refCountAfterAdd = res->AddRef();
332             CHECK_BOOL(refCountAfterAdd == 3);
333             res->Release();
334         }
335 
336         // Make sure it has implicit heap.
337         CHECK_BOOL( resources[i].allocation->GetHeap() == NULL && resources[i].allocation->GetOffset() == 0 );
338 
339         resources[i].allocation->SetName(names[i]);
340     }
341 
342     // Check names.
343     for(UINT i = 0; i < count; ++i)
344     {
345         const wchar_t* const allocName = resources[i].allocation->GetName();
346         if(allocName)
347         {
348             CHECK_BOOL( wcscmp(allocName, names[i]) == 0 );
349         }
350         else
351         {
352             CHECK_BOOL(names[i] == NULL);
353         }
354     }
355 
356     WCHAR* jsonString;
357     ctx.allocator->BuildStatsString(&jsonString, TRUE);
358     CHECK_BOOL(wcsstr(jsonString, L"\"Resource\\nFoo\\r\\nBar\"") != NULL);
359     CHECK_BOOL(wcsstr(jsonString, L"\"Resource \\\"'&<>?#@!&-=_+[]{};:,.\\/\\\\\"") != NULL);
360     CHECK_BOOL(wcsstr(jsonString, L"\"\"") != NULL);
361     ctx.allocator->FreeStatsString(jsonString);
362 }
363 
TestCustomHeapFlags(const TestContext & ctx)364 static void TestCustomHeapFlags(const TestContext& ctx)
365 {
366     wprintf(L"Test custom heap flags\n");
367 
368     // 1. Just memory heap with custom flags
369     {
370         D3D12MA::ALLOCATION_DESC allocDesc = {};
371         allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
372         allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES |
373             D3D12_HEAP_FLAG_SHARED; // Extra flag.
374 
375         D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = {};
376         resAllocInfo.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
377         resAllocInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
378 
379         D3D12MA::Allocation* alloc = nullptr;
380         CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, &resAllocInfo, &alloc) );
381         ResourceWithAllocation res;
382         res.allocation.reset(alloc);
383 
384         // Must be created as separate allocation.
385         CHECK_BOOL( res.allocation->GetOffset() == 0 );
386     }
387 
388     // 2. Committed resource with custom flags
389     {
390         D3D12_RESOURCE_DESC resourceDesc = {};
391         resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
392         resourceDesc.Alignment = 0;
393         resourceDesc.Width = 1920;
394         resourceDesc.Height = 1080;
395         resourceDesc.DepthOrArraySize = 1;
396         resourceDesc.MipLevels = 1;
397         resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
398         resourceDesc.SampleDesc.Count = 1;
399         resourceDesc.SampleDesc.Quality = 0;
400         resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
401         resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
402 
403         D3D12MA::ALLOCATION_DESC allocDesc = {};
404         allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
405         allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER; // Extra flags.
406 
407         ResourceWithAllocation res;
408         D3D12MA::Allocation* alloc = nullptr;
409         CHECK_HR( ctx.allocator->CreateResource(
410             &allocDesc,
411             &resourceDesc,
412             D3D12_RESOURCE_STATE_COMMON,
413             NULL,
414             &alloc,
415             IID_PPV_ARGS(&res.resource)) );
416         res.allocation.reset(alloc);
417 
418         // Must be created as committed.
419         CHECK_BOOL( res.allocation->GetHeap() == NULL );
420     }
421 }
422 
TestPlacedResources(const TestContext & ctx)423 static void TestPlacedResources(const TestContext& ctx)
424 {
425     wprintf(L"Test placed resources\n");
426 
427     const bool alwaysCommitted = (ctx.allocatorFlags & D3D12MA::ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0;
428 
429     const UINT count = 4;
430     const UINT64 bufSize = 32ull * 1024;
431     ResourceWithAllocation resources[count];
432 
433     D3D12MA::ALLOCATION_DESC allocDesc = {};
434     allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
435 
436     D3D12_RESOURCE_DESC resourceDesc;
437     FillResourceDescForBuffer(resourceDesc, bufSize);
438 
439     D3D12MA::Allocation* alloc = nullptr;
440     for(UINT i = 0; i < count; ++i)
441     {
442         CHECK_HR( ctx.allocator->CreateResource(
443             &allocDesc,
444             &resourceDesc,
445             D3D12_RESOURCE_STATE_GENERIC_READ,
446             NULL,
447             &alloc,
448             IID_PPV_ARGS(&resources[i].resource)) );
449         resources[i].allocation.reset(alloc);
450 
451         // Make sure it doesn't have implicit heap.
452         if(!alwaysCommitted)
453         {
454             CHECK_BOOL( resources[i].allocation->GetHeap() != NULL );
455         }
456     }
457 
458     // Make sure at least some of the resources belong to the same heap, but their memory ranges don't overlap.
459     bool sameHeapFound = false;
460     for(size_t i = 0; i < count; ++i)
461     {
462         for(size_t j = i + 1; j < count; ++j)
463         {
464             const ResourceWithAllocation& resI = resources[i];
465             const ResourceWithAllocation& resJ = resources[j];
466             if(resI.allocation->GetHeap() != NULL &&
467                 resI.allocation->GetHeap() == resJ.allocation->GetHeap())
468             {
469                 sameHeapFound = true;
470                 CHECK_BOOL(resI.allocation->GetOffset() + resI.allocation->GetSize() <= resJ.allocation->GetOffset() ||
471                     resJ.allocation->GetOffset() + resJ.allocation->GetSize() <= resI.allocation->GetOffset());
472             }
473         }
474     }
475     if(!alwaysCommitted)
476     {
477         CHECK_BOOL(sameHeapFound);
478     }
479 
480     // Additionally create a texture to see if no error occurs due to bad handling of Resource Tier.
481     resourceDesc = {};
482     resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
483     resourceDesc.Alignment = 0;
484     resourceDesc.Width = 1024;
485     resourceDesc.Height = 1024;
486     resourceDesc.DepthOrArraySize = 1;
487     resourceDesc.MipLevels = 1;
488     resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
489     resourceDesc.SampleDesc.Count = 1;
490     resourceDesc.SampleDesc.Quality = 0;
491     resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
492     resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
493     ResourceWithAllocation textureRes;
494     CHECK_HR( ctx.allocator->CreateResource(
495         &allocDesc,
496         &resourceDesc,
497         D3D12_RESOURCE_STATE_COPY_DEST,
498         NULL,
499         &alloc,
500         IID_PPV_ARGS(&textureRes.resource)) );
501     textureRes.allocation.reset(alloc);
502 
503     // Additionally create an MSAA render target to see if no error occurs due to bad handling of Resource Tier.
504     resourceDesc = {};
505     resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
506     resourceDesc.Alignment = 0;
507     resourceDesc.Width = 1920;
508     resourceDesc.Height = 1080;
509     resourceDesc.DepthOrArraySize = 1;
510     resourceDesc.MipLevels = 1;
511     resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
512     resourceDesc.SampleDesc.Count = 2;
513     resourceDesc.SampleDesc.Quality = 0;
514     resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
515     resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
516     ResourceWithAllocation renderTargetRes;
517     CHECK_HR( ctx.allocator->CreateResource(
518         &allocDesc,
519         &resourceDesc,
520         D3D12_RESOURCE_STATE_RENDER_TARGET,
521         NULL,
522         &alloc,
523         IID_PPV_ARGS(&renderTargetRes.resource)) );
524     renderTargetRes.allocation.reset(alloc);
525 }
526 
TestOtherComInterface(const TestContext & ctx)527 static void TestOtherComInterface(const TestContext& ctx)
528 {
529     wprintf(L"Test other COM interface\n");
530 
531     D3D12_RESOURCE_DESC resDesc;
532     FillResourceDescForBuffer(resDesc, 0x10000);
533 
534     for(uint32_t i = 0; i < 2; ++i)
535     {
536         D3D12MA::ALLOCATION_DESC allocDesc = {};
537         allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
538         if(i == 1)
539         {
540             allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;
541         }
542 
543         D3D12MA::Allocation* alloc = nullptr;
544         CComPtr<ID3D12Pageable> pageable;
545         CHECK_HR(ctx.allocator->CreateResource(
546             &allocDesc,
547             &resDesc,
548             D3D12_RESOURCE_STATE_COMMON,
549             nullptr, // pOptimizedClearValue
550             &alloc,
551             IID_PPV_ARGS(&pageable)));
552 
553         // Do something with the interface to make sure it's valid.
554         CComPtr<ID3D12Device> device;
555         CHECK_HR(pageable->GetDevice(IID_PPV_ARGS(&device)));
556         CHECK_BOOL(device == ctx.device);
557 
558         alloc->Release();
559     }
560 }
561 
TestCustomPools(const TestContext & ctx)562 static void TestCustomPools(const TestContext& ctx)
563 {
564     wprintf(L"Test custom pools\n");
565 
566     // # Fetch global stats 1
567 
568     D3D12MA::Stats globalStatsBeg = {};
569     ctx.allocator->CalculateStats(&globalStatsBeg);
570 
571     // # Create pool, 1..2 blocks of 11 MB
572 
573     D3D12MA::POOL_DESC poolDesc = {};
574     poolDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
575     poolDesc.HeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
576     poolDesc.BlockSize = 11 * MEGABYTE;
577     poolDesc.MinBlockCount = 1;
578     poolDesc.MaxBlockCount = 2;
579 
580     D3D12MA::Pool* poolPtr;
581     CHECK_HR( ctx.allocator->CreatePool(&poolDesc, &poolPtr) );
582     PoolUniquePtr pool{poolPtr};
583 
584     D3D12MA::Allocation* allocPtr;
585 
586     // # Validate stats for empty pool
587 
588     D3D12MA::StatInfo poolStats = {};
589     pool->CalculateStats(&poolStats);
590     CHECK_BOOL( poolStats.BlockCount == 1 );
591     CHECK_BOOL( poolStats.AllocationCount == 0 );
592     CHECK_BOOL( poolStats.UsedBytes == 0 );
593     CHECK_BOOL( poolStats.UnusedBytes == poolStats.BlockCount * poolDesc.BlockSize );
594 
595     // # SetName and GetName
596     static const wchar_t* NAME = L"Custom pool name 1";
597     pool->SetName(NAME);
598     CHECK_BOOL( wcscmp(pool->GetName(), NAME) == 0 );
599 
600     // # SetMinBytes
601 
602     CHECK_HR( pool->SetMinBytes(15 * MEGABYTE) );
603     pool->CalculateStats(&poolStats);
604     CHECK_BOOL( poolStats.BlockCount == 2 );
605     CHECK_BOOL( poolStats.AllocationCount == 0 );
606     CHECK_BOOL( poolStats.UsedBytes == 0 );
607     CHECK_BOOL( poolStats.UnusedBytes == poolStats.BlockCount * poolDesc.BlockSize );
608 
609     CHECK_HR( pool->SetMinBytes(0) );
610     pool->CalculateStats(&poolStats);
611     CHECK_BOOL( poolStats.BlockCount == 1 );
612     CHECK_BOOL( poolStats.AllocationCount == 0 );
613     CHECK_BOOL( poolStats.UsedBytes == 0 );
614     CHECK_BOOL( poolStats.UnusedBytes == poolStats.BlockCount * poolDesc.BlockSize );
615 
616     // # Create buffers 2x 5 MB
617 
618     D3D12MA::ALLOCATION_DESC allocDesc = {};
619     allocDesc.CustomPool = pool.get();
620     allocDesc.ExtraHeapFlags = (D3D12_HEAP_FLAGS)0xCDCDCDCD; // Should be ignored.
621     allocDesc.HeapType = (D3D12_HEAP_TYPE)0xCDCDCDCD; // Should be ignored.
622 
623     const UINT64 BUFFER_SIZE = 5 * MEGABYTE;
624     D3D12_RESOURCE_DESC resDesc;
625     FillResourceDescForBuffer(resDesc, BUFFER_SIZE);
626 
627     AllocationUniquePtr allocs[4];
628     for(uint32_t i = 0; i < 2; ++i)
629     {
630         CHECK_HR( ctx.allocator->CreateResource(&allocDesc, &resDesc,
631             D3D12_RESOURCE_STATE_GENERIC_READ,
632             NULL, // pOptimizedClearValue
633             &allocPtr,
634             __uuidof(ID3D12Resource), NULL) ); // riidResource, ppvResource
635         allocs[i].reset(allocPtr);
636     }
637 
638     // # Validate pool stats now
639 
640     pool->CalculateStats(&poolStats);
641     CHECK_BOOL( poolStats.BlockCount == 1 );
642     CHECK_BOOL( poolStats.AllocationCount == 2 );
643     CHECK_BOOL( poolStats.UsedBytes == 2 * BUFFER_SIZE );
644     CHECK_BOOL( poolStats.UnusedBytes == poolDesc.BlockSize - poolStats.UsedBytes );
645 
646     // # Check that global stats are updated as well
647 
648     D3D12MA::Stats globalStatsCurr = {};
649     ctx.allocator->CalculateStats(&globalStatsCurr);
650 
651     CHECK_BOOL( globalStatsCurr.Total.AllocationCount == globalStatsBeg.Total.AllocationCount + poolStats.AllocationCount );
652     CHECK_BOOL( globalStatsCurr.Total.BlockCount == globalStatsBeg.Total.BlockCount + poolStats.BlockCount );
653     CHECK_BOOL( globalStatsCurr.Total.UsedBytes == globalStatsBeg.Total.UsedBytes + poolStats.UsedBytes );
654 
655     // # NEVER_ALLOCATE and COMMITTED should fail
656 
657     for(uint32_t i = 0; i < 2; ++i)
658     {
659         allocDesc.Flags = i == 0 ?
660             D3D12MA::ALLOCATION_FLAG_NEVER_ALLOCATE:
661             D3D12MA::ALLOCATION_FLAG_COMMITTED;
662         const HRESULT hr = ctx.allocator->CreateResource(&allocDesc, &resDesc,
663             D3D12_RESOURCE_STATE_GENERIC_READ,
664             NULL, // pOptimizedClearValue
665             &allocPtr,
666             __uuidof(ID3D12Resource), NULL); // riidResource, ppvResource
667         CHECK_BOOL( FAILED(hr) );
668     }
669 
670     // # 3 more buffers. 3rd should fail.
671 
672     allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_NONE;
673     for(uint32_t i = 2; i < 5; ++i)
674     {
675         HRESULT hr = ctx.allocator->CreateResource(&allocDesc, &resDesc,
676             D3D12_RESOURCE_STATE_GENERIC_READ,
677             NULL, // pOptimizedClearValue
678             &allocPtr,
679             __uuidof(ID3D12Resource), NULL); // riidResource, ppvResource
680         if(i < 4)
681         {
682             CHECK_HR( hr );
683             allocs[i].reset(allocPtr);
684         }
685         else
686         {
687             CHECK_BOOL( FAILED(hr) );
688         }
689     }
690 
691     pool->CalculateStats(&poolStats);
692     CHECK_BOOL( poolStats.BlockCount == 2 );
693     CHECK_BOOL( poolStats.AllocationCount == 4 );
694     CHECK_BOOL( poolStats.UsedBytes == 4 * BUFFER_SIZE );
695     CHECK_BOOL( poolStats.UnusedBytes == poolStats.BlockCount * poolDesc.BlockSize - poolStats.UsedBytes );
696 
697     // # Make room, AllocateMemory, CreateAliasingResource
698 
699     allocs[3].reset();
700     allocs[0].reset();
701 
702     D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = {};
703     resAllocInfo.SizeInBytes = 5 * MEGABYTE;
704     resAllocInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
705 
706     CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, &resAllocInfo, &allocPtr) );
707     allocs[0].reset(allocPtr);
708 
709     resDesc.Width = 1 * MEGABYTE;
710     CComPtr<ID3D12Resource> res;
711     CHECK_HR( ctx.allocator->CreateAliasingResource(allocs[0].get(),
712         0, // AllocationLocalOffset
713         &resDesc,
714         D3D12_RESOURCE_STATE_GENERIC_READ,
715         NULL, // pOptimizedClearValue
716         IID_PPV_ARGS(&res)) );
717 }
718 
TestDefaultPoolMinBytes(const TestContext & ctx)719 static void TestDefaultPoolMinBytes(const TestContext& ctx)
720 {
721     D3D12MA::Stats stats;
722     ctx.allocator->CalculateStats(&stats);
723     const UINT64 gpuAllocatedBefore = stats.HeapType[0].UsedBytes + stats.HeapType[0].UnusedBytes;
724 
725     const UINT64 gpuAllocatedMin = gpuAllocatedBefore * 105 / 100;
726     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,            CeilDiv(gpuAllocatedMin, 3ull)) );
727     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, CeilDiv(gpuAllocatedMin, 3ull)) );
728     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,     CeilDiv(gpuAllocatedMin, 3ull)) );
729 
730     ctx.allocator->CalculateStats(&stats);
731     const UINT64 gpuAllocatedAfter = stats.HeapType[0].UsedBytes + stats.HeapType[0].UnusedBytes;
732     CHECK_BOOL(gpuAllocatedAfter >= gpuAllocatedMin);
733 
734     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,            0) );
735     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, 0) );
736     CHECK_HR( ctx.allocator->SetDefaultHeapMinBytes(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,     0) );
737 }
738 
TestAliasingMemory(const TestContext & ctx)739 static void TestAliasingMemory(const TestContext& ctx)
740 {
741     wprintf(L"Test aliasing memory\n");
742 
743     D3D12_RESOURCE_DESC resDesc1 = {};
744     resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
745     resDesc1.Alignment = 0;
746     resDesc1.Width = 1920;
747     resDesc1.Height = 1080;
748     resDesc1.DepthOrArraySize = 1;
749     resDesc1.MipLevels = 1;
750     resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
751     resDesc1.SampleDesc.Count = 1;
752     resDesc1.SampleDesc.Quality = 0;
753     resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
754     resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
755 
756     D3D12_RESOURCE_DESC resDesc2 = {};
757     resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
758     resDesc2.Alignment = 0;
759     resDesc2.Width = 1024;
760     resDesc2.Height = 1024;
761     resDesc2.DepthOrArraySize = 1;
762     resDesc2.MipLevels = 0;
763     resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
764     resDesc2.SampleDesc.Count = 1;
765     resDesc2.SampleDesc.Quality = 0;
766     resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
767     resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
768 
769     const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 =
770         ctx.device->GetResourceAllocationInfo(0, 1, &resDesc1);
771     const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 =
772         ctx.device->GetResourceAllocationInfo(0, 1, &resDesc2);
773 
774     D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {};
775     finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment);
776     finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes);
777 
778     D3D12MA::ALLOCATION_DESC allocDesc = {};
779     allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
780     allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
781 
782     D3D12MA::Allocation* alloc = NULL;
783     CHECK_HR( ctx.allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc) );
784     CHECK_BOOL(alloc != NULL && alloc->GetHeap() != NULL);
785 
786     ID3D12Resource* res1 = NULL;
787     CHECK_HR( ctx.allocator->CreateAliasingResource(
788         alloc,
789         0, // AllocationLocalOffset
790         &resDesc1,
791         D3D12_RESOURCE_STATE_COMMON,
792         NULL, // pOptimizedClearValue
793         IID_PPV_ARGS(&res1)) );
794     CHECK_BOOL(res1 != NULL);
795 
796     ID3D12Resource* res2 = NULL;
797     CHECK_HR( ctx.allocator->CreateAliasingResource(
798         alloc,
799         0, // AllocationLocalOffset
800         &resDesc2,
801         D3D12_RESOURCE_STATE_COMMON,
802         NULL, // pOptimizedClearValue
803         IID_PPV_ARGS(&res2)) );
804     CHECK_BOOL(res2 != NULL);
805 
806     // You can use res1 and res2, but not at the same time!
807 
808     res2->Release();
809     res1->Release();
810     alloc->Release();
811 }
812 
TestMapping(const TestContext & ctx)813 static void TestMapping(const TestContext& ctx)
814 {
815     wprintf(L"Test mapping\n");
816 
817     const UINT count = 10;
818     const UINT64 bufSize = 32ull * 1024;
819     ResourceWithAllocation resources[count];
820 
821     D3D12MA::ALLOCATION_DESC allocDesc = {};
822     allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
823 
824     D3D12_RESOURCE_DESC resourceDesc;
825     FillResourceDescForBuffer(resourceDesc, bufSize);
826 
827     for(UINT i = 0; i < count; ++i)
828     {
829         D3D12MA::Allocation* alloc = nullptr;
830         CHECK_HR( ctx.allocator->CreateResource(
831             &allocDesc,
832             &resourceDesc,
833             D3D12_RESOURCE_STATE_GENERIC_READ,
834             NULL,
835             &alloc,
836             IID_PPV_ARGS(&resources[i].resource)) );
837         resources[i].allocation.reset(alloc);
838 
839         void* mappedPtr = NULL;
840         CHECK_HR( resources[i].resource->Map(0, &EMPTY_RANGE, &mappedPtr) );
841 
842         FillData(mappedPtr, bufSize, i);
843 
844         // Unmap every other buffer. Leave others mapped.
845         if((i % 2) != 0)
846         {
847             resources[i].resource->Unmap(0, NULL);
848         }
849     }
850 }
851 
StatInfoEqual(const D3D12MA::StatInfo & lhs,const D3D12MA::StatInfo & rhs)852 static inline bool StatInfoEqual(const D3D12MA::StatInfo& lhs, const D3D12MA::StatInfo& rhs)
853 {
854     return lhs.BlockCount == rhs.BlockCount &&
855         lhs.AllocationCount == rhs.AllocationCount &&
856         lhs.UnusedRangeCount == rhs.UnusedRangeCount &&
857         lhs.UsedBytes == rhs.UsedBytes &&
858         lhs.UnusedBytes == rhs.UnusedBytes &&
859         lhs.AllocationSizeMin == rhs.AllocationSizeMin &&
860         lhs.AllocationSizeMax == rhs.AllocationSizeMax &&
861         lhs.AllocationSizeAvg == rhs.AllocationSizeAvg &&
862         lhs.UnusedRangeSizeMin == rhs.UnusedRangeSizeMin &&
863         lhs.UnusedRangeSizeMax == rhs.UnusedRangeSizeMax &&
864         lhs.UnusedRangeSizeAvg == rhs.UnusedRangeSizeAvg;
865 }
866 
CheckStatInfo(const D3D12MA::StatInfo & statInfo)867 static void CheckStatInfo(const D3D12MA::StatInfo& statInfo)
868 {
869     if(statInfo.AllocationCount > 0)
870     {
871         CHECK_BOOL(statInfo.AllocationSizeAvg >= statInfo.AllocationSizeMin &&
872             statInfo.AllocationSizeAvg <= statInfo.AllocationSizeMax);
873     }
874     if(statInfo.UsedBytes > 0)
875     {
876         CHECK_BOOL(statInfo.AllocationCount > 0);
877     }
878     if(statInfo.UnusedRangeCount > 0)
879     {
880         CHECK_BOOL(statInfo.UnusedRangeSizeAvg >= statInfo.UnusedRangeSizeMin &&
881             statInfo.UnusedRangeSizeAvg <= statInfo.UnusedRangeSizeMax);
882         CHECK_BOOL(statInfo.UnusedRangeSizeMin > 0);
883         CHECK_BOOL(statInfo.UnusedRangeSizeMax > 0);
884     }
885 }
886 
TestStats(const TestContext & ctx)887 static void TestStats(const TestContext& ctx)
888 {
889     wprintf(L"Test stats\n");
890 
891     D3D12MA::Stats begStats = {};
892     ctx.allocator->CalculateStats(&begStats);
893 
894     const UINT count = 10;
895     const UINT64 bufSize = 64ull * 1024;
896     ResourceWithAllocation resources[count];
897 
898     D3D12MA::ALLOCATION_DESC allocDesc = {};
899     allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
900 
901     D3D12_RESOURCE_DESC resourceDesc;
902     FillResourceDescForBuffer(resourceDesc, bufSize);
903 
904     for(UINT i = 0; i < count; ++i)
905     {
906         if(i == count / 2)
907             allocDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
908         D3D12MA::Allocation* alloc = nullptr;
909         CHECK_HR( ctx.allocator->CreateResource(
910             &allocDesc,
911             &resourceDesc,
912             D3D12_RESOURCE_STATE_GENERIC_READ,
913             NULL,
914             &alloc,
915             IID_PPV_ARGS(&resources[i].resource)) );
916         resources[i].allocation.reset(alloc);
917     }
918 
919     D3D12MA::Stats endStats = {};
920     ctx.allocator->CalculateStats(&endStats);
921 
922     CHECK_BOOL(endStats.Total.BlockCount >= begStats.Total.BlockCount);
923     CHECK_BOOL(endStats.Total.AllocationCount == begStats.Total.AllocationCount + count);
924     CHECK_BOOL(endStats.Total.UsedBytes == begStats.Total.UsedBytes + count * bufSize);
925     CHECK_BOOL(endStats.Total.AllocationSizeMin <= bufSize);
926     CHECK_BOOL(endStats.Total.AllocationSizeMax >= bufSize);
927 
928     CHECK_BOOL(endStats.HeapType[1].BlockCount >= begStats.HeapType[1].BlockCount);
929     CHECK_BOOL(endStats.HeapType[1].AllocationCount >= begStats.HeapType[1].AllocationCount + count);
930     CHECK_BOOL(endStats.HeapType[1].UsedBytes >= begStats.HeapType[1].UsedBytes + count * bufSize);
931     CHECK_BOOL(endStats.HeapType[1].AllocationSizeMin <= bufSize);
932     CHECK_BOOL(endStats.HeapType[1].AllocationSizeMax >= bufSize);
933 
934     CHECK_BOOL(StatInfoEqual(begStats.HeapType[0], endStats.HeapType[0]));
935     CHECK_BOOL(StatInfoEqual(begStats.HeapType[2], endStats.HeapType[2]));
936 
937     CheckStatInfo(endStats.Total);
938     CheckStatInfo(endStats.HeapType[0]);
939     CheckStatInfo(endStats.HeapType[1]);
940     CheckStatInfo(endStats.HeapType[2]);
941 
942     D3D12MA::Budget gpuBudget = {}, cpuBudget = {};
943     ctx.allocator->GetBudget(&gpuBudget, &cpuBudget);
944 
945     CHECK_BOOL(gpuBudget.AllocationBytes <= gpuBudget.BlockBytes);
946     CHECK_BOOL(gpuBudget.AllocationBytes == endStats.HeapType[0].UsedBytes);
947     CHECK_BOOL(gpuBudget.BlockBytes == endStats.HeapType[0].UsedBytes + endStats.HeapType[0].UnusedBytes);
948 
949     CHECK_BOOL(cpuBudget.AllocationBytes <= cpuBudget.BlockBytes);
950     CHECK_BOOL(cpuBudget.AllocationBytes == endStats.HeapType[1].UsedBytes + endStats.HeapType[2].UsedBytes);
951     CHECK_BOOL(cpuBudget.BlockBytes == endStats.HeapType[1].UsedBytes + endStats.HeapType[1].UnusedBytes +
952         endStats.HeapType[2].UsedBytes + endStats.HeapType[2].UnusedBytes);
953 }
954 
TestTransfer(const TestContext & ctx)955 static void TestTransfer(const TestContext& ctx)
956 {
957     wprintf(L"Test mapping\n");
958 
959     const UINT count = 10;
960     const UINT64 bufSize = 32ull * 1024;
961 
962     ResourceWithAllocation resourcesUpload[count];
963     ResourceWithAllocation resourcesDefault[count];
964     ResourceWithAllocation resourcesReadback[count];
965 
966     D3D12MA::ALLOCATION_DESC allocDescUpload = {};
967     allocDescUpload.HeapType = D3D12_HEAP_TYPE_UPLOAD;
968     D3D12MA::ALLOCATION_DESC allocDescDefault = {};
969     allocDescDefault.HeapType = D3D12_HEAP_TYPE_DEFAULT;
970     D3D12MA::ALLOCATION_DESC allocDescReadback = {};
971     allocDescReadback.HeapType = D3D12_HEAP_TYPE_READBACK;
972 
973     D3D12_RESOURCE_DESC resourceDesc;
974     FillResourceDescForBuffer(resourceDesc, bufSize);
975 
976     // Create 3 sets of resources.
977     for(UINT i = 0; i < count; ++i)
978     {
979         D3D12MA::Allocation* alloc = nullptr;
980         CHECK_HR( ctx.allocator->CreateResource(
981             &allocDescUpload,
982             &resourceDesc,
983             D3D12_RESOURCE_STATE_GENERIC_READ,
984             NULL,
985             &alloc,
986             IID_PPV_ARGS(&resourcesUpload[i].resource)) );
987         resourcesUpload[i].allocation.reset(alloc);
988 
989         CHECK_HR( ctx.allocator->CreateResource(
990             &allocDescDefault,
991             &resourceDesc,
992             D3D12_RESOURCE_STATE_COPY_DEST,
993             NULL,
994             &alloc,
995             IID_PPV_ARGS(&resourcesDefault[i].resource)) );
996         resourcesDefault[i].allocation.reset(alloc);
997 
998         CHECK_HR( ctx.allocator->CreateResource(
999             &allocDescReadback,
1000             &resourceDesc,
1001             D3D12_RESOURCE_STATE_COPY_DEST,
1002             NULL,
1003             &alloc,
1004             IID_PPV_ARGS(&resourcesReadback[i].resource)) );
1005         resourcesReadback[i].allocation.reset(alloc);
1006     }
1007 
1008     // Map and fill data in UPLOAD.
1009     for(UINT i = 0; i < count; ++i)
1010     {
1011         void* mappedPtr = nullptr;
1012         CHECK_HR( resourcesUpload[i].resource->Map(0, &EMPTY_RANGE, &mappedPtr) );
1013 
1014         FillData(mappedPtr, bufSize, i);
1015 
1016         // Unmap every other resource, leave others mapped.
1017         if((i % 2) != 0)
1018         {
1019             resourcesUpload[i].resource->Unmap(0, NULL);
1020         }
1021     }
1022 
1023     // Transfer from UPLOAD to DEFAULT, from there to READBACK.
1024     ID3D12GraphicsCommandList* cmdList = BeginCommandList();
1025     for(UINT i = 0; i < count; ++i)
1026     {
1027         cmdList->CopyBufferRegion(resourcesDefault[i].resource, 0, resourcesUpload[i].resource, 0, bufSize);
1028     }
1029     D3D12_RESOURCE_BARRIER barriers[count] = {};
1030     for(UINT i = 0; i < count; ++i)
1031     {
1032         barriers[i].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1033         barriers[i].Transition.pResource = resourcesDefault[i].resource;
1034         barriers[i].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
1035         barriers[i].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
1036         barriers[i].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1037     }
1038     cmdList->ResourceBarrier(count, barriers);
1039     for(UINT i = 0; i < count; ++i)
1040     {
1041         cmdList->CopyBufferRegion(resourcesReadback[i].resource, 0, resourcesDefault[i].resource, 0, bufSize);
1042     }
1043     EndCommandList(cmdList);
1044 
1045     // Validate READBACK buffers.
1046     for(UINT i = count; i--; )
1047     {
1048         const D3D12_RANGE mapRange = {0, bufSize};
1049         void* mappedPtr = nullptr;
1050         CHECK_HR( resourcesReadback[i].resource->Map(0, &mapRange, &mappedPtr) );
1051 
1052         CHECK_BOOL( ValidateData(mappedPtr, bufSize, i) );
1053 
1054         // Unmap every 3rd resource, leave others mapped.
1055         if((i % 3) != 0)
1056         {
1057             resourcesReadback[i].resource->Unmap(0, &EMPTY_RANGE);
1058         }
1059     }
1060 }
1061 
TestZeroInitialized(const TestContext & ctx)1062 static void TestZeroInitialized(const TestContext& ctx)
1063 {
1064     wprintf(L"Test zero initialized\n");
1065 
1066     const UINT64 bufSize = 128ull * 1024;
1067     D3D12MA::Allocation* alloc = nullptr;
1068 
1069     D3D12_RESOURCE_DESC resourceDesc;
1070     FillResourceDescForBuffer(resourceDesc, bufSize);
1071 
1072     // # Create upload buffer and fill it with data.
1073 
1074     D3D12MA::ALLOCATION_DESC allocDescUpload = {};
1075     allocDescUpload.HeapType = D3D12_HEAP_TYPE_UPLOAD;
1076 
1077     ResourceWithAllocation bufUpload;
1078     CHECK_HR( ctx.allocator->CreateResource(
1079         &allocDescUpload,
1080         &resourceDesc,
1081         D3D12_RESOURCE_STATE_GENERIC_READ,
1082         NULL,
1083         &alloc,
1084         IID_PPV_ARGS(&bufUpload.resource)) );
1085     bufUpload.allocation.reset(alloc);
1086 
1087     {
1088         void* mappedPtr = nullptr;
1089         CHECK_HR( bufUpload.resource->Map(0, &EMPTY_RANGE, &mappedPtr) );
1090         FillData(mappedPtr, bufSize, 5236245);
1091         bufUpload.resource->Unmap(0, NULL);
1092     }
1093 
1094     // # Create readback buffer
1095 
1096     D3D12MA::ALLOCATION_DESC allocDescReadback = {};
1097     allocDescReadback.HeapType = D3D12_HEAP_TYPE_READBACK;
1098 
1099     ResourceWithAllocation bufReadback;
1100     CHECK_HR( ctx.allocator->CreateResource(
1101         &allocDescReadback,
1102         &resourceDesc,
1103         D3D12_RESOURCE_STATE_COPY_DEST,
1104         NULL,
1105         &alloc,
1106         IID_PPV_ARGS(&bufReadback.resource)) );
1107     bufReadback.allocation.reset(alloc);
1108 
1109     auto CheckBufferData = [&](const ResourceWithAllocation& buf)
1110     {
1111         const bool shouldBeZero = buf.allocation->WasZeroInitialized() != FALSE;
1112 
1113         {
1114             ID3D12GraphicsCommandList* cmdList = BeginCommandList();
1115             cmdList->CopyBufferRegion(bufReadback.resource, 0, buf.resource, 0, bufSize);
1116             EndCommandList(cmdList);
1117         }
1118 
1119         bool isZero = false;
1120         {
1121             const D3D12_RANGE readRange{0, bufSize}; // I could pass pReadRange = NULL but it generates D3D Debug layer warning: EXECUTION WARNING #930: MAP_INVALID_NULLRANGE
1122             void* mappedPtr = nullptr;
1123             CHECK_HR( bufReadback.resource->Map(0, &readRange, &mappedPtr) );
1124             isZero = ValidateDataZero(mappedPtr, bufSize);
1125             bufReadback.resource->Unmap(0, &EMPTY_RANGE);
1126         }
1127 
1128         wprintf(L"Should be zero: %u, is zero: %u\n", shouldBeZero ? 1 : 0, isZero ? 1 : 0);
1129 
1130         if(shouldBeZero)
1131         {
1132             CHECK_BOOL(isZero);
1133         }
1134     };
1135 
1136     // # Test 1: Committed resource. Should always be zero initialized.
1137 
1138     {
1139         D3D12MA::ALLOCATION_DESC allocDescDefault = {};
1140         allocDescDefault.HeapType = D3D12_HEAP_TYPE_DEFAULT;
1141         allocDescDefault.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;
1142 
1143         ResourceWithAllocation bufDefault;
1144         CHECK_HR( ctx.allocator->CreateResource(
1145             &allocDescDefault,
1146             &resourceDesc,
1147             D3D12_RESOURCE_STATE_COPY_SOURCE,
1148             NULL,
1149             &alloc,
1150             IID_PPV_ARGS(&bufDefault.resource)) );
1151         bufDefault.allocation.reset(alloc);
1152 
1153         wprintf(L"  Committed: ");
1154         CheckBufferData(bufDefault);
1155         CHECK_BOOL( bufDefault.allocation->WasZeroInitialized() );
1156     }
1157 
1158     // # Test 2: (Probably) placed resource.
1159 
1160     ResourceWithAllocation bufDefault;
1161     for(uint32_t i = 0; i < 2; ++i)
1162     {
1163         // 1. Create buffer
1164 
1165         D3D12MA::ALLOCATION_DESC allocDescDefault = {};
1166         allocDescDefault.HeapType = D3D12_HEAP_TYPE_DEFAULT;
1167 
1168         CHECK_HR( ctx.allocator->CreateResource(
1169             &allocDescDefault,
1170             &resourceDesc,
1171             D3D12_RESOURCE_STATE_COPY_SOURCE,
1172             NULL,
1173             &alloc,
1174             IID_PPV_ARGS(&bufDefault.resource)) );
1175         bufDefault.allocation.reset(alloc);
1176 
1177         // 2. Check it
1178 
1179         wprintf(L"  Normal #%u: ", i);
1180         CheckBufferData(bufDefault);
1181 
1182         // 3. Upload some data to it
1183 
1184         {
1185             ID3D12GraphicsCommandList* cmdList = BeginCommandList();
1186 
1187             D3D12_RESOURCE_BARRIER barrier = {};
1188             barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
1189             barrier.Transition.pResource = bufDefault.resource;
1190             barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
1191             barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
1192             barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
1193             cmdList->ResourceBarrier(1, &barrier);
1194 
1195             cmdList->CopyBufferRegion(bufDefault.resource, 0, bufUpload.resource, 0, bufSize);
1196 
1197             EndCommandList(cmdList);
1198         }
1199 
1200         // 4. Delete it
1201 
1202         bufDefault.Reset();
1203     }
1204 }
1205 
TestMultithreading(const TestContext & ctx)1206 static void TestMultithreading(const TestContext& ctx)
1207 {
1208     wprintf(L"Test multithreading\n");
1209 
1210     const UINT threadCount = 32;
1211     const UINT bufSizeMin = 1024ull;
1212     const UINT bufSizeMax = 1024ull * 1024;
1213 
1214     D3D12MA::ALLOCATION_DESC allocDesc = {};
1215     allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
1216 
1217     // Launch threads.
1218     std::thread threads[threadCount];
1219     for(UINT threadIndex = 0; threadIndex < threadCount; ++threadIndex)
1220     {
1221         auto threadFunc = [&, threadIndex]()
1222         {
1223             RandomNumberGenerator rand(threadIndex);
1224 
1225             std::vector<ResourceWithAllocation> resources;
1226             resources.reserve(256);
1227 
1228             // Create starting number of buffers.
1229             const UINT bufToCreateCount = 32;
1230             for(UINT bufIndex = 0; bufIndex < bufToCreateCount; ++bufIndex)
1231             {
1232                 ResourceWithAllocation res = {};
1233                 res.dataSeed = (threadIndex << 16) | bufIndex;
1234                 res.size = AlignUp<UINT>(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 16);
1235 
1236                 D3D12_RESOURCE_DESC resourceDesc;
1237                 FillResourceDescForBuffer(resourceDesc, res.size);
1238 
1239                 D3D12MA::Allocation* alloc = nullptr;
1240                 CHECK_HR( ctx.allocator->CreateResource(
1241                     &allocDesc,
1242                     &resourceDesc,
1243                     D3D12_RESOURCE_STATE_GENERIC_READ,
1244                     NULL,
1245                     &alloc,
1246                     IID_PPV_ARGS(&res.resource)) );
1247                 res.allocation.reset(alloc);
1248 
1249                 void* mappedPtr = nullptr;
1250                 CHECK_HR( res.resource->Map(0, &EMPTY_RANGE, &mappedPtr) );
1251 
1252                 FillData(mappedPtr, res.size, res.dataSeed);
1253 
1254                 // Unmap some of them, leave others mapped.
1255                 if(rand.GenerateBool())
1256                 {
1257                     res.resource->Unmap(0, NULL);
1258                 }
1259 
1260                 resources.push_back(std::move(res));
1261             }
1262 
1263             Sleep(20);
1264 
1265             // Make a number of random allocate and free operations.
1266             const UINT operationCount = 128;
1267             for(UINT operationIndex = 0; operationIndex < operationCount; ++operationIndex)
1268             {
1269                 const bool removePossible = !resources.empty();
1270                 const bool remove = removePossible && rand.GenerateBool();
1271                 if(remove)
1272                 {
1273                     const UINT indexToRemove = rand.Generate() % resources.size();
1274                     resources.erase(resources.begin() + indexToRemove);
1275                 }
1276                 else // Create new buffer.
1277                 {
1278                     ResourceWithAllocation res = {};
1279                     res.dataSeed = (threadIndex << 16) | operationIndex;
1280                     res.size = AlignUp<UINT>(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 16);
1281                     D3D12_RESOURCE_DESC resourceDesc;
1282                     FillResourceDescForBuffer(resourceDesc, res.size);
1283 
1284                     D3D12MA::Allocation* alloc = nullptr;
1285                     CHECK_HR( ctx.allocator->CreateResource(
1286                         &allocDesc,
1287                         &resourceDesc,
1288                         D3D12_RESOURCE_STATE_GENERIC_READ,
1289                         NULL,
1290                         &alloc,
1291                         IID_PPV_ARGS(&res.resource)) );
1292                     res.allocation.reset(alloc);
1293 
1294                     void* mappedPtr = nullptr;
1295                     CHECK_HR( res.resource->Map(0, NULL, &mappedPtr) );
1296 
1297                     FillData(mappedPtr, res.size, res.dataSeed);
1298 
1299                     // Unmap some of them, leave others mapped.
1300                     if(rand.GenerateBool())
1301                     {
1302                         res.resource->Unmap(0, NULL);
1303                     }
1304 
1305                     resources.push_back(std::move(res));
1306                 }
1307             }
1308 
1309             Sleep(20);
1310 
1311             // Validate data in all remaining buffers while deleting them.
1312             for(size_t resIndex = resources.size(); resIndex--; )
1313             {
1314                 void* mappedPtr = nullptr;
1315                 CHECK_HR( resources[resIndex].resource->Map(0, NULL, &mappedPtr) );
1316 
1317                 ValidateData(mappedPtr, resources[resIndex].size, resources[resIndex].dataSeed);
1318 
1319                 // Unmap some of them, leave others mapped.
1320                 if((resIndex % 3) == 1)
1321                 {
1322                     resources[resIndex].resource->Unmap(0, &EMPTY_RANGE);
1323                 }
1324 
1325                 resources.pop_back();
1326             }
1327         };
1328         threads[threadIndex] = std::thread(threadFunc);
1329     }
1330 
1331     // Wait for threads to finish.
1332     for(UINT threadIndex = threadCount; threadIndex--; )
1333     {
1334         threads[threadIndex].join();
1335     }
1336 }
1337 
TestGroupVirtual(const TestContext & ctx)1338 static void TestGroupVirtual(const TestContext& ctx)
1339 {
1340     TestVirtualBlocks(ctx);
1341 }
1342 
TestGroupBasics(const TestContext & ctx)1343 static void TestGroupBasics(const TestContext& ctx)
1344 {
1345     TestFrameIndexAndJson(ctx);
1346     TestCommittedResourcesAndJson(ctx);
1347     TestCustomHeapFlags(ctx);
1348     TestPlacedResources(ctx);
1349     TestOtherComInterface(ctx);
1350     TestCustomPools(ctx);
1351     TestDefaultPoolMinBytes(ctx);
1352     TestAliasingMemory(ctx);
1353     TestMapping(ctx);
1354     TestStats(ctx);
1355     TestTransfer(ctx);
1356     TestZeroInitialized(ctx);
1357     TestMultithreading(ctx);
1358 }
1359 
Test(const TestContext & ctx)1360 void Test(const TestContext& ctx)
1361 {
1362     wprintf(L"TESTS BEGIN\n");
1363 
1364     if(false)
1365     {
1366         ////////////////////////////////////////////////////////////////////////////////
1367         // Temporarily insert custom tests here:
1368         return;
1369     }
1370 
1371     TestGroupVirtual(ctx);
1372     TestGroupBasics(ctx);
1373 
1374     wprintf(L"TESTS END\n");
1375 }
1376