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