blob: 970c6adf415fec8e39d8f0f0dc244d7badb4a7a6 [file] [log] [blame]
Adam Sawickif1a793c2018-03-13 15:42:22 +01001#include "Tests.h"
2#include "VmaUsage.h"
3#include "Common.h"
Adam Sawickib8333fb2018-03-13 16:15:53 +01004#include <atomic>
5#include <thread>
6#include <mutex>
Adam Sawickif1a793c2018-03-13 15:42:22 +01007
8#ifdef _WIN32
9
Adam Sawickib8333fb2018-03-13 16:15:53 +010010enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
11
12struct AllocationSize
13{
14 uint32_t Probability;
15 VkDeviceSize BufferSizeMin, BufferSizeMax;
16 uint32_t ImageSizeMin, ImageSizeMax;
17};
18
19struct Config
20{
21 uint32_t RandSeed;
22 VkDeviceSize BeginBytesToAllocate;
23 uint32_t AdditionalOperationCount;
24 VkDeviceSize MaxBytesToAllocate;
25 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
26 std::vector<AllocationSize> AllocationSizes;
27 uint32_t ThreadCount;
28 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
29 FREE_ORDER FreeOrder;
30};
31
32struct Result
33{
34 duration TotalTime;
35 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
36 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
37 VkDeviceSize TotalMemoryAllocated;
38 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
39};
40
41void TestDefragmentationSimple();
42void TestDefragmentationFull();
43
44struct PoolTestConfig
45{
46 uint32_t RandSeed;
47 uint32_t ThreadCount;
48 VkDeviceSize PoolSize;
49 uint32_t FrameCount;
50 uint32_t TotalItemCount;
51 // Range for number of items used in each frame.
52 uint32_t UsedItemCountMin, UsedItemCountMax;
53 // Percent of items to make unused, and possibly make some others used in each frame.
54 uint32_t ItemsToMakeUnusedPercent;
55 std::vector<AllocationSize> AllocationSizes;
56
57 VkDeviceSize CalcAvgResourceSize() const
58 {
59 uint32_t probabilitySum = 0;
60 VkDeviceSize sizeSum = 0;
61 for(size_t i = 0; i < AllocationSizes.size(); ++i)
62 {
63 const AllocationSize& allocSize = AllocationSizes[i];
64 if(allocSize.BufferSizeMax > 0)
65 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
66 else
67 {
68 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
69 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
70 }
71 probabilitySum += allocSize.Probability;
72 }
73 return sizeSum / probabilitySum;
74 }
75
76 bool UsesBuffers() const
77 {
78 for(size_t i = 0; i < AllocationSizes.size(); ++i)
79 if(AllocationSizes[i].BufferSizeMax > 0)
80 return true;
81 return false;
82 }
83
84 bool UsesImages() const
85 {
86 for(size_t i = 0; i < AllocationSizes.size(); ++i)
87 if(AllocationSizes[i].ImageSizeMax > 0)
88 return true;
89 return false;
90 }
91};
92
93struct PoolTestResult
94{
95 duration TotalTime;
96 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
97 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
98 size_t LostAllocationCount, LostAllocationTotalSize;
99 size_t FailedAllocationCount, FailedAllocationTotalSize;
100};
101
102static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
103
104struct BufferInfo
105{
106 VkBuffer Buffer = VK_NULL_HANDLE;
107 VmaAllocation Allocation = VK_NULL_HANDLE;
108};
109
110static void InitResult(Result& outResult)
111{
112 outResult.TotalTime = duration::zero();
113 outResult.AllocationTimeMin = duration::max();
114 outResult.AllocationTimeAvg = duration::zero();
115 outResult.AllocationTimeMax = duration::min();
116 outResult.DeallocationTimeMin = duration::max();
117 outResult.DeallocationTimeAvg = duration::zero();
118 outResult.DeallocationTimeMax = duration::min();
119 outResult.TotalMemoryAllocated = 0;
120 outResult.FreeRangeSizeAvg = 0;
121 outResult.FreeRangeSizeMax = 0;
122}
123
124class TimeRegisterObj
125{
126public:
127 TimeRegisterObj(duration& min, duration& sum, duration& max) :
128 m_Min(min),
129 m_Sum(sum),
130 m_Max(max),
131 m_TimeBeg(std::chrono::high_resolution_clock::now())
132 {
133 }
134
135 ~TimeRegisterObj()
136 {
137 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
138 m_Sum += d;
139 if(d < m_Min) m_Min = d;
140 if(d > m_Max) m_Max = d;
141 }
142
143private:
144 duration& m_Min;
145 duration& m_Sum;
146 duration& m_Max;
147 time_point m_TimeBeg;
148};
149
150struct PoolTestThreadResult
151{
152 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
153 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
154 size_t AllocationCount, DeallocationCount;
155 size_t LostAllocationCount, LostAllocationTotalSize;
156 size_t FailedAllocationCount, FailedAllocationTotalSize;
157};
158
159class AllocationTimeRegisterObj : public TimeRegisterObj
160{
161public:
162 AllocationTimeRegisterObj(Result& result) :
163 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
164 {
165 }
166};
167
168class DeallocationTimeRegisterObj : public TimeRegisterObj
169{
170public:
171 DeallocationTimeRegisterObj(Result& result) :
172 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
173 {
174 }
175};
176
177class PoolAllocationTimeRegisterObj : public TimeRegisterObj
178{
179public:
180 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
181 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
182 {
183 }
184};
185
186class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
187{
188public:
189 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
190 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
191 {
192 }
193};
194
195VkResult MainTest(Result& outResult, const Config& config)
196{
197 assert(config.ThreadCount > 0);
198
199 InitResult(outResult);
200
201 RandomNumberGenerator mainRand{config.RandSeed};
202
203 time_point timeBeg = std::chrono::high_resolution_clock::now();
204
205 std::atomic<size_t> allocationCount = 0;
206 VkResult res = VK_SUCCESS;
207
208 uint32_t memUsageProbabilitySum =
209 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
210 config.MemUsageProbability[2] + config.MemUsageProbability[3];
211 assert(memUsageProbabilitySum > 0);
212
213 uint32_t allocationSizeProbabilitySum = std::accumulate(
214 config.AllocationSizes.begin(),
215 config.AllocationSizes.end(),
216 0u,
217 [](uint32_t sum, const AllocationSize& allocSize) {
218 return sum + allocSize.Probability;
219 });
220
221 struct Allocation
222 {
223 VkBuffer Buffer;
224 VkImage Image;
225 VmaAllocation Alloc;
226 };
227
228 std::vector<Allocation> commonAllocations;
229 std::mutex commonAllocationsMutex;
230
231 auto Allocate = [&](
232 VkDeviceSize bufferSize,
233 const VkExtent2D imageExtent,
234 RandomNumberGenerator& localRand,
235 VkDeviceSize& totalAllocatedBytes,
236 std::vector<Allocation>& allocations) -> VkResult
237 {
238 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
239
240 uint32_t memUsageIndex = 0;
241 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
242 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
243 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
244
245 VmaAllocationCreateInfo memReq = {};
246 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
247
248 Allocation allocation = {};
249 VmaAllocationInfo allocationInfo;
250
251 // Buffer
252 if(bufferSize > 0)
253 {
254 assert(imageExtent.width == 0);
255 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
256 bufferInfo.size = bufferSize;
257 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
258
259 {
260 AllocationTimeRegisterObj timeRegisterObj{outResult};
261 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
262 }
263 }
264 // Image
265 else
266 {
267 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
268 imageInfo.imageType = VK_IMAGE_TYPE_2D;
269 imageInfo.extent.width = imageExtent.width;
270 imageInfo.extent.height = imageExtent.height;
271 imageInfo.extent.depth = 1;
272 imageInfo.mipLevels = 1;
273 imageInfo.arrayLayers = 1;
274 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
275 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
276 VK_IMAGE_TILING_OPTIMAL :
277 VK_IMAGE_TILING_LINEAR;
278 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
279 switch(memReq.usage)
280 {
281 case VMA_MEMORY_USAGE_GPU_ONLY:
282 switch(localRand.Generate() % 3)
283 {
284 case 0:
285 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
286 break;
287 case 1:
288 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
289 break;
290 case 2:
291 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
292 break;
293 }
294 break;
295 case VMA_MEMORY_USAGE_CPU_ONLY:
296 case VMA_MEMORY_USAGE_CPU_TO_GPU:
297 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
298 break;
299 case VMA_MEMORY_USAGE_GPU_TO_CPU:
300 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
301 break;
302 }
303 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
304 imageInfo.flags = 0;
305
306 {
307 AllocationTimeRegisterObj timeRegisterObj{outResult};
308 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
309 }
310 }
311
312 if(res == VK_SUCCESS)
313 {
314 ++allocationCount;
315 totalAllocatedBytes += allocationInfo.size;
316 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
317 if(useCommonAllocations)
318 {
319 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
320 commonAllocations.push_back(allocation);
321 }
322 else
323 allocations.push_back(allocation);
324 }
325 else
326 {
327 assert(0);
328 }
329 return res;
330 };
331
332 auto GetNextAllocationSize = [&](
333 VkDeviceSize& outBufSize,
334 VkExtent2D& outImageSize,
335 RandomNumberGenerator& localRand)
336 {
337 outBufSize = 0;
338 outImageSize = {0, 0};
339
340 uint32_t allocSizeIndex = 0;
341 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
342 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
343 r -= config.AllocationSizes[allocSizeIndex++].Probability;
344
345 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
346 if(allocSize.BufferSizeMax > 0)
347 {
348 assert(allocSize.ImageSizeMax == 0);
349 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
350 outBufSize = allocSize.BufferSizeMin;
351 else
352 {
353 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
354 outBufSize = outBufSize / 16 * 16;
355 }
356 }
357 else
358 {
359 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
360 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
361 else
362 {
363 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
364 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
365 }
366 }
367 };
368
369 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
370 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
371
372 auto ThreadProc = [&](uint32_t randSeed) -> void
373 {
374 RandomNumberGenerator threadRand(randSeed);
375 VkDeviceSize threadTotalAllocatedBytes = 0;
376 std::vector<Allocation> threadAllocations;
377 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
378 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
379 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
380
381 // BEGIN ALLOCATIONS
382 for(;;)
383 {
384 VkDeviceSize bufferSize = 0;
385 VkExtent2D imageExtent = {};
386 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
387 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
388 threadBeginBytesToAllocate)
389 {
390 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
391 break;
392 }
393 else
394 break;
395 }
396
397 // ADDITIONAL ALLOCATIONS AND FREES
398 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
399 {
400 VkDeviceSize bufferSize = 0;
401 VkExtent2D imageExtent = {};
402 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
403
404 // true = allocate, false = free
405 bool allocate = threadRand.Generate() % 2 != 0;
406
407 if(allocate)
408 {
409 if(threadTotalAllocatedBytes +
410 bufferSize +
411 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
412 threadMaxBytesToAllocate)
413 {
414 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
415 break;
416 }
417 }
418 else
419 {
420 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
421 if(useCommonAllocations)
422 {
423 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
424 if(!commonAllocations.empty())
425 {
426 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
427 VmaAllocationInfo allocationInfo;
428 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
429 if(threadTotalAllocatedBytes >= allocationInfo.size)
430 {
431 DeallocationTimeRegisterObj timeRegisterObj{outResult};
432 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
433 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
434 else
435 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
436 threadTotalAllocatedBytes -= allocationInfo.size;
437 commonAllocations.erase(commonAllocations.begin() + indexToFree);
438 }
439 }
440 }
441 else
442 {
443 if(!threadAllocations.empty())
444 {
445 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
446 VmaAllocationInfo allocationInfo;
447 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
448 if(threadTotalAllocatedBytes >= allocationInfo.size)
449 {
450 DeallocationTimeRegisterObj timeRegisterObj{outResult};
451 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
452 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
453 else
454 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
455 threadTotalAllocatedBytes -= allocationInfo.size;
456 threadAllocations.erase(threadAllocations.begin() + indexToFree);
457 }
458 }
459 }
460 }
461 }
462
463 ++numThreadsReachedMaxAllocations;
464
465 WaitForSingleObject(threadsFinishEvent, INFINITE);
466
467 // DEALLOCATION
468 while(!threadAllocations.empty())
469 {
470 size_t indexToFree = 0;
471 switch(config.FreeOrder)
472 {
473 case FREE_ORDER::FORWARD:
474 indexToFree = 0;
475 break;
476 case FREE_ORDER::BACKWARD:
477 indexToFree = threadAllocations.size() - 1;
478 break;
479 case FREE_ORDER::RANDOM:
480 indexToFree = mainRand.Generate() % threadAllocations.size();
481 break;
482 }
483
484 {
485 DeallocationTimeRegisterObj timeRegisterObj{outResult};
486 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
487 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
488 else
489 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
490 }
491 threadAllocations.erase(threadAllocations.begin() + indexToFree);
492 }
493 };
494
495 uint32_t threadRandSeed = mainRand.Generate();
496 std::vector<std::thread> bkgThreads;
497 for(size_t i = 0; i < config.ThreadCount; ++i)
498 {
499 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
500 }
501
502 // Wait for threads reached max allocations
503 while(numThreadsReachedMaxAllocations < config.ThreadCount)
504 Sleep(0);
505
506 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
507 VmaStats vmaStats = {};
508 vmaCalculateStats(g_hAllocator, &vmaStats);
509 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
510 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
511 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
512
513 // Signal threads to deallocate
514 SetEvent(threadsFinishEvent);
515
516 // Wait for threads finished
517 for(size_t i = 0; i < bkgThreads.size(); ++i)
518 bkgThreads[i].join();
519 bkgThreads.clear();
520
521 CloseHandle(threadsFinishEvent);
522
523 // Deallocate remaining common resources
524 while(!commonAllocations.empty())
525 {
526 size_t indexToFree = 0;
527 switch(config.FreeOrder)
528 {
529 case FREE_ORDER::FORWARD:
530 indexToFree = 0;
531 break;
532 case FREE_ORDER::BACKWARD:
533 indexToFree = commonAllocations.size() - 1;
534 break;
535 case FREE_ORDER::RANDOM:
536 indexToFree = mainRand.Generate() % commonAllocations.size();
537 break;
538 }
539
540 {
541 DeallocationTimeRegisterObj timeRegisterObj{outResult};
542 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
543 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
544 else
545 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
546 }
547 commonAllocations.erase(commonAllocations.begin() + indexToFree);
548 }
549
550 if(allocationCount)
551 {
552 outResult.AllocationTimeAvg /= allocationCount;
553 outResult.DeallocationTimeAvg /= allocationCount;
554 }
555
556 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
557
558 return res;
559}
560
561static void SaveAllocatorStatsToFile(VmaAllocator alloc, const wchar_t* filePath)
562{
563 char* stats;
564 vmaBuildStatsString(alloc, &stats, VK_TRUE);
565 SaveFile(filePath, stats, strlen(stats));
566 vmaFreeStatsString(alloc, stats);
567}
568
569struct AllocInfo
570{
571 VmaAllocation m_Allocation;
572 VkBuffer m_Buffer;
573 VkImage m_Image;
574 uint32_t m_StartValue;
575 union
576 {
577 VkBufferCreateInfo m_BufferInfo;
578 VkImageCreateInfo m_ImageInfo;
579 };
580};
581
582static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
583{
584 outMemReq = {};
585 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
586 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
587}
588
589static void CreateBuffer(
590 VmaPool pool,
591 const VkBufferCreateInfo& bufCreateInfo,
592 bool persistentlyMapped,
593 AllocInfo& outAllocInfo)
594{
595 outAllocInfo = {};
596 outAllocInfo.m_BufferInfo = bufCreateInfo;
597
598 VmaAllocationCreateInfo allocCreateInfo = {};
599 allocCreateInfo.pool = pool;
600 if(persistentlyMapped)
601 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
602
603 VmaAllocationInfo vmaAllocInfo = {};
604 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
605
606 // Setup StartValue and fill.
607 {
608 outAllocInfo.m_StartValue = (uint32_t)rand();
609 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
610 assert((data != nullptr) == persistentlyMapped);
611 if(!persistentlyMapped)
612 {
613 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
614 }
615
616 uint32_t value = outAllocInfo.m_StartValue;
617 assert(bufCreateInfo.size % 4 == 0);
618 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
619 data[i] = value++;
620
621 if(!persistentlyMapped)
622 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
623 }
624}
625
626static void CreateAllocation(AllocInfo& outAllocation, VmaAllocator allocator)
627{
628 outAllocation.m_Allocation = nullptr;
629 outAllocation.m_Buffer = nullptr;
630 outAllocation.m_Image = nullptr;
631 outAllocation.m_StartValue = (uint32_t)rand();
632
633 VmaAllocationCreateInfo vmaMemReq;
634 GetMemReq(vmaMemReq);
635
636 VmaAllocationInfo allocInfo;
637
638 const bool isBuffer = true;//(rand() & 0x1) != 0;
639 const bool isLarge = (rand() % 16) == 0;
640 if(isBuffer)
641 {
642 const uint32_t bufferSize = isLarge ?
643 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
644 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
645
646 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
647 bufferInfo.size = bufferSize;
648 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
649
650 VkResult res = vmaCreateBuffer(allocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
651 outAllocation.m_BufferInfo = bufferInfo;
652 assert(res == VK_SUCCESS);
653 }
654 else
655 {
656 const uint32_t imageSizeX = isLarge ?
657 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
658 rand() % 1024 + 1; // 1 ... 1024
659 const uint32_t imageSizeY = isLarge ?
660 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
661 rand() % 1024 + 1; // 1 ... 1024
662
663 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
664 imageInfo.imageType = VK_IMAGE_TYPE_2D;
665 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
666 imageInfo.extent.width = imageSizeX;
667 imageInfo.extent.height = imageSizeY;
668 imageInfo.extent.depth = 1;
669 imageInfo.mipLevels = 1;
670 imageInfo.arrayLayers = 1;
671 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
672 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
673 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
674 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
675
676 VkResult res = vmaCreateImage(allocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
677 outAllocation.m_ImageInfo = imageInfo;
678 assert(res == VK_SUCCESS);
679 }
680
681 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
682 if(allocInfo.pMappedData == nullptr)
683 {
684 VkResult res = vmaMapMemory(allocator, outAllocation.m_Allocation, (void**)&data);
685 assert(res == VK_SUCCESS);
686 }
687
688 uint32_t value = outAllocation.m_StartValue;
689 assert(allocInfo.size % 4 == 0);
690 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
691 data[i] = value++;
692
693 if(allocInfo.pMappedData == nullptr)
694 vmaUnmapMemory(allocator, outAllocation.m_Allocation);
695}
696
697static void DestroyAllocation(const AllocInfo& allocation)
698{
699 if(allocation.m_Buffer)
700 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
701 else
702 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
703}
704
705static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
706{
707 for(size_t i = allocations.size(); i--; )
708 DestroyAllocation(allocations[i]);
709 allocations.clear();
710}
711
712static void ValidateAllocationData(const AllocInfo& allocation)
713{
714 VmaAllocationInfo allocInfo;
715 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
716
717 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
718 if(allocInfo.pMappedData == nullptr)
719 {
720 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
721 assert(res == VK_SUCCESS);
722 }
723
724 uint32_t value = allocation.m_StartValue;
725 bool ok = true;
726 size_t i;
727 assert(allocInfo.size % 4 == 0);
728 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
729 {
730 if(data[i] != value++)
731 {
732 ok = false;
733 break;
734 }
735 }
736 assert(ok);
737
738 if(allocInfo.pMappedData == nullptr)
739 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
740}
741
742static void RecreateAllocationResource(AllocInfo& allocation)
743{
744 VmaAllocationInfo allocInfo;
745 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
746
747 if(allocation.m_Buffer)
748 {
749 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, nullptr);
750
751 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, nullptr, &allocation.m_Buffer);
752 assert(res == VK_SUCCESS);
753
754 // Just to silence validation layer warnings.
755 VkMemoryRequirements vkMemReq;
756 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
757 assert(vkMemReq.size == allocation.m_BufferInfo.size);
758
759 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
760 assert(res == VK_SUCCESS);
761 }
762 else
763 {
764 vkDestroyImage(g_hDevice, allocation.m_Image, nullptr);
765
766 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, nullptr, &allocation.m_Image);
767 assert(res == VK_SUCCESS);
768
769 // Just to silence validation layer warnings.
770 VkMemoryRequirements vkMemReq;
771 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
772
773 res = vkBindImageMemory(g_hDevice, allocation.m_Image, allocInfo.deviceMemory, allocInfo.offset);
774 assert(res == VK_SUCCESS);
775 }
776}
777
778static void Defragment(AllocInfo* allocs, size_t allocCount,
779 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
780 VmaDefragmentationStats* defragmentationStats = nullptr)
781{
782 std::vector<VmaAllocation> vmaAllocs(allocCount);
783 for(size_t i = 0; i < allocCount; ++i)
784 vmaAllocs[i] = allocs[i].m_Allocation;
785
786 std::vector<VkBool32> allocChanged(allocCount);
787
788 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
789 defragmentationInfo, defragmentationStats) );
790
791 for(size_t i = 0; i < allocCount; ++i)
792 {
793 if(allocChanged[i])
794 {
795 RecreateAllocationResource(allocs[i]);
796 }
797 }
798}
799
800static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
801{
802 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
803 ValidateAllocationData(allocInfo);
804 });
805}
806
807void TestDefragmentationSimple()
808{
809 wprintf(L"Test defragmentation simple\n");
810
811 RandomNumberGenerator rand(667);
812
813 const VkDeviceSize BUF_SIZE = 0x10000;
814 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
815
816 const VkDeviceSize MIN_BUF_SIZE = 32;
817 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
818 auto RandomBufSize = [&]() -> VkDeviceSize {
819 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
820 };
821
822 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
823 bufCreateInfo.size = BUF_SIZE;
824 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
825
826 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
827 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
828
829 uint32_t memTypeIndex = UINT32_MAX;
830 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
831
832 VmaPoolCreateInfo poolCreateInfo = {};
833 poolCreateInfo.blockSize = BLOCK_SIZE;
834 poolCreateInfo.memoryTypeIndex = memTypeIndex;
835
836 VmaPool pool;
837 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
838
839 std::vector<AllocInfo> allocations;
840
841 // persistentlyMappedOption = 0 - not persistently mapped.
842 // persistentlyMappedOption = 1 - persistently mapped.
843 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
844 {
845 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
846 const bool persistentlyMapped = persistentlyMappedOption != 0;
847
848 // # Test 1
849 // Buffers of fixed size.
850 // Fill 2 blocks. Remove odd buffers. Defragment everything.
851 // Expected result: at least 1 block freed.
852 {
853 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
854 {
855 AllocInfo allocInfo;
856 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
857 allocations.push_back(allocInfo);
858 }
859
860 for(size_t i = 1; i < allocations.size(); ++i)
861 {
862 DestroyAllocation(allocations[i]);
863 allocations.erase(allocations.begin() + i);
864 }
865
866 VmaDefragmentationStats defragStats;
867 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
868 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
869 assert(defragStats.deviceMemoryBlocksFreed >= 1);
870
871 ValidateAllocationsData(allocations.data(), allocations.size());
872
873 DestroyAllAllocations(allocations);
874 }
875
876 // # Test 2
877 // Buffers of fixed size.
878 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
879 // Expected result: Each of 4 interations makes some progress.
880 {
881 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
882 {
883 AllocInfo allocInfo;
884 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
885 allocations.push_back(allocInfo);
886 }
887
888 for(size_t i = 1; i < allocations.size(); ++i)
889 {
890 DestroyAllocation(allocations[i]);
891 allocations.erase(allocations.begin() + i);
892 }
893
894 VmaDefragmentationInfo defragInfo = {};
895 defragInfo.maxAllocationsToMove = 1;
896 defragInfo.maxBytesToMove = BUF_SIZE;
897
898 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
899 {
900 VmaDefragmentationStats defragStats;
901 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
902 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
903 }
904
905 ValidateAllocationsData(allocations.data(), allocations.size());
906
907 DestroyAllAllocations(allocations);
908 }
909
910 // # Test 3
911 // Buffers of variable size.
912 // Create a number of buffers. Remove some percent of them.
913 // Defragment while having some percent of them unmovable.
914 // Expected result: Just simple validation.
915 {
916 for(size_t i = 0; i < 100; ++i)
917 {
918 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
919 localBufCreateInfo.size = RandomBufSize();
920
921 AllocInfo allocInfo;
922 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
923 allocations.push_back(allocInfo);
924 }
925
926 const uint32_t percentToDelete = 60;
927 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
928 for(size_t i = 0; i < numberToDelete; ++i)
929 {
930 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
931 DestroyAllocation(allocations[indexToDelete]);
932 allocations.erase(allocations.begin() + indexToDelete);
933 }
934
935 // Non-movable allocations will be at the beginning of allocations array.
936 const uint32_t percentNonMovable = 20;
937 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
938 for(size_t i = 0; i < numberNonMovable; ++i)
939 {
940 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
941 if(indexNonMovable != i)
942 std::swap(allocations[i], allocations[indexNonMovable]);
943 }
944
945 VmaDefragmentationStats defragStats;
946 Defragment(
947 allocations.data() + numberNonMovable,
948 allocations.size() - numberNonMovable,
949 nullptr, &defragStats);
950
951 ValidateAllocationsData(allocations.data(), allocations.size());
952
953 DestroyAllAllocations(allocations);
954 }
955 }
956
957 vmaDestroyPool(g_hAllocator, pool);
958}
959
960void TestDefragmentationFull()
961{
962 std::vector<AllocInfo> allocations;
963
964 // Create initial allocations.
965 for(size_t i = 0; i < 400; ++i)
966 {
967 AllocInfo allocation;
968 CreateAllocation(allocation, g_hAllocator);
969 allocations.push_back(allocation);
970 }
971
972 // Delete random allocations
973 const size_t allocationsToDeletePercent = 80;
974 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
975 for(size_t i = 0; i < allocationsToDelete; ++i)
976 {
977 size_t index = (size_t)rand() % allocations.size();
978 DestroyAllocation(allocations[index]);
979 allocations.erase(allocations.begin() + index);
980 }
981
982 for(size_t i = 0; i < allocations.size(); ++i)
983 ValidateAllocationData(allocations[i]);
984
985 SaveAllocatorStatsToFile(g_hAllocator, L"Before.csv");
986
987 {
988 std::vector<VmaAllocation> vmaAllocations(allocations.size());
989 for(size_t i = 0; i < allocations.size(); ++i)
990 vmaAllocations[i] = allocations[i].m_Allocation;
991
992 const size_t nonMovablePercent = 0;
993 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
994 for(size_t i = 0; i < nonMovableCount; ++i)
995 {
996 size_t index = (size_t)rand() % vmaAllocations.size();
997 vmaAllocations.erase(vmaAllocations.begin() + index);
998 }
999
1000 const uint32_t defragCount = 1;
1001 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1002 {
1003 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1004
1005 VmaDefragmentationInfo defragmentationInfo;
1006 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1007 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1008
1009 wprintf(L"Defragmentation #%u\n", defragIndex);
1010
1011 time_point begTime = std::chrono::high_resolution_clock::now();
1012
1013 VmaDefragmentationStats stats;
1014 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
1015 assert(res >= 0);
1016
1017 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1018
1019 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1020 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1021 wprintf(L"Time: %.2f s\n", defragmentDuration);
1022
1023 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1024 {
1025 if(allocationsChanged[i])
1026 {
1027 RecreateAllocationResource(allocations[i]);
1028 }
1029 }
1030
1031 for(size_t i = 0; i < allocations.size(); ++i)
1032 ValidateAllocationData(allocations[i]);
1033
1034 wchar_t fileName[MAX_PATH];
1035 swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1036 SaveAllocatorStatsToFile(g_hAllocator, fileName);
1037 }
1038 }
1039
1040 // Destroy all remaining allocations.
1041 DestroyAllAllocations(allocations);
1042}
1043
1044static void TestUserData()
1045{
1046 VkResult res;
1047
1048 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1049 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1050 bufCreateInfo.size = 0x10000;
1051
1052 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
1053 {
1054 // Opaque pointer
1055 {
1056
1057 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
1058 void* pointerToSomething = &res;
1059
1060 VmaAllocationCreateInfo allocCreateInfo = {};
1061 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1062 allocCreateInfo.pUserData = numberAsPointer;
1063 if(testIndex == 1)
1064 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1065
1066 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1067 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1068 assert(res == VK_SUCCESS);
1069 assert(allocInfo.pUserData = numberAsPointer);
1070
1071 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1072 assert(allocInfo.pUserData == numberAsPointer);
1073
1074 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1075 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1076 assert(allocInfo.pUserData == pointerToSomething);
1077
1078 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1079 }
1080
1081 // String
1082 {
1083 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
1084 const char* name2 = "2";
1085 const size_t name1Len = strlen(name1);
1086
1087 char* name1Buf = new char[name1Len + 1];
1088 strcpy_s(name1Buf, name1Len + 1, name1);
1089
1090 VmaAllocationCreateInfo allocCreateInfo = {};
1091 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1092 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1093 allocCreateInfo.pUserData = name1Buf;
1094 if(testIndex == 1)
1095 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1096
1097 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1098 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1099 assert(res == VK_SUCCESS);
1100 assert(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1101 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1102
1103 delete[] name1Buf;
1104
1105 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1106 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1107
1108 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1109 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1110 assert(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
1111
1112 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1113 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1114 assert(allocInfo.pUserData == nullptr);
1115
1116 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1117 }
1118 }
1119}
1120
1121static void TestMemoryRequirements()
1122{
1123 VkResult res;
1124 VkBuffer buf;
1125 VmaAllocation alloc;
1126 VmaAllocationInfo allocInfo;
1127
1128 const VkPhysicalDeviceMemoryProperties* memProps;
1129 vmaGetMemoryProperties(g_hAllocator, &memProps);
1130
1131 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1132 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1133 bufInfo.size = 128;
1134
1135 VmaAllocationCreateInfo allocCreateInfo = {};
1136
1137 // No requirements.
1138 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1139 assert(res == VK_SUCCESS);
1140 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1141
1142 // Usage.
1143 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1144 allocCreateInfo.requiredFlags = 0;
1145 allocCreateInfo.preferredFlags = 0;
1146 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1147
1148 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1149 assert(res == VK_SUCCESS);
1150 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1151 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1152
1153 // Required flags, preferred flags.
1154 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1155 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1156 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1157 allocCreateInfo.memoryTypeBits = 0;
1158
1159 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1160 assert(res == VK_SUCCESS);
1161 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1162 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1163 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1164
1165 // memoryTypeBits.
1166 const uint32_t memType = allocInfo.memoryType;
1167 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1168 allocCreateInfo.requiredFlags = 0;
1169 allocCreateInfo.preferredFlags = 0;
1170 allocCreateInfo.memoryTypeBits = 1u << memType;
1171
1172 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1173 assert(res == VK_SUCCESS);
1174 assert(allocInfo.memoryType == memType);
1175 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1176
1177}
1178
1179static void TestBasics()
1180{
1181 VkResult res;
1182
1183 TestMemoryRequirements();
1184
1185 // Lost allocation
1186 {
1187 VmaAllocation alloc = VK_NULL_HANDLE;
1188 vmaCreateLostAllocation(g_hAllocator, &alloc);
1189 assert(alloc != VK_NULL_HANDLE);
1190
1191 VmaAllocationInfo allocInfo;
1192 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1193 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1194 assert(allocInfo.size == 0);
1195
1196 vmaFreeMemory(g_hAllocator, alloc);
1197 }
1198
1199 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1200 {
1201 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1202 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1203 bufCreateInfo.size = 128;
1204
1205 VmaAllocationCreateInfo allocCreateInfo = {};
1206 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1207 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1208
1209 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1210 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1211 assert(res == VK_SUCCESS);
1212
1213 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1214
1215 // Same with OWN_MEMORY.
1216 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1217
1218 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1219 assert(res == VK_SUCCESS);
1220
1221 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1222 }
1223
1224 TestUserData();
1225}
1226
1227void TestHeapSizeLimit()
1228{
1229 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1230 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1231
1232 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1233 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1234 {
1235 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1236 }
1237
1238 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1239 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1240 allocatorCreateInfo.device = g_hDevice;
1241 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1242
1243 VmaAllocator hAllocator;
1244 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
1245 assert(res == VK_SUCCESS);
1246
1247 struct Item
1248 {
1249 VkBuffer hBuf;
1250 VmaAllocation hAlloc;
1251 };
1252 std::vector<Item> items;
1253
1254 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1255 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1256
1257 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1258 VmaAllocationInfo ownAllocInfo;
1259 {
1260 VmaAllocationCreateInfo allocCreateInfo = {};
1261 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1262 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1263
1264 bufCreateInfo.size = BLOCK_SIZE / 2;
1265
1266 for(size_t i = 0; i < 2; ++i)
1267 {
1268 Item item;
1269 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
1270 assert(res == VK_SUCCESS);
1271 items.push_back(item);
1272 }
1273 }
1274
1275 // Create pool to make sure allocations must be out of this memory type.
1276 VmaPoolCreateInfo poolCreateInfo = {};
1277 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1278 poolCreateInfo.blockSize = BLOCK_SIZE;
1279
1280 VmaPool hPool;
1281 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
1282 assert(res == VK_SUCCESS);
1283
1284 // 2. Allocate normal buffers from all the remaining memory.
1285 {
1286 VmaAllocationCreateInfo allocCreateInfo = {};
1287 allocCreateInfo.pool = hPool;
1288
1289 bufCreateInfo.size = BLOCK_SIZE / 2;
1290
1291 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1292 for(size_t i = 0; i < bufCount; ++i)
1293 {
1294 Item item;
1295 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
1296 assert(res == VK_SUCCESS);
1297 items.push_back(item);
1298 }
1299 }
1300
1301 // 3. Allocation of one more (even small) buffer should fail.
1302 {
1303 VmaAllocationCreateInfo allocCreateInfo = {};
1304 allocCreateInfo.pool = hPool;
1305
1306 bufCreateInfo.size = 128;
1307
1308 VkBuffer hBuf;
1309 VmaAllocation hAlloc;
1310 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
1311 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1312 }
1313
1314 // Destroy everything.
1315 for(size_t i = items.size(); i--; )
1316 {
1317 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1318 }
1319
1320 vmaDestroyPool(hAllocator, hPool);
1321
1322 vmaDestroyAllocator(hAllocator);
1323}
1324
Adam Sawicki212a4a62018-06-14 15:44:45 +02001325#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001326static void TestDebugMargin()
1327{
1328 if(VMA_DEBUG_MARGIN == 0)
1329 {
1330 return;
1331 }
1332
1333 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001334 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001335
1336 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001337 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001338
1339 // Create few buffers of different size.
1340 const size_t BUF_COUNT = 10;
1341 BufferInfo buffers[BUF_COUNT];
1342 VmaAllocationInfo allocInfo[BUF_COUNT];
1343 for(size_t i = 0; i < 10; ++i)
1344 {
1345 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001346 // Last one will be mapped.
1347 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001348
1349 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
1350 assert(res == VK_SUCCESS);
1351 // Margin is preserved also at the beginning of a block.
1352 assert(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001353
1354 if(i == BUF_COUNT - 1)
1355 {
1356 // Fill with data.
1357 assert(allocInfo[i].pMappedData != nullptr);
1358 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1359 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1360 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001361 }
1362
1363 // Check if their offsets preserve margin between them.
1364 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1365 {
1366 if(lhs.deviceMemory != rhs.deviceMemory)
1367 {
1368 return lhs.deviceMemory < rhs.deviceMemory;
1369 }
1370 return lhs.offset < rhs.offset;
1371 });
1372 for(size_t i = 1; i < BUF_COUNT; ++i)
1373 {
1374 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1375 {
1376 assert(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
1377 }
1378 }
1379
Adam Sawicki212a4a62018-06-14 15:44:45 +02001380 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
1381 assert(res == VK_SUCCESS);
1382
Adam Sawicki73b16652018-06-11 16:39:25 +02001383 // Destroy all buffers.
1384 for(size_t i = BUF_COUNT; i--; )
1385 {
1386 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1387 }
1388}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001389#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001390
Adam Sawickib8333fb2018-03-13 16:15:53 +01001391static void TestPool_SameSize()
1392{
1393 const VkDeviceSize BUF_SIZE = 1024 * 1024;
1394 const size_t BUF_COUNT = 100;
1395 VkResult res;
1396
1397 RandomNumberGenerator rand{123};
1398
1399 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1400 bufferInfo.size = BUF_SIZE;
1401 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1402
1403 uint32_t memoryTypeBits = UINT32_MAX;
1404 {
1405 VkBuffer dummyBuffer;
1406 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
1407 assert(res == VK_SUCCESS);
1408
1409 VkMemoryRequirements memReq;
1410 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
1411 memoryTypeBits = memReq.memoryTypeBits;
1412
1413 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
1414 }
1415
1416 VmaAllocationCreateInfo poolAllocInfo = {};
1417 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1418 uint32_t memTypeIndex;
1419 res = vmaFindMemoryTypeIndex(
1420 g_hAllocator,
1421 memoryTypeBits,
1422 &poolAllocInfo,
1423 &memTypeIndex);
1424
1425 VmaPoolCreateInfo poolCreateInfo = {};
1426 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1427 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
1428 poolCreateInfo.minBlockCount = 1;
1429 poolCreateInfo.maxBlockCount = 4;
1430 poolCreateInfo.frameInUseCount = 0;
1431
1432 VmaPool pool;
1433 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1434 assert(res == VK_SUCCESS);
1435
1436 vmaSetCurrentFrameIndex(g_hAllocator, 1);
1437
1438 VmaAllocationCreateInfo allocInfo = {};
1439 allocInfo.pool = pool;
1440 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1441 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1442
1443 struct BufItem
1444 {
1445 VkBuffer Buf;
1446 VmaAllocation Alloc;
1447 };
1448 std::vector<BufItem> items;
1449
1450 // Fill entire pool.
1451 for(size_t i = 0; i < BUF_COUNT; ++i)
1452 {
1453 BufItem item;
1454 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1455 assert(res == VK_SUCCESS);
1456 items.push_back(item);
1457 }
1458
1459 // Make sure that another allocation would fail.
1460 {
1461 BufItem item;
1462 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1463 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1464 }
1465
1466 // Validate that no buffer is lost. Also check that they are not mapped.
1467 for(size_t i = 0; i < items.size(); ++i)
1468 {
1469 VmaAllocationInfo allocInfo;
1470 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1471 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1472 assert(allocInfo.pMappedData == nullptr);
1473 }
1474
1475 // Free some percent of random items.
1476 {
1477 const size_t PERCENT_TO_FREE = 10;
1478 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
1479 for(size_t i = 0; i < itemsToFree; ++i)
1480 {
1481 size_t index = (size_t)rand.Generate() % items.size();
1482 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1483 items.erase(items.begin() + index);
1484 }
1485 }
1486
1487 // Randomly allocate and free items.
1488 {
1489 const size_t OPERATION_COUNT = BUF_COUNT;
1490 for(size_t i = 0; i < OPERATION_COUNT; ++i)
1491 {
1492 bool allocate = rand.Generate() % 2 != 0;
1493 if(allocate)
1494 {
1495 if(items.size() < BUF_COUNT)
1496 {
1497 BufItem item;
1498 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1499 assert(res == VK_SUCCESS);
1500 items.push_back(item);
1501 }
1502 }
1503 else // Free
1504 {
1505 if(!items.empty())
1506 {
1507 size_t index = (size_t)rand.Generate() % items.size();
1508 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
1509 items.erase(items.begin() + index);
1510 }
1511 }
1512 }
1513 }
1514
1515 // Allocate up to maximum.
1516 while(items.size() < BUF_COUNT)
1517 {
1518 BufItem item;
1519 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1520 assert(res == VK_SUCCESS);
1521 items.push_back(item);
1522 }
1523
1524 // Validate that no buffer is lost.
1525 for(size_t i = 0; i < items.size(); ++i)
1526 {
1527 VmaAllocationInfo allocInfo;
1528 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1529 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1530 }
1531
1532 // Next frame.
1533 vmaSetCurrentFrameIndex(g_hAllocator, 2);
1534
1535 // Allocate another BUF_COUNT buffers.
1536 for(size_t i = 0; i < BUF_COUNT; ++i)
1537 {
1538 BufItem item;
1539 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1540 assert(res == VK_SUCCESS);
1541 items.push_back(item);
1542 }
1543
1544 // Make sure the first BUF_COUNT is lost. Delete them.
1545 for(size_t i = 0; i < BUF_COUNT; ++i)
1546 {
1547 VmaAllocationInfo allocInfo;
1548 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1549 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1550 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1551 }
1552 items.erase(items.begin(), items.begin() + BUF_COUNT);
1553
1554 // Validate that no buffer is lost.
1555 for(size_t i = 0; i < items.size(); ++i)
1556 {
1557 VmaAllocationInfo allocInfo;
1558 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1559 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
1560 }
1561
1562 // Free one item.
1563 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
1564 items.pop_back();
1565
1566 // Validate statistics.
1567 {
1568 VmaPoolStats poolStats = {};
1569 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1570 assert(poolStats.allocationCount == items.size());
1571 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
1572 assert(poolStats.unusedRangeCount == 1);
1573 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
1574 assert(poolStats.unusedSize == BUF_SIZE);
1575 }
1576
1577 // Free all remaining items.
1578 for(size_t i = items.size(); i--; )
1579 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1580 items.clear();
1581
1582 // Allocate maximum items again.
1583 for(size_t i = 0; i < BUF_COUNT; ++i)
1584 {
1585 BufItem item;
1586 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1587 assert(res == VK_SUCCESS);
1588 items.push_back(item);
1589 }
1590
1591 // Delete every other item.
1592 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
1593 {
1594 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1595 items.erase(items.begin() + i);
1596 }
1597
1598 // Defragment!
1599 {
1600 std::vector<VmaAllocation> allocationsToDefragment(items.size());
1601 for(size_t i = 0; i < items.size(); ++i)
1602 allocationsToDefragment[i] = items[i].Alloc;
1603
1604 VmaDefragmentationStats defragmentationStats;
1605 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
1606 assert(res == VK_SUCCESS);
1607 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
1608 }
1609
1610 // Free all remaining items.
1611 for(size_t i = items.size(); i--; )
1612 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1613 items.clear();
1614
1615 ////////////////////////////////////////////////////////////////////////////////
1616 // Test for vmaMakePoolAllocationsLost
1617
1618 // Allocate 4 buffers on frame 10.
1619 vmaSetCurrentFrameIndex(g_hAllocator, 10);
1620 for(size_t i = 0; i < 4; ++i)
1621 {
1622 BufItem item;
1623 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
1624 assert(res == VK_SUCCESS);
1625 items.push_back(item);
1626 }
1627
1628 // Touch first 2 of them on frame 11.
1629 vmaSetCurrentFrameIndex(g_hAllocator, 11);
1630 for(size_t i = 0; i < 2; ++i)
1631 {
1632 VmaAllocationInfo allocInfo;
1633 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
1634 }
1635
1636 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
1637 size_t lostCount = 0xDEADC0DE;
1638 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1639 assert(lostCount == 2);
1640
1641 // Make another call. Now 0 should be lost.
1642 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
1643 assert(lostCount == 0);
1644
1645 // Make another call, with null count. Should not crash.
1646 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
1647
1648 // END: Free all remaining items.
1649 for(size_t i = items.size(); i--; )
1650 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
1651
1652 items.clear();
1653
Adam Sawickid2924172018-06-11 12:48:46 +02001654 ////////////////////////////////////////////////////////////////////////////////
1655 // Test for allocation too large for pool
1656
1657 {
1658 VmaAllocationCreateInfo allocCreateInfo = {};
1659 allocCreateInfo.pool = pool;
1660
1661 VkMemoryRequirements memReq;
1662 memReq.memoryTypeBits = UINT32_MAX;
1663 memReq.alignment = 1;
1664 memReq.size = poolCreateInfo.blockSize + 4;
1665
1666 VmaAllocation alloc = nullptr;
1667 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
1668 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
1669 }
1670
Adam Sawickib8333fb2018-03-13 16:15:53 +01001671 vmaDestroyPool(g_hAllocator, pool);
1672}
1673
1674static void TestPool_Benchmark(
1675 PoolTestResult& outResult,
1676 const PoolTestConfig& config)
1677{
1678 assert(config.ThreadCount > 0);
1679
1680 RandomNumberGenerator mainRand{config.RandSeed};
1681
1682 uint32_t allocationSizeProbabilitySum = std::accumulate(
1683 config.AllocationSizes.begin(),
1684 config.AllocationSizes.end(),
1685 0u,
1686 [](uint32_t sum, const AllocationSize& allocSize) {
1687 return sum + allocSize.Probability;
1688 });
1689
1690 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1691 bufferInfo.size = 256; // Whatever.
1692 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1693
1694 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1695 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1696 imageInfo.extent.width = 256; // Whatever.
1697 imageInfo.extent.height = 256; // Whatever.
1698 imageInfo.extent.depth = 1;
1699 imageInfo.mipLevels = 1;
1700 imageInfo.arrayLayers = 1;
1701 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1702 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
1703 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1704 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
1705 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1706
1707 uint32_t bufferMemoryTypeBits = UINT32_MAX;
1708 {
1709 VkBuffer dummyBuffer;
1710 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
1711 assert(res == VK_SUCCESS);
1712
1713 VkMemoryRequirements memReq;
1714 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
1715 bufferMemoryTypeBits = memReq.memoryTypeBits;
1716
1717 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
1718 }
1719
1720 uint32_t imageMemoryTypeBits = UINT32_MAX;
1721 {
1722 VkImage dummyImage;
1723 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
1724 assert(res == VK_SUCCESS);
1725
1726 VkMemoryRequirements memReq;
1727 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
1728 imageMemoryTypeBits = memReq.memoryTypeBits;
1729
1730 vkDestroyImage(g_hDevice, dummyImage, nullptr);
1731 }
1732
1733 uint32_t memoryTypeBits = 0;
1734 if(config.UsesBuffers() && config.UsesImages())
1735 {
1736 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
1737 if(memoryTypeBits == 0)
1738 {
1739 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
1740 return;
1741 }
1742 }
1743 else if(config.UsesBuffers())
1744 memoryTypeBits = bufferMemoryTypeBits;
1745 else if(config.UsesImages())
1746 memoryTypeBits = imageMemoryTypeBits;
1747 else
1748 assert(0);
1749
1750 VmaPoolCreateInfo poolCreateInfo = {};
1751 poolCreateInfo.memoryTypeIndex = 0;
1752 poolCreateInfo.minBlockCount = 1;
1753 poolCreateInfo.maxBlockCount = 1;
1754 poolCreateInfo.blockSize = config.PoolSize;
1755 poolCreateInfo.frameInUseCount = 1;
1756
1757 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
1758 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1759 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1760
1761 VmaPool pool;
1762 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1763 assert(res == VK_SUCCESS);
1764
1765 // Start time measurement - after creating pool and initializing data structures.
1766 time_point timeBeg = std::chrono::high_resolution_clock::now();
1767
1768 ////////////////////////////////////////////////////////////////////////////////
1769 // ThreadProc
1770 auto ThreadProc = [&](
1771 PoolTestThreadResult* outThreadResult,
1772 uint32_t randSeed,
1773 HANDLE frameStartEvent,
1774 HANDLE frameEndEvent) -> void
1775 {
1776 RandomNumberGenerator threadRand{randSeed};
1777
1778 outThreadResult->AllocationTimeMin = duration::max();
1779 outThreadResult->AllocationTimeSum = duration::zero();
1780 outThreadResult->AllocationTimeMax = duration::min();
1781 outThreadResult->DeallocationTimeMin = duration::max();
1782 outThreadResult->DeallocationTimeSum = duration::zero();
1783 outThreadResult->DeallocationTimeMax = duration::min();
1784 outThreadResult->AllocationCount = 0;
1785 outThreadResult->DeallocationCount = 0;
1786 outThreadResult->LostAllocationCount = 0;
1787 outThreadResult->LostAllocationTotalSize = 0;
1788 outThreadResult->FailedAllocationCount = 0;
1789 outThreadResult->FailedAllocationTotalSize = 0;
1790
1791 struct Item
1792 {
1793 VkDeviceSize BufferSize;
1794 VkExtent2D ImageSize;
1795 VkBuffer Buf;
1796 VkImage Image;
1797 VmaAllocation Alloc;
1798
1799 VkDeviceSize CalcSizeBytes() const
1800 {
1801 return BufferSize +
1802 ImageSize.width * ImageSize.height * 4;
1803 }
1804 };
1805 std::vector<Item> unusedItems, usedItems;
1806
1807 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
1808
1809 // Create all items - all unused, not yet allocated.
1810 for(size_t i = 0; i < threadTotalItemCount; ++i)
1811 {
1812 Item item = {};
1813
1814 uint32_t allocSizeIndex = 0;
1815 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
1816 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
1817 r -= config.AllocationSizes[allocSizeIndex++].Probability;
1818
1819 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
1820 if(allocSize.BufferSizeMax > 0)
1821 {
1822 assert(allocSize.BufferSizeMin > 0);
1823 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
1824 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
1825 item.BufferSize = allocSize.BufferSizeMin;
1826 else
1827 {
1828 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
1829 item.BufferSize = item.BufferSize / 16 * 16;
1830 }
1831 }
1832 else
1833 {
1834 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
1835 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
1836 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
1837 else
1838 {
1839 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
1840 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
1841 }
1842 }
1843
1844 unusedItems.push_back(item);
1845 }
1846
1847 auto Allocate = [&](Item& item) -> VkResult
1848 {
1849 VmaAllocationCreateInfo allocCreateInfo = {};
1850 allocCreateInfo.pool = pool;
1851 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1852 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1853
1854 if(item.BufferSize)
1855 {
1856 bufferInfo.size = item.BufferSize;
1857 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1858 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
1859 }
1860 else
1861 {
1862 assert(item.ImageSize.width && item.ImageSize.height);
1863
1864 imageInfo.extent.width = item.ImageSize.width;
1865 imageInfo.extent.height = item.ImageSize.height;
1866 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1867 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
1868 }
1869 };
1870
1871 ////////////////////////////////////////////////////////////////////////////////
1872 // Frames
1873 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
1874 {
1875 WaitForSingleObject(frameStartEvent, INFINITE);
1876
1877 // Always make some percent of used bufs unused, to choose different used ones.
1878 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
1879 for(size_t i = 0; i < bufsToMakeUnused; ++i)
1880 {
1881 size_t index = threadRand.Generate() % usedItems.size();
1882 unusedItems.push_back(usedItems[index]);
1883 usedItems.erase(usedItems.begin() + index);
1884 }
1885
1886 // Determine which bufs we want to use in this frame.
1887 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
1888 / config.ThreadCount;
1889 assert(usedBufCount < usedItems.size() + unusedItems.size());
1890 // Move some used to unused.
1891 while(usedBufCount < usedItems.size())
1892 {
1893 size_t index = threadRand.Generate() % usedItems.size();
1894 unusedItems.push_back(usedItems[index]);
1895 usedItems.erase(usedItems.begin() + index);
1896 }
1897 // Move some unused to used.
1898 while(usedBufCount > usedItems.size())
1899 {
1900 size_t index = threadRand.Generate() % unusedItems.size();
1901 usedItems.push_back(unusedItems[index]);
1902 unusedItems.erase(unusedItems.begin() + index);
1903 }
1904
1905 uint32_t touchExistingCount = 0;
1906 uint32_t touchLostCount = 0;
1907 uint32_t createSucceededCount = 0;
1908 uint32_t createFailedCount = 0;
1909
1910 // Touch all used bufs. If not created or lost, allocate.
1911 for(size_t i = 0; i < usedItems.size(); ++i)
1912 {
1913 Item& item = usedItems[i];
1914 // Not yet created.
1915 if(item.Alloc == VK_NULL_HANDLE)
1916 {
1917 res = Allocate(item);
1918 ++outThreadResult->AllocationCount;
1919 if(res != VK_SUCCESS)
1920 {
1921 item.Alloc = VK_NULL_HANDLE;
1922 item.Buf = VK_NULL_HANDLE;
1923 ++outThreadResult->FailedAllocationCount;
1924 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
1925 ++createFailedCount;
1926 }
1927 else
1928 ++createSucceededCount;
1929 }
1930 else
1931 {
1932 // Touch.
1933 VmaAllocationInfo allocInfo;
1934 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
1935 // Lost.
1936 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1937 {
1938 ++touchLostCount;
1939
1940 // Destroy.
1941 {
1942 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1943 if(item.Buf)
1944 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
1945 else
1946 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
1947 ++outThreadResult->DeallocationCount;
1948 }
1949 item.Alloc = VK_NULL_HANDLE;
1950 item.Buf = VK_NULL_HANDLE;
1951
1952 ++outThreadResult->LostAllocationCount;
1953 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
1954
1955 // Recreate.
1956 res = Allocate(item);
1957 ++outThreadResult->AllocationCount;
1958 // Creation failed.
1959 if(res != VK_SUCCESS)
1960 {
1961 ++outThreadResult->FailedAllocationCount;
1962 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
1963 ++createFailedCount;
1964 }
1965 else
1966 ++createSucceededCount;
1967 }
1968 else
1969 ++touchExistingCount;
1970 }
1971 }
1972
1973 /*
1974 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
1975 randSeed, frameIndex,
1976 touchExistingCount, touchLostCount,
1977 createSucceededCount, createFailedCount);
1978 */
1979
1980 SetEvent(frameEndEvent);
1981 }
1982
1983 // Free all remaining items.
1984 for(size_t i = usedItems.size(); i--; )
1985 {
1986 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
1987 if(usedItems[i].Buf)
1988 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
1989 else
1990 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
1991 ++outThreadResult->DeallocationCount;
1992 }
1993 for(size_t i = unusedItems.size(); i--; )
1994 {
1995 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
1996 if(unusedItems[i].Buf)
1997 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
1998 else
1999 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
2000 ++outThreadResult->DeallocationCount;
2001 }
2002 };
2003
2004 // Launch threads.
2005 uint32_t threadRandSeed = mainRand.Generate();
2006 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
2007 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
2008 std::vector<std::thread> bkgThreads;
2009 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
2010 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2011 {
2012 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2013 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2014 bkgThreads.emplace_back(std::bind(
2015 ThreadProc,
2016 &threadResults[threadIndex],
2017 threadRandSeed + threadIndex,
2018 frameStartEvents[threadIndex],
2019 frameEndEvents[threadIndex]));
2020 }
2021
2022 // Execute frames.
2023 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
2024 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2025 {
2026 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
2027 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2028 SetEvent(frameStartEvents[threadIndex]);
2029 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
2030 }
2031
2032 // Wait for threads finished
2033 for(size_t i = 0; i < bkgThreads.size(); ++i)
2034 {
2035 bkgThreads[i].join();
2036 CloseHandle(frameEndEvents[i]);
2037 CloseHandle(frameStartEvents[i]);
2038 }
2039 bkgThreads.clear();
2040
2041 // Finish time measurement - before destroying pool.
2042 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
2043
2044 vmaDestroyPool(g_hAllocator, pool);
2045
2046 outResult.AllocationTimeMin = duration::max();
2047 outResult.AllocationTimeAvg = duration::zero();
2048 outResult.AllocationTimeMax = duration::min();
2049 outResult.DeallocationTimeMin = duration::max();
2050 outResult.DeallocationTimeAvg = duration::zero();
2051 outResult.DeallocationTimeMax = duration::min();
2052 outResult.LostAllocationCount = 0;
2053 outResult.LostAllocationTotalSize = 0;
2054 outResult.FailedAllocationCount = 0;
2055 outResult.FailedAllocationTotalSize = 0;
2056 size_t allocationCount = 0;
2057 size_t deallocationCount = 0;
2058 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2059 {
2060 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
2061 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
2062 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
2063 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
2064 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
2065 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
2066 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
2067 allocationCount += threadResult.AllocationCount;
2068 deallocationCount += threadResult.DeallocationCount;
2069 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
2070 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
2071 outResult.LostAllocationCount += threadResult.LostAllocationCount;
2072 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
2073 }
2074 if(allocationCount)
2075 outResult.AllocationTimeAvg /= allocationCount;
2076 if(deallocationCount)
2077 outResult.DeallocationTimeAvg /= deallocationCount;
2078}
2079
2080static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
2081{
2082 if(ptr1 < ptr2)
2083 return ptr1 + size1 > ptr2;
2084 else if(ptr2 < ptr1)
2085 return ptr2 + size2 > ptr1;
2086 else
2087 return true;
2088}
2089
2090static void TestMapping()
2091{
2092 wprintf(L"Testing mapping...\n");
2093
2094 VkResult res;
2095 uint32_t memTypeIndex = UINT32_MAX;
2096
2097 enum TEST
2098 {
2099 TEST_NORMAL,
2100 TEST_POOL,
2101 TEST_DEDICATED,
2102 TEST_COUNT
2103 };
2104 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2105 {
2106 VmaPool pool = nullptr;
2107 if(testIndex == TEST_POOL)
2108 {
2109 assert(memTypeIndex != UINT32_MAX);
2110 VmaPoolCreateInfo poolInfo = {};
2111 poolInfo.memoryTypeIndex = memTypeIndex;
2112 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2113 assert(res == VK_SUCCESS);
2114 }
2115
2116 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2117 bufInfo.size = 0x10000;
2118 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2119
2120 VmaAllocationCreateInfo allocCreateInfo = {};
2121 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2122 allocCreateInfo.pool = pool;
2123 if(testIndex == TEST_DEDICATED)
2124 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2125
2126 VmaAllocationInfo allocInfo;
2127
2128 // Mapped manually
2129
2130 // Create 2 buffers.
2131 BufferInfo bufferInfos[3];
2132 for(size_t i = 0; i < 2; ++i)
2133 {
2134 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2135 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
2136 assert(res == VK_SUCCESS);
2137 assert(allocInfo.pMappedData == nullptr);
2138 memTypeIndex = allocInfo.memoryType;
2139 }
2140
2141 // Map buffer 0.
2142 char* data00 = nullptr;
2143 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
2144 assert(res == VK_SUCCESS && data00 != nullptr);
2145 data00[0xFFFF] = data00[0];
2146
2147 // Map buffer 0 second time.
2148 char* data01 = nullptr;
2149 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
2150 assert(res == VK_SUCCESS && data01 == data00);
2151
2152 // Map buffer 1.
2153 char* data1 = nullptr;
2154 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
2155 assert(res == VK_SUCCESS && data1 != nullptr);
2156 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
2157 data1[0xFFFF] = data1[0];
2158
2159 // Unmap buffer 0 two times.
2160 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2161 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2162 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
2163 assert(allocInfo.pMappedData == nullptr);
2164
2165 // Unmap buffer 1.
2166 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
2167 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
2168 assert(allocInfo.pMappedData == nullptr);
2169
2170 // Create 3rd buffer - persistently mapped.
2171 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2172 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2173 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
2174 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
2175
2176 // Map buffer 2.
2177 char* data2 = nullptr;
2178 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
2179 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
2180 data2[0xFFFF] = data2[0];
2181
2182 // Unmap buffer 2.
2183 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
2184 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
2185 assert(allocInfo.pMappedData == data2);
2186
2187 // Destroy all buffers.
2188 for(size_t i = 3; i--; )
2189 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
2190
2191 vmaDestroyPool(g_hAllocator, pool);
2192 }
2193}
2194
2195static void TestMappingMultithreaded()
2196{
2197 wprintf(L"Testing mapping multithreaded...\n");
2198
2199 static const uint32_t threadCount = 16;
2200 static const uint32_t bufferCount = 1024;
2201 static const uint32_t threadBufferCount = bufferCount / threadCount;
2202
2203 VkResult res;
2204 volatile uint32_t memTypeIndex = UINT32_MAX;
2205
2206 enum TEST
2207 {
2208 TEST_NORMAL,
2209 TEST_POOL,
2210 TEST_DEDICATED,
2211 TEST_COUNT
2212 };
2213 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2214 {
2215 VmaPool pool = nullptr;
2216 if(testIndex == TEST_POOL)
2217 {
2218 assert(memTypeIndex != UINT32_MAX);
2219 VmaPoolCreateInfo poolInfo = {};
2220 poolInfo.memoryTypeIndex = memTypeIndex;
2221 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2222 assert(res == VK_SUCCESS);
2223 }
2224
2225 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2226 bufCreateInfo.size = 0x10000;
2227 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2228
2229 VmaAllocationCreateInfo allocCreateInfo = {};
2230 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2231 allocCreateInfo.pool = pool;
2232 if(testIndex == TEST_DEDICATED)
2233 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2234
2235 std::thread threads[threadCount];
2236 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2237 {
2238 threads[threadIndex] = std::thread([=, &memTypeIndex](){
2239 // ======== THREAD FUNCTION ========
2240
2241 RandomNumberGenerator rand{threadIndex};
2242
2243 enum class MODE
2244 {
2245 // Don't map this buffer at all.
2246 DONT_MAP,
2247 // Map and quickly unmap.
2248 MAP_FOR_MOMENT,
2249 // Map and unmap before destruction.
2250 MAP_FOR_LONGER,
2251 // Map two times. Quickly unmap, second unmap before destruction.
2252 MAP_TWO_TIMES,
2253 // Create this buffer as persistently mapped.
2254 PERSISTENTLY_MAPPED,
2255 COUNT
2256 };
2257 std::vector<BufferInfo> bufInfos{threadBufferCount};
2258 std::vector<MODE> bufModes{threadBufferCount};
2259
2260 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
2261 {
2262 BufferInfo& bufInfo = bufInfos[bufferIndex];
2263 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
2264 bufModes[bufferIndex] = mode;
2265
2266 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
2267 if(mode == MODE::PERSISTENTLY_MAPPED)
2268 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
2269
2270 VmaAllocationInfo allocInfo;
2271 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
2272 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
2273 assert(res == VK_SUCCESS);
2274
2275 if(memTypeIndex == UINT32_MAX)
2276 memTypeIndex = allocInfo.memoryType;
2277
2278 char* data = nullptr;
2279
2280 if(mode == MODE::PERSISTENTLY_MAPPED)
2281 {
2282 data = (char*)allocInfo.pMappedData;
2283 assert(data != nullptr);
2284 }
2285 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
2286 mode == MODE::MAP_TWO_TIMES)
2287 {
2288 assert(data == nullptr);
2289 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
2290 assert(res == VK_SUCCESS && data != nullptr);
2291
2292 if(mode == MODE::MAP_TWO_TIMES)
2293 {
2294 char* data2 = nullptr;
2295 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
2296 assert(res == VK_SUCCESS && data2 == data);
2297 }
2298 }
2299 else if(mode == MODE::DONT_MAP)
2300 {
2301 assert(allocInfo.pMappedData == nullptr);
2302 }
2303 else
2304 assert(0);
2305
2306 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2307 if(data)
2308 data[0xFFFF] = data[0];
2309
2310 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
2311 {
2312 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
2313
2314 VmaAllocationInfo allocInfo;
2315 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
2316 if(mode == MODE::MAP_FOR_MOMENT)
2317 assert(allocInfo.pMappedData == nullptr);
2318 else
2319 assert(allocInfo.pMappedData == data);
2320 }
2321
2322 switch(rand.Generate() % 3)
2323 {
2324 case 0: Sleep(0); break; // Yield.
2325 case 1: Sleep(10); break; // 10 ms
2326 // default: No sleep.
2327 }
2328
2329 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
2330 if(data)
2331 data[0xFFFF] = data[0];
2332 }
2333
2334 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
2335 {
2336 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
2337 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
2338 {
2339 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
2340
2341 VmaAllocationInfo allocInfo;
2342 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
2343 assert(allocInfo.pMappedData == nullptr);
2344 }
2345
2346 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
2347 }
2348 });
2349 }
2350
2351 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
2352 threads[threadIndex].join();
2353
2354 vmaDestroyPool(g_hAllocator, pool);
2355 }
2356}
2357
2358static void WriteMainTestResultHeader(FILE* file)
2359{
2360 fprintf(file,
2361 "Code,Test,Time,"
2362 "Config,"
2363 "Total Time (us),"
2364 "Allocation Time Min (us),"
2365 "Allocation Time Avg (us),"
2366 "Allocation Time Max (us),"
2367 "Deallocation Time Min (us),"
2368 "Deallocation Time Avg (us),"
2369 "Deallocation Time Max (us),"
2370 "Total Memory Allocated (B),"
2371 "Free Range Size Avg (B),"
2372 "Free Range Size Max (B)\n");
2373}
2374
2375static void WriteMainTestResult(
2376 FILE* file,
2377 const char* codeDescription,
2378 const char* testDescription,
2379 const Config& config, const Result& result)
2380{
2381 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2382 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2383 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2384 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2385 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2386 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2387 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2388
2389 time_t rawTime; time(&rawTime);
2390 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2391 char timeStr[128];
2392 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2393
2394 fprintf(file,
2395 "%s,%s,%s,"
2396 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%d,"
2397 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
2398 codeDescription,
2399 testDescription,
2400 timeStr,
2401 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, (uint32_t)config.FreeOrder,
2402 totalTimeSeconds * 1e6f,
2403 allocationTimeMinSeconds * 1e6f,
2404 allocationTimeAvgSeconds * 1e6f,
2405 allocationTimeMaxSeconds * 1e6f,
2406 deallocationTimeMinSeconds * 1e6f,
2407 deallocationTimeAvgSeconds * 1e6f,
2408 deallocationTimeMaxSeconds * 1e6f,
2409 result.TotalMemoryAllocated,
2410 result.FreeRangeSizeAvg,
2411 result.FreeRangeSizeMax);
2412}
2413
2414static void WritePoolTestResultHeader(FILE* file)
2415{
2416 fprintf(file,
2417 "Code,Test,Time,"
2418 "Config,"
2419 "Total Time (us),"
2420 "Allocation Time Min (us),"
2421 "Allocation Time Avg (us),"
2422 "Allocation Time Max (us),"
2423 "Deallocation Time Min (us),"
2424 "Deallocation Time Avg (us),"
2425 "Deallocation Time Max (us),"
2426 "Lost Allocation Count,"
2427 "Lost Allocation Total Size (B),"
2428 "Failed Allocation Count,"
2429 "Failed Allocation Total Size (B)\n");
2430}
2431
2432static void WritePoolTestResult(
2433 FILE* file,
2434 const char* codeDescription,
2435 const char* testDescription,
2436 const PoolTestConfig& config,
2437 const PoolTestResult& result)
2438{
2439 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
2440 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
2441 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
2442 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
2443 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
2444 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
2445 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
2446
2447 time_t rawTime; time(&rawTime);
2448 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
2449 char timeStr[128];
2450 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
2451
2452 fprintf(file,
2453 "%s,%s,%s,"
2454 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
2455 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
2456 // General
2457 codeDescription,
2458 testDescription,
2459 timeStr,
2460 // Config
2461 config.ThreadCount,
2462 (unsigned long long)config.PoolSize,
2463 config.FrameCount,
2464 config.TotalItemCount,
2465 config.UsedItemCountMin,
2466 config.UsedItemCountMax,
2467 config.ItemsToMakeUnusedPercent,
2468 // Results
2469 totalTimeSeconds * 1e6f,
2470 allocationTimeMinSeconds * 1e6f,
2471 allocationTimeAvgSeconds * 1e6f,
2472 allocationTimeMaxSeconds * 1e6f,
2473 deallocationTimeMinSeconds * 1e6f,
2474 deallocationTimeAvgSeconds * 1e6f,
2475 deallocationTimeMaxSeconds * 1e6f,
2476 result.LostAllocationCount,
2477 result.LostAllocationTotalSize,
2478 result.FailedAllocationCount,
2479 result.FailedAllocationTotalSize);
2480}
2481
2482static void PerformCustomMainTest(FILE* file)
2483{
2484 Config config{};
2485 config.RandSeed = 65735476;
2486 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
2487 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
2488 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2489 config.FreeOrder = FREE_ORDER::FORWARD;
2490 config.ThreadCount = 16;
2491 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2492
2493 // Buffers
2494 //config.AllocationSizes.push_back({4, 16, 1024});
2495 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2496
2497 // Images
2498 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2499 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2500
2501 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
2502 config.AdditionalOperationCount = 1024;
2503
2504 Result result{};
2505 VkResult res = MainTest(result, config);
2506 assert(res == VK_SUCCESS);
2507 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
2508}
2509
2510static void PerformCustomPoolTest(FILE* file)
2511{
2512 PoolTestConfig config;
2513 config.PoolSize = 100 * 1024 * 1024;
2514 config.RandSeed = 2345764;
2515 config.ThreadCount = 1;
2516 config.FrameCount = 200;
2517 config.ItemsToMakeUnusedPercent = 2;
2518
2519 AllocationSize allocSize = {};
2520 allocSize.BufferSizeMin = 1024;
2521 allocSize.BufferSizeMax = 1024 * 1024;
2522 allocSize.Probability = 1;
2523 config.AllocationSizes.push_back(allocSize);
2524
2525 allocSize.BufferSizeMin = 0;
2526 allocSize.BufferSizeMax = 0;
2527 allocSize.ImageSizeMin = 128;
2528 allocSize.ImageSizeMax = 1024;
2529 allocSize.Probability = 1;
2530 config.AllocationSizes.push_back(allocSize);
2531
2532 config.PoolSize = config.CalcAvgResourceSize() * 200;
2533 config.UsedItemCountMax = 160;
2534 config.TotalItemCount = config.UsedItemCountMax * 10;
2535 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
2536
2537 g_MemoryAliasingWarningEnabled = false;
2538 PoolTestResult result = {};
2539 TestPool_Benchmark(result, config);
2540 g_MemoryAliasingWarningEnabled = true;
2541
2542 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
2543}
2544
2545enum CONFIG_TYPE {
2546 CONFIG_TYPE_MINIMUM,
2547 CONFIG_TYPE_SMALL,
2548 CONFIG_TYPE_AVERAGE,
2549 CONFIG_TYPE_LARGE,
2550 CONFIG_TYPE_MAXIMUM,
2551 CONFIG_TYPE_COUNT
2552};
2553
2554static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
2555//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
2556static const char* CODE_DESCRIPTION = "Foo";
2557
2558static void PerformMainTests(FILE* file)
2559{
2560 uint32_t repeatCount = 1;
2561 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
2562
2563 Config config{};
2564 config.RandSeed = 65735476;
2565 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
2566 config.FreeOrder = FREE_ORDER::FORWARD;
2567
2568 size_t threadCountCount = 1;
2569 switch(ConfigType)
2570 {
2571 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
2572 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
2573 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
2574 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
2575 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
2576 default: assert(0);
2577 }
2578 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
2579 {
2580 std::string desc1;
2581
2582 switch(threadCountIndex)
2583 {
2584 case 0:
2585 desc1 += "1_thread";
2586 config.ThreadCount = 1;
2587 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2588 break;
2589 case 1:
2590 desc1 += "16_threads+0%_common";
2591 config.ThreadCount = 16;
2592 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2593 break;
2594 case 2:
2595 desc1 += "16_threads+50%_common";
2596 config.ThreadCount = 16;
2597 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2598 break;
2599 case 3:
2600 desc1 += "16_threads+100%_common";
2601 config.ThreadCount = 16;
2602 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
2603 break;
2604 case 4:
2605 desc1 += "2_threads+0%_common";
2606 config.ThreadCount = 2;
2607 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
2608 break;
2609 case 5:
2610 desc1 += "2_threads+50%_common";
2611 config.ThreadCount = 2;
2612 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
2613 break;
2614 case 6:
2615 desc1 += "2_threads+100%_common";
2616 config.ThreadCount = 2;
2617 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
2618 break;
2619 default:
2620 assert(0);
2621 }
2622
2623 // 0 = buffers, 1 = images, 2 = buffers and images
2624 size_t buffersVsImagesCount = 2;
2625 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
2626 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
2627 {
2628 std::string desc2 = desc1;
2629 switch(buffersVsImagesIndex)
2630 {
2631 case 0: desc2 += " Buffers"; break;
2632 case 1: desc2 += " Images"; break;
2633 case 2: desc2 += " Buffers+Images"; break;
2634 default: assert(0);
2635 }
2636
2637 // 0 = small, 1 = large, 2 = small and large
2638 size_t smallVsLargeCount = 2;
2639 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
2640 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
2641 {
2642 std::string desc3 = desc2;
2643 switch(smallVsLargeIndex)
2644 {
2645 case 0: desc3 += " Small"; break;
2646 case 1: desc3 += " Large"; break;
2647 case 2: desc3 += " Small+Large"; break;
2648 default: assert(0);
2649 }
2650
2651 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2652 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
2653 else
2654 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
2655
2656 // 0 = varying sizes min...max, 1 = set of constant sizes
2657 size_t constantSizesCount = 1;
2658 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
2659 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
2660 {
2661 std::string desc4 = desc3;
2662 switch(constantSizesIndex)
2663 {
2664 case 0: desc4 += " Varying_sizes"; break;
2665 case 1: desc4 += " Constant_sizes"; break;
2666 default: assert(0);
2667 }
2668
2669 config.AllocationSizes.clear();
2670 // Buffers present
2671 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
2672 {
2673 // Small
2674 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2675 {
2676 // Varying size
2677 if(constantSizesIndex == 0)
2678 config.AllocationSizes.push_back({4, 16, 1024});
2679 // Constant sizes
2680 else
2681 {
2682 config.AllocationSizes.push_back({1, 16, 16});
2683 config.AllocationSizes.push_back({1, 64, 64});
2684 config.AllocationSizes.push_back({1, 256, 256});
2685 config.AllocationSizes.push_back({1, 1024, 1024});
2686 }
2687 }
2688 // Large
2689 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2690 {
2691 // Varying size
2692 if(constantSizesIndex == 0)
2693 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2694 // Constant sizes
2695 else
2696 {
2697 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
2698 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
2699 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
2700 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
2701 }
2702 }
2703 }
2704 // Images present
2705 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
2706 {
2707 // Small
2708 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2709 {
2710 // Varying size
2711 if(constantSizesIndex == 0)
2712 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2713 // Constant sizes
2714 else
2715 {
2716 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
2717 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
2718 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
2719 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
2720 }
2721 }
2722 // Large
2723 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2724 {
2725 // Varying size
2726 if(constantSizesIndex == 0)
2727 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2728 // Constant sizes
2729 else
2730 {
2731 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
2732 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
2733 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
2734 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
2735 }
2736 }
2737 }
2738
2739 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
2740 size_t beginBytesToAllocateCount = 1;
2741 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
2742 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
2743 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
2744 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
2745 {
2746 std::string desc5 = desc4;
2747
2748 switch(beginBytesToAllocateIndex)
2749 {
2750 case 0:
2751 desc5 += " Allocate_100%";
2752 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
2753 config.AdditionalOperationCount = 0;
2754 break;
2755 case 1:
2756 desc5 += " Allocate_50%+Operations";
2757 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
2758 config.AdditionalOperationCount = 1024;
2759 break;
2760 case 2:
2761 desc5 += " Allocate_5%+Operations";
2762 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
2763 config.AdditionalOperationCount = 1024;
2764 break;
2765 case 3:
2766 desc5 += " Allocate_95%+Operations";
2767 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
2768 config.AdditionalOperationCount = 1024;
2769 break;
2770 default:
2771 assert(0);
2772 }
2773
2774 const char* testDescription = desc5.c_str();
2775
2776 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
2777 {
2778 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
2779
2780 Result result{};
2781 VkResult res = MainTest(result, config);
2782 assert(res == VK_SUCCESS);
2783 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
2784 }
2785 }
2786 }
2787 }
2788 }
2789 }
2790}
2791
2792static void PerformPoolTests(FILE* file)
2793{
2794 const size_t AVG_RESOURCES_PER_POOL = 300;
2795
2796 uint32_t repeatCount = 1;
2797 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
2798
2799 PoolTestConfig config{};
2800 config.RandSeed = 2346343;
2801 config.FrameCount = 200;
2802 config.ItemsToMakeUnusedPercent = 2;
2803
2804 size_t threadCountCount = 1;
2805 switch(ConfigType)
2806 {
2807 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
2808 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
2809 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
2810 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
2811 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
2812 default: assert(0);
2813 }
2814 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
2815 {
2816 std::string desc1;
2817
2818 switch(threadCountIndex)
2819 {
2820 case 0:
2821 desc1 += "1_thread";
2822 config.ThreadCount = 1;
2823 break;
2824 case 1:
2825 desc1 += "16_threads";
2826 config.ThreadCount = 16;
2827 break;
2828 case 2:
2829 desc1 += "2_threads";
2830 config.ThreadCount = 2;
2831 break;
2832 default:
2833 assert(0);
2834 }
2835
2836 // 0 = buffers, 1 = images, 2 = buffers and images
2837 size_t buffersVsImagesCount = 2;
2838 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
2839 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
2840 {
2841 std::string desc2 = desc1;
2842 switch(buffersVsImagesIndex)
2843 {
2844 case 0: desc2 += " Buffers"; break;
2845 case 1: desc2 += " Images"; break;
2846 case 2: desc2 += " Buffers+Images"; break;
2847 default: assert(0);
2848 }
2849
2850 // 0 = small, 1 = large, 2 = small and large
2851 size_t smallVsLargeCount = 2;
2852 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
2853 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
2854 {
2855 std::string desc3 = desc2;
2856 switch(smallVsLargeIndex)
2857 {
2858 case 0: desc3 += " Small"; break;
2859 case 1: desc3 += " Large"; break;
2860 case 2: desc3 += " Small+Large"; break;
2861 default: assert(0);
2862 }
2863
2864 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2865 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
2866 else
2867 config.PoolSize = 4ull * 1024 * 1024;
2868
2869 // 0 = varying sizes min...max, 1 = set of constant sizes
2870 size_t constantSizesCount = 1;
2871 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
2872 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
2873 {
2874 std::string desc4 = desc3;
2875 switch(constantSizesIndex)
2876 {
2877 case 0: desc4 += " Varying_sizes"; break;
2878 case 1: desc4 += " Constant_sizes"; break;
2879 default: assert(0);
2880 }
2881
2882 config.AllocationSizes.clear();
2883 // Buffers present
2884 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
2885 {
2886 // Small
2887 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2888 {
2889 // Varying size
2890 if(constantSizesIndex == 0)
2891 config.AllocationSizes.push_back({4, 16, 1024});
2892 // Constant sizes
2893 else
2894 {
2895 config.AllocationSizes.push_back({1, 16, 16});
2896 config.AllocationSizes.push_back({1, 64, 64});
2897 config.AllocationSizes.push_back({1, 256, 256});
2898 config.AllocationSizes.push_back({1, 1024, 1024});
2899 }
2900 }
2901 // Large
2902 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2903 {
2904 // Varying size
2905 if(constantSizesIndex == 0)
2906 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
2907 // Constant sizes
2908 else
2909 {
2910 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
2911 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
2912 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
2913 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
2914 }
2915 }
2916 }
2917 // Images present
2918 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
2919 {
2920 // Small
2921 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
2922 {
2923 // Varying size
2924 if(constantSizesIndex == 0)
2925 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
2926 // Constant sizes
2927 else
2928 {
2929 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
2930 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
2931 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
2932 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
2933 }
2934 }
2935 // Large
2936 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
2937 {
2938 // Varying size
2939 if(constantSizesIndex == 0)
2940 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
2941 // Constant sizes
2942 else
2943 {
2944 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
2945 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
2946 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
2947 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
2948 }
2949 }
2950 }
2951
2952 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
2953 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
2954
2955 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
2956 size_t subscriptionModeCount;
2957 switch(ConfigType)
2958 {
2959 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
2960 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
2961 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
2962 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
2963 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
2964 default: assert(0);
2965 }
2966 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
2967 {
2968 std::string desc5 = desc4;
2969
2970 switch(subscriptionModeIndex)
2971 {
2972 case 0:
2973 desc5 += " Subscription_66%";
2974 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
2975 break;
2976 case 1:
2977 desc5 += " Subscription_133%";
2978 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
2979 break;
2980 case 2:
2981 desc5 += " Subscription_100%";
2982 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
2983 break;
2984 case 3:
2985 desc5 += " Subscription_33%";
2986 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
2987 break;
2988 case 4:
2989 desc5 += " Subscription_166%";
2990 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
2991 break;
2992 default:
2993 assert(0);
2994 }
2995
2996 config.TotalItemCount = config.UsedItemCountMax * 5;
2997 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
2998
2999 const char* testDescription = desc5.c_str();
3000
3001 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3002 {
3003 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3004
3005 PoolTestResult result{};
3006 g_MemoryAliasingWarningEnabled = false;
3007 TestPool_Benchmark(result, config);
3008 g_MemoryAliasingWarningEnabled = true;
3009 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3010 }
3011 }
3012 }
3013 }
3014 }
3015 }
3016}
3017
3018void Test()
3019{
3020 wprintf(L"TESTING:\n");
3021
Adam Sawicki212a4a62018-06-14 15:44:45 +02003022 // TEMP tests
3023
Adam Sawickib8333fb2018-03-13 16:15:53 +01003024 // # Simple tests
3025
3026 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02003027#if VMA_DEBUG_MARGIN
3028 TestDebugMargin();
3029#else
3030 TestPool_SameSize();
3031 TestHeapSizeLimit();
3032#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01003033 TestMapping();
3034 TestMappingMultithreaded();
3035 TestDefragmentationSimple();
3036 TestDefragmentationFull();
3037
3038 // # Detailed tests
3039 FILE* file;
3040 fopen_s(&file, "Results.csv", "w");
3041 assert(file != NULL);
3042
3043 WriteMainTestResultHeader(file);
3044 PerformMainTests(file);
3045 //PerformCustomMainTest(file);
3046
3047 WritePoolTestResultHeader(file);
3048 PerformPoolTests(file);
3049 //PerformCustomPoolTest(file);
3050
3051 fclose(file);
3052
3053 wprintf(L"Done.\n");
3054}
3055
Adam Sawickif1a793c2018-03-13 15:42:22 +01003056#endif // #ifdef _WIN32