blob: 36e86bc3a7c9d086457ffeb8ad742657260be6bf [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 Sawicki0a607132018-08-24 11:18:41 +020010enum CONFIG_TYPE {
11 CONFIG_TYPE_MINIMUM,
12 CONFIG_TYPE_SMALL,
13 CONFIG_TYPE_AVERAGE,
14 CONFIG_TYPE_LARGE,
15 CONFIG_TYPE_MAXIMUM,
16 CONFIG_TYPE_COUNT
17};
18
19static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
20//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
21
Adam Sawickib8333fb2018-03-13 16:15:53 +010022enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
23
Adam Sawicki0a607132018-08-24 11:18:41 +020024static const wchar_t* FREE_ORDER_NAMES[] = {
25 L"FORWARD",
26 L"BACKWARD",
27 L"RANDOM",
28};
29
Adam Sawickib8333fb2018-03-13 16:15:53 +010030struct AllocationSize
31{
32 uint32_t Probability;
33 VkDeviceSize BufferSizeMin, BufferSizeMax;
34 uint32_t ImageSizeMin, ImageSizeMax;
35};
36
37struct Config
38{
39 uint32_t RandSeed;
40 VkDeviceSize BeginBytesToAllocate;
41 uint32_t AdditionalOperationCount;
42 VkDeviceSize MaxBytesToAllocate;
43 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
44 std::vector<AllocationSize> AllocationSizes;
45 uint32_t ThreadCount;
46 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
47 FREE_ORDER FreeOrder;
48};
49
50struct Result
51{
52 duration TotalTime;
53 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
54 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
55 VkDeviceSize TotalMemoryAllocated;
56 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
57};
58
59void TestDefragmentationSimple();
60void TestDefragmentationFull();
61
62struct PoolTestConfig
63{
64 uint32_t RandSeed;
65 uint32_t ThreadCount;
66 VkDeviceSize PoolSize;
67 uint32_t FrameCount;
68 uint32_t TotalItemCount;
69 // Range for number of items used in each frame.
70 uint32_t UsedItemCountMin, UsedItemCountMax;
71 // Percent of items to make unused, and possibly make some others used in each frame.
72 uint32_t ItemsToMakeUnusedPercent;
73 std::vector<AllocationSize> AllocationSizes;
74
75 VkDeviceSize CalcAvgResourceSize() const
76 {
77 uint32_t probabilitySum = 0;
78 VkDeviceSize sizeSum = 0;
79 for(size_t i = 0; i < AllocationSizes.size(); ++i)
80 {
81 const AllocationSize& allocSize = AllocationSizes[i];
82 if(allocSize.BufferSizeMax > 0)
83 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
84 else
85 {
86 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
87 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
88 }
89 probabilitySum += allocSize.Probability;
90 }
91 return sizeSum / probabilitySum;
92 }
93
94 bool UsesBuffers() const
95 {
96 for(size_t i = 0; i < AllocationSizes.size(); ++i)
97 if(AllocationSizes[i].BufferSizeMax > 0)
98 return true;
99 return false;
100 }
101
102 bool UsesImages() const
103 {
104 for(size_t i = 0; i < AllocationSizes.size(); ++i)
105 if(AllocationSizes[i].ImageSizeMax > 0)
106 return true;
107 return false;
108 }
109};
110
111struct PoolTestResult
112{
113 duration TotalTime;
114 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
115 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
116 size_t LostAllocationCount, LostAllocationTotalSize;
117 size_t FailedAllocationCount, FailedAllocationTotalSize;
118};
119
120static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
121
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200122static uint32_t g_FrameIndex = 0;
123
Adam Sawickib8333fb2018-03-13 16:15:53 +0100124struct BufferInfo
125{
126 VkBuffer Buffer = VK_NULL_HANDLE;
127 VmaAllocation Allocation = VK_NULL_HANDLE;
128};
129
130static void InitResult(Result& outResult)
131{
132 outResult.TotalTime = duration::zero();
133 outResult.AllocationTimeMin = duration::max();
134 outResult.AllocationTimeAvg = duration::zero();
135 outResult.AllocationTimeMax = duration::min();
136 outResult.DeallocationTimeMin = duration::max();
137 outResult.DeallocationTimeAvg = duration::zero();
138 outResult.DeallocationTimeMax = duration::min();
139 outResult.TotalMemoryAllocated = 0;
140 outResult.FreeRangeSizeAvg = 0;
141 outResult.FreeRangeSizeMax = 0;
142}
143
144class TimeRegisterObj
145{
146public:
147 TimeRegisterObj(duration& min, duration& sum, duration& max) :
148 m_Min(min),
149 m_Sum(sum),
150 m_Max(max),
151 m_TimeBeg(std::chrono::high_resolution_clock::now())
152 {
153 }
154
155 ~TimeRegisterObj()
156 {
157 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
158 m_Sum += d;
159 if(d < m_Min) m_Min = d;
160 if(d > m_Max) m_Max = d;
161 }
162
163private:
164 duration& m_Min;
165 duration& m_Sum;
166 duration& m_Max;
167 time_point m_TimeBeg;
168};
169
170struct PoolTestThreadResult
171{
172 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
173 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
174 size_t AllocationCount, DeallocationCount;
175 size_t LostAllocationCount, LostAllocationTotalSize;
176 size_t FailedAllocationCount, FailedAllocationTotalSize;
177};
178
179class AllocationTimeRegisterObj : public TimeRegisterObj
180{
181public:
182 AllocationTimeRegisterObj(Result& result) :
183 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
184 {
185 }
186};
187
188class DeallocationTimeRegisterObj : public TimeRegisterObj
189{
190public:
191 DeallocationTimeRegisterObj(Result& result) :
192 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
193 {
194 }
195};
196
197class PoolAllocationTimeRegisterObj : public TimeRegisterObj
198{
199public:
200 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
201 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
202 {
203 }
204};
205
206class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
207{
208public:
209 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
210 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
211 {
212 }
213};
214
215VkResult MainTest(Result& outResult, const Config& config)
216{
217 assert(config.ThreadCount > 0);
218
219 InitResult(outResult);
220
221 RandomNumberGenerator mainRand{config.RandSeed};
222
223 time_point timeBeg = std::chrono::high_resolution_clock::now();
224
225 std::atomic<size_t> allocationCount = 0;
226 VkResult res = VK_SUCCESS;
227
228 uint32_t memUsageProbabilitySum =
229 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
230 config.MemUsageProbability[2] + config.MemUsageProbability[3];
231 assert(memUsageProbabilitySum > 0);
232
233 uint32_t allocationSizeProbabilitySum = std::accumulate(
234 config.AllocationSizes.begin(),
235 config.AllocationSizes.end(),
236 0u,
237 [](uint32_t sum, const AllocationSize& allocSize) {
238 return sum + allocSize.Probability;
239 });
240
241 struct Allocation
242 {
243 VkBuffer Buffer;
244 VkImage Image;
245 VmaAllocation Alloc;
246 };
247
248 std::vector<Allocation> commonAllocations;
249 std::mutex commonAllocationsMutex;
250
251 auto Allocate = [&](
252 VkDeviceSize bufferSize,
253 const VkExtent2D imageExtent,
254 RandomNumberGenerator& localRand,
255 VkDeviceSize& totalAllocatedBytes,
256 std::vector<Allocation>& allocations) -> VkResult
257 {
258 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
259
260 uint32_t memUsageIndex = 0;
261 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
262 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
263 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
264
265 VmaAllocationCreateInfo memReq = {};
266 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
267
268 Allocation allocation = {};
269 VmaAllocationInfo allocationInfo;
270
271 // Buffer
272 if(bufferSize > 0)
273 {
274 assert(imageExtent.width == 0);
275 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
276 bufferInfo.size = bufferSize;
277 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
278
279 {
280 AllocationTimeRegisterObj timeRegisterObj{outResult};
281 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
282 }
283 }
284 // Image
285 else
286 {
287 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
288 imageInfo.imageType = VK_IMAGE_TYPE_2D;
289 imageInfo.extent.width = imageExtent.width;
290 imageInfo.extent.height = imageExtent.height;
291 imageInfo.extent.depth = 1;
292 imageInfo.mipLevels = 1;
293 imageInfo.arrayLayers = 1;
294 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
295 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
296 VK_IMAGE_TILING_OPTIMAL :
297 VK_IMAGE_TILING_LINEAR;
298 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
299 switch(memReq.usage)
300 {
301 case VMA_MEMORY_USAGE_GPU_ONLY:
302 switch(localRand.Generate() % 3)
303 {
304 case 0:
305 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
306 break;
307 case 1:
308 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
309 break;
310 case 2:
311 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
312 break;
313 }
314 break;
315 case VMA_MEMORY_USAGE_CPU_ONLY:
316 case VMA_MEMORY_USAGE_CPU_TO_GPU:
317 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
318 break;
319 case VMA_MEMORY_USAGE_GPU_TO_CPU:
320 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
321 break;
322 }
323 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
324 imageInfo.flags = 0;
325
326 {
327 AllocationTimeRegisterObj timeRegisterObj{outResult};
328 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
329 }
330 }
331
332 if(res == VK_SUCCESS)
333 {
334 ++allocationCount;
335 totalAllocatedBytes += allocationInfo.size;
336 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
337 if(useCommonAllocations)
338 {
339 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
340 commonAllocations.push_back(allocation);
341 }
342 else
343 allocations.push_back(allocation);
344 }
345 else
346 {
347 assert(0);
348 }
349 return res;
350 };
351
352 auto GetNextAllocationSize = [&](
353 VkDeviceSize& outBufSize,
354 VkExtent2D& outImageSize,
355 RandomNumberGenerator& localRand)
356 {
357 outBufSize = 0;
358 outImageSize = {0, 0};
359
360 uint32_t allocSizeIndex = 0;
361 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
362 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
363 r -= config.AllocationSizes[allocSizeIndex++].Probability;
364
365 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
366 if(allocSize.BufferSizeMax > 0)
367 {
368 assert(allocSize.ImageSizeMax == 0);
369 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
370 outBufSize = allocSize.BufferSizeMin;
371 else
372 {
373 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
374 outBufSize = outBufSize / 16 * 16;
375 }
376 }
377 else
378 {
379 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
380 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
381 else
382 {
383 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
384 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
385 }
386 }
387 };
388
389 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
390 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
391
392 auto ThreadProc = [&](uint32_t randSeed) -> void
393 {
394 RandomNumberGenerator threadRand(randSeed);
395 VkDeviceSize threadTotalAllocatedBytes = 0;
396 std::vector<Allocation> threadAllocations;
397 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
398 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
399 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
400
401 // BEGIN ALLOCATIONS
402 for(;;)
403 {
404 VkDeviceSize bufferSize = 0;
405 VkExtent2D imageExtent = {};
406 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
407 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
408 threadBeginBytesToAllocate)
409 {
410 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
411 break;
412 }
413 else
414 break;
415 }
416
417 // ADDITIONAL ALLOCATIONS AND FREES
418 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
419 {
420 VkDeviceSize bufferSize = 0;
421 VkExtent2D imageExtent = {};
422 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
423
424 // true = allocate, false = free
425 bool allocate = threadRand.Generate() % 2 != 0;
426
427 if(allocate)
428 {
429 if(threadTotalAllocatedBytes +
430 bufferSize +
431 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
432 threadMaxBytesToAllocate)
433 {
434 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
435 break;
436 }
437 }
438 else
439 {
440 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
441 if(useCommonAllocations)
442 {
443 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
444 if(!commonAllocations.empty())
445 {
446 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
447 VmaAllocationInfo allocationInfo;
448 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
449 if(threadTotalAllocatedBytes >= allocationInfo.size)
450 {
451 DeallocationTimeRegisterObj timeRegisterObj{outResult};
452 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
453 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
454 else
455 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
456 threadTotalAllocatedBytes -= allocationInfo.size;
457 commonAllocations.erase(commonAllocations.begin() + indexToFree);
458 }
459 }
460 }
461 else
462 {
463 if(!threadAllocations.empty())
464 {
465 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
466 VmaAllocationInfo allocationInfo;
467 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
468 if(threadTotalAllocatedBytes >= allocationInfo.size)
469 {
470 DeallocationTimeRegisterObj timeRegisterObj{outResult};
471 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
472 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
473 else
474 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
475 threadTotalAllocatedBytes -= allocationInfo.size;
476 threadAllocations.erase(threadAllocations.begin() + indexToFree);
477 }
478 }
479 }
480 }
481 }
482
483 ++numThreadsReachedMaxAllocations;
484
485 WaitForSingleObject(threadsFinishEvent, INFINITE);
486
487 // DEALLOCATION
488 while(!threadAllocations.empty())
489 {
490 size_t indexToFree = 0;
491 switch(config.FreeOrder)
492 {
493 case FREE_ORDER::FORWARD:
494 indexToFree = 0;
495 break;
496 case FREE_ORDER::BACKWARD:
497 indexToFree = threadAllocations.size() - 1;
498 break;
499 case FREE_ORDER::RANDOM:
500 indexToFree = mainRand.Generate() % threadAllocations.size();
501 break;
502 }
503
504 {
505 DeallocationTimeRegisterObj timeRegisterObj{outResult};
506 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
507 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
508 else
509 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
510 }
511 threadAllocations.erase(threadAllocations.begin() + indexToFree);
512 }
513 };
514
515 uint32_t threadRandSeed = mainRand.Generate();
516 std::vector<std::thread> bkgThreads;
517 for(size_t i = 0; i < config.ThreadCount; ++i)
518 {
519 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
520 }
521
522 // Wait for threads reached max allocations
523 while(numThreadsReachedMaxAllocations < config.ThreadCount)
524 Sleep(0);
525
526 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
527 VmaStats vmaStats = {};
528 vmaCalculateStats(g_hAllocator, &vmaStats);
529 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
530 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
531 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
532
533 // Signal threads to deallocate
534 SetEvent(threadsFinishEvent);
535
536 // Wait for threads finished
537 for(size_t i = 0; i < bkgThreads.size(); ++i)
538 bkgThreads[i].join();
539 bkgThreads.clear();
540
541 CloseHandle(threadsFinishEvent);
542
543 // Deallocate remaining common resources
544 while(!commonAllocations.empty())
545 {
546 size_t indexToFree = 0;
547 switch(config.FreeOrder)
548 {
549 case FREE_ORDER::FORWARD:
550 indexToFree = 0;
551 break;
552 case FREE_ORDER::BACKWARD:
553 indexToFree = commonAllocations.size() - 1;
554 break;
555 case FREE_ORDER::RANDOM:
556 indexToFree = mainRand.Generate() % commonAllocations.size();
557 break;
558 }
559
560 {
561 DeallocationTimeRegisterObj timeRegisterObj{outResult};
562 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
563 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
564 else
565 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
566 }
567 commonAllocations.erase(commonAllocations.begin() + indexToFree);
568 }
569
570 if(allocationCount)
571 {
572 outResult.AllocationTimeAvg /= allocationCount;
573 outResult.DeallocationTimeAvg /= allocationCount;
574 }
575
576 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
577
578 return res;
579}
580
Adam Sawickie44c6262018-06-15 14:30:39 +0200581static void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100582{
583 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200584 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100585 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200586 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100587}
588
589struct AllocInfo
590{
591 VmaAllocation m_Allocation;
592 VkBuffer m_Buffer;
593 VkImage m_Image;
594 uint32_t m_StartValue;
595 union
596 {
597 VkBufferCreateInfo m_BufferInfo;
598 VkImageCreateInfo m_ImageInfo;
599 };
600};
601
602static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
603{
604 outMemReq = {};
605 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
606 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
607}
608
609static void CreateBuffer(
610 VmaPool pool,
611 const VkBufferCreateInfo& bufCreateInfo,
612 bool persistentlyMapped,
613 AllocInfo& outAllocInfo)
614{
615 outAllocInfo = {};
616 outAllocInfo.m_BufferInfo = bufCreateInfo;
617
618 VmaAllocationCreateInfo allocCreateInfo = {};
619 allocCreateInfo.pool = pool;
620 if(persistentlyMapped)
621 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
622
623 VmaAllocationInfo vmaAllocInfo = {};
624 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
625
626 // Setup StartValue and fill.
627 {
628 outAllocInfo.m_StartValue = (uint32_t)rand();
629 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
630 assert((data != nullptr) == persistentlyMapped);
631 if(!persistentlyMapped)
632 {
633 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
634 }
635
636 uint32_t value = outAllocInfo.m_StartValue;
637 assert(bufCreateInfo.size % 4 == 0);
638 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
639 data[i] = value++;
640
641 if(!persistentlyMapped)
642 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
643 }
644}
645
646static void CreateAllocation(AllocInfo& outAllocation, VmaAllocator allocator)
647{
648 outAllocation.m_Allocation = nullptr;
649 outAllocation.m_Buffer = nullptr;
650 outAllocation.m_Image = nullptr;
651 outAllocation.m_StartValue = (uint32_t)rand();
652
653 VmaAllocationCreateInfo vmaMemReq;
654 GetMemReq(vmaMemReq);
655
656 VmaAllocationInfo allocInfo;
657
658 const bool isBuffer = true;//(rand() & 0x1) != 0;
659 const bool isLarge = (rand() % 16) == 0;
660 if(isBuffer)
661 {
662 const uint32_t bufferSize = isLarge ?
663 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
664 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
665
666 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
667 bufferInfo.size = bufferSize;
668 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
669
670 VkResult res = vmaCreateBuffer(allocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
671 outAllocation.m_BufferInfo = bufferInfo;
672 assert(res == VK_SUCCESS);
673 }
674 else
675 {
676 const uint32_t imageSizeX = isLarge ?
677 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
678 rand() % 1024 + 1; // 1 ... 1024
679 const uint32_t imageSizeY = isLarge ?
680 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
681 rand() % 1024 + 1; // 1 ... 1024
682
683 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
684 imageInfo.imageType = VK_IMAGE_TYPE_2D;
685 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
686 imageInfo.extent.width = imageSizeX;
687 imageInfo.extent.height = imageSizeY;
688 imageInfo.extent.depth = 1;
689 imageInfo.mipLevels = 1;
690 imageInfo.arrayLayers = 1;
691 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
692 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
693 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
694 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
695
696 VkResult res = vmaCreateImage(allocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
697 outAllocation.m_ImageInfo = imageInfo;
698 assert(res == VK_SUCCESS);
699 }
700
701 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
702 if(allocInfo.pMappedData == nullptr)
703 {
704 VkResult res = vmaMapMemory(allocator, outAllocation.m_Allocation, (void**)&data);
705 assert(res == VK_SUCCESS);
706 }
707
708 uint32_t value = outAllocation.m_StartValue;
709 assert(allocInfo.size % 4 == 0);
710 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
711 data[i] = value++;
712
713 if(allocInfo.pMappedData == nullptr)
714 vmaUnmapMemory(allocator, outAllocation.m_Allocation);
715}
716
717static void DestroyAllocation(const AllocInfo& allocation)
718{
719 if(allocation.m_Buffer)
720 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
721 else
722 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
723}
724
725static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
726{
727 for(size_t i = allocations.size(); i--; )
728 DestroyAllocation(allocations[i]);
729 allocations.clear();
730}
731
732static void ValidateAllocationData(const AllocInfo& allocation)
733{
734 VmaAllocationInfo allocInfo;
735 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
736
737 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
738 if(allocInfo.pMappedData == nullptr)
739 {
740 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
741 assert(res == VK_SUCCESS);
742 }
743
744 uint32_t value = allocation.m_StartValue;
745 bool ok = true;
746 size_t i;
747 assert(allocInfo.size % 4 == 0);
748 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
749 {
750 if(data[i] != value++)
751 {
752 ok = false;
753 break;
754 }
755 }
756 assert(ok);
757
758 if(allocInfo.pMappedData == nullptr)
759 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
760}
761
762static void RecreateAllocationResource(AllocInfo& allocation)
763{
764 VmaAllocationInfo allocInfo;
765 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
766
767 if(allocation.m_Buffer)
768 {
769 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, nullptr);
770
771 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, nullptr, &allocation.m_Buffer);
772 assert(res == VK_SUCCESS);
773
774 // Just to silence validation layer warnings.
775 VkMemoryRequirements vkMemReq;
776 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
777 assert(vkMemReq.size == allocation.m_BufferInfo.size);
778
779 res = vkBindBufferMemory(g_hDevice, allocation.m_Buffer, allocInfo.deviceMemory, allocInfo.offset);
780 assert(res == VK_SUCCESS);
781 }
782 else
783 {
784 vkDestroyImage(g_hDevice, allocation.m_Image, nullptr);
785
786 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, nullptr, &allocation.m_Image);
787 assert(res == VK_SUCCESS);
788
789 // Just to silence validation layer warnings.
790 VkMemoryRequirements vkMemReq;
791 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
792
793 res = vkBindImageMemory(g_hDevice, allocation.m_Image, allocInfo.deviceMemory, allocInfo.offset);
794 assert(res == VK_SUCCESS);
795 }
796}
797
798static void Defragment(AllocInfo* allocs, size_t allocCount,
799 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
800 VmaDefragmentationStats* defragmentationStats = nullptr)
801{
802 std::vector<VmaAllocation> vmaAllocs(allocCount);
803 for(size_t i = 0; i < allocCount; ++i)
804 vmaAllocs[i] = allocs[i].m_Allocation;
805
806 std::vector<VkBool32> allocChanged(allocCount);
807
808 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
809 defragmentationInfo, defragmentationStats) );
810
811 for(size_t i = 0; i < allocCount; ++i)
812 {
813 if(allocChanged[i])
814 {
815 RecreateAllocationResource(allocs[i]);
816 }
817 }
818}
819
820static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
821{
822 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
823 ValidateAllocationData(allocInfo);
824 });
825}
826
827void TestDefragmentationSimple()
828{
829 wprintf(L"Test defragmentation simple\n");
830
831 RandomNumberGenerator rand(667);
832
833 const VkDeviceSize BUF_SIZE = 0x10000;
834 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
835
836 const VkDeviceSize MIN_BUF_SIZE = 32;
837 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
838 auto RandomBufSize = [&]() -> VkDeviceSize {
839 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
840 };
841
842 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
843 bufCreateInfo.size = BUF_SIZE;
844 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
845
846 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
847 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
848
849 uint32_t memTypeIndex = UINT32_MAX;
850 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
851
852 VmaPoolCreateInfo poolCreateInfo = {};
853 poolCreateInfo.blockSize = BLOCK_SIZE;
854 poolCreateInfo.memoryTypeIndex = memTypeIndex;
855
856 VmaPool pool;
857 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
858
859 std::vector<AllocInfo> allocations;
860
861 // persistentlyMappedOption = 0 - not persistently mapped.
862 // persistentlyMappedOption = 1 - persistently mapped.
863 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
864 {
865 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
866 const bool persistentlyMapped = persistentlyMappedOption != 0;
867
868 // # Test 1
869 // Buffers of fixed size.
870 // Fill 2 blocks. Remove odd buffers. Defragment everything.
871 // Expected result: at least 1 block freed.
872 {
873 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
874 {
875 AllocInfo allocInfo;
876 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
877 allocations.push_back(allocInfo);
878 }
879
880 for(size_t i = 1; i < allocations.size(); ++i)
881 {
882 DestroyAllocation(allocations[i]);
883 allocations.erase(allocations.begin() + i);
884 }
885
886 VmaDefragmentationStats defragStats;
887 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
888 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
889 assert(defragStats.deviceMemoryBlocksFreed >= 1);
890
891 ValidateAllocationsData(allocations.data(), allocations.size());
892
893 DestroyAllAllocations(allocations);
894 }
895
896 // # Test 2
897 // Buffers of fixed size.
898 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
899 // Expected result: Each of 4 interations makes some progress.
900 {
901 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
902 {
903 AllocInfo allocInfo;
904 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
905 allocations.push_back(allocInfo);
906 }
907
908 for(size_t i = 1; i < allocations.size(); ++i)
909 {
910 DestroyAllocation(allocations[i]);
911 allocations.erase(allocations.begin() + i);
912 }
913
914 VmaDefragmentationInfo defragInfo = {};
915 defragInfo.maxAllocationsToMove = 1;
916 defragInfo.maxBytesToMove = BUF_SIZE;
917
918 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
919 {
920 VmaDefragmentationStats defragStats;
921 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
922 assert(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
923 }
924
925 ValidateAllocationsData(allocations.data(), allocations.size());
926
927 DestroyAllAllocations(allocations);
928 }
929
930 // # Test 3
931 // Buffers of variable size.
932 // Create a number of buffers. Remove some percent of them.
933 // Defragment while having some percent of them unmovable.
934 // Expected result: Just simple validation.
935 {
936 for(size_t i = 0; i < 100; ++i)
937 {
938 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
939 localBufCreateInfo.size = RandomBufSize();
940
941 AllocInfo allocInfo;
942 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
943 allocations.push_back(allocInfo);
944 }
945
946 const uint32_t percentToDelete = 60;
947 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
948 for(size_t i = 0; i < numberToDelete; ++i)
949 {
950 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
951 DestroyAllocation(allocations[indexToDelete]);
952 allocations.erase(allocations.begin() + indexToDelete);
953 }
954
955 // Non-movable allocations will be at the beginning of allocations array.
956 const uint32_t percentNonMovable = 20;
957 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
958 for(size_t i = 0; i < numberNonMovable; ++i)
959 {
960 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
961 if(indexNonMovable != i)
962 std::swap(allocations[i], allocations[indexNonMovable]);
963 }
964
965 VmaDefragmentationStats defragStats;
966 Defragment(
967 allocations.data() + numberNonMovable,
968 allocations.size() - numberNonMovable,
969 nullptr, &defragStats);
970
971 ValidateAllocationsData(allocations.data(), allocations.size());
972
973 DestroyAllAllocations(allocations);
974 }
975 }
976
977 vmaDestroyPool(g_hAllocator, pool);
978}
979
980void TestDefragmentationFull()
981{
982 std::vector<AllocInfo> allocations;
983
984 // Create initial allocations.
985 for(size_t i = 0; i < 400; ++i)
986 {
987 AllocInfo allocation;
988 CreateAllocation(allocation, g_hAllocator);
989 allocations.push_back(allocation);
990 }
991
992 // Delete random allocations
993 const size_t allocationsToDeletePercent = 80;
994 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
995 for(size_t i = 0; i < allocationsToDelete; ++i)
996 {
997 size_t index = (size_t)rand() % allocations.size();
998 DestroyAllocation(allocations[index]);
999 allocations.erase(allocations.begin() + index);
1000 }
1001
1002 for(size_t i = 0; i < allocations.size(); ++i)
1003 ValidateAllocationData(allocations[i]);
1004
Adam Sawickie44c6262018-06-15 14:30:39 +02001005 SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001006
1007 {
1008 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1009 for(size_t i = 0; i < allocations.size(); ++i)
1010 vmaAllocations[i] = allocations[i].m_Allocation;
1011
1012 const size_t nonMovablePercent = 0;
1013 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1014 for(size_t i = 0; i < nonMovableCount; ++i)
1015 {
1016 size_t index = (size_t)rand() % vmaAllocations.size();
1017 vmaAllocations.erase(vmaAllocations.begin() + index);
1018 }
1019
1020 const uint32_t defragCount = 1;
1021 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1022 {
1023 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1024
1025 VmaDefragmentationInfo defragmentationInfo;
1026 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1027 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1028
1029 wprintf(L"Defragmentation #%u\n", defragIndex);
1030
1031 time_point begTime = std::chrono::high_resolution_clock::now();
1032
1033 VmaDefragmentationStats stats;
1034 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
1035 assert(res >= 0);
1036
1037 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1038
1039 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1040 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1041 wprintf(L"Time: %.2f s\n", defragmentDuration);
1042
1043 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1044 {
1045 if(allocationsChanged[i])
1046 {
1047 RecreateAllocationResource(allocations[i]);
1048 }
1049 }
1050
1051 for(size_t i = 0; i < allocations.size(); ++i)
1052 ValidateAllocationData(allocations[i]);
1053
1054 wchar_t fileName[MAX_PATH];
1055 swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
Adam Sawickie44c6262018-06-15 14:30:39 +02001056 SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001057 }
1058 }
1059
1060 // Destroy all remaining allocations.
1061 DestroyAllAllocations(allocations);
1062}
1063
1064static void TestUserData()
1065{
1066 VkResult res;
1067
1068 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1069 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1070 bufCreateInfo.size = 0x10000;
1071
1072 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
1073 {
1074 // Opaque pointer
1075 {
1076
1077 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
1078 void* pointerToSomething = &res;
1079
1080 VmaAllocationCreateInfo allocCreateInfo = {};
1081 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1082 allocCreateInfo.pUserData = numberAsPointer;
1083 if(testIndex == 1)
1084 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1085
1086 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1087 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1088 assert(res == VK_SUCCESS);
1089 assert(allocInfo.pUserData = numberAsPointer);
1090
1091 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1092 assert(allocInfo.pUserData == numberAsPointer);
1093
1094 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
1095 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1096 assert(allocInfo.pUserData == pointerToSomething);
1097
1098 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1099 }
1100
1101 // String
1102 {
1103 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
1104 const char* name2 = "2";
1105 const size_t name1Len = strlen(name1);
1106
1107 char* name1Buf = new char[name1Len + 1];
1108 strcpy_s(name1Buf, name1Len + 1, name1);
1109
1110 VmaAllocationCreateInfo allocCreateInfo = {};
1111 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1112 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1113 allocCreateInfo.pUserData = name1Buf;
1114 if(testIndex == 1)
1115 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1116
1117 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1118 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1119 assert(res == VK_SUCCESS);
1120 assert(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
1121 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1122
1123 delete[] name1Buf;
1124
1125 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1126 assert(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
1127
1128 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
1129 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1130 assert(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
1131
1132 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
1133 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1134 assert(allocInfo.pUserData == nullptr);
1135
1136 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1137 }
1138 }
1139}
1140
1141static void TestMemoryRequirements()
1142{
1143 VkResult res;
1144 VkBuffer buf;
1145 VmaAllocation alloc;
1146 VmaAllocationInfo allocInfo;
1147
1148 const VkPhysicalDeviceMemoryProperties* memProps;
1149 vmaGetMemoryProperties(g_hAllocator, &memProps);
1150
1151 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1152 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1153 bufInfo.size = 128;
1154
1155 VmaAllocationCreateInfo allocCreateInfo = {};
1156
1157 // No requirements.
1158 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1159 assert(res == VK_SUCCESS);
1160 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1161
1162 // Usage.
1163 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1164 allocCreateInfo.requiredFlags = 0;
1165 allocCreateInfo.preferredFlags = 0;
1166 allocCreateInfo.memoryTypeBits = UINT32_MAX;
1167
1168 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1169 assert(res == VK_SUCCESS);
1170 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1171 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1172
1173 // Required flags, preferred flags.
1174 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
1175 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1176 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1177 allocCreateInfo.memoryTypeBits = 0;
1178
1179 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1180 assert(res == VK_SUCCESS);
1181 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1182 assert(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
1183 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1184
1185 // memoryTypeBits.
1186 const uint32_t memType = allocInfo.memoryType;
1187 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1188 allocCreateInfo.requiredFlags = 0;
1189 allocCreateInfo.preferredFlags = 0;
1190 allocCreateInfo.memoryTypeBits = 1u << memType;
1191
1192 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1193 assert(res == VK_SUCCESS);
1194 assert(allocInfo.memoryType == memType);
1195 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1196
1197}
1198
1199static void TestBasics()
1200{
1201 VkResult res;
1202
1203 TestMemoryRequirements();
1204
1205 // Lost allocation
1206 {
1207 VmaAllocation alloc = VK_NULL_HANDLE;
1208 vmaCreateLostAllocation(g_hAllocator, &alloc);
1209 assert(alloc != VK_NULL_HANDLE);
1210
1211 VmaAllocationInfo allocInfo;
1212 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
1213 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1214 assert(allocInfo.size == 0);
1215
1216 vmaFreeMemory(g_hAllocator, alloc);
1217 }
1218
1219 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
1220 {
1221 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1222 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1223 bufCreateInfo.size = 128;
1224
1225 VmaAllocationCreateInfo allocCreateInfo = {};
1226 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1227 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1228
1229 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
1230 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1231 assert(res == VK_SUCCESS);
1232
1233 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1234
1235 // Same with OWN_MEMORY.
1236 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1237
1238 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
1239 assert(res == VK_SUCCESS);
1240
1241 vmaDestroyBuffer(g_hAllocator, buf, alloc);
1242 }
1243
1244 TestUserData();
1245}
1246
1247void TestHeapSizeLimit()
1248{
1249 const VkDeviceSize HEAP_SIZE_LIMIT = 1ull * 1024 * 1024 * 1024; // 1 GB
1250 const VkDeviceSize BLOCK_SIZE = 128ull * 1024 * 1024; // 128 MB
1251
1252 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
1253 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
1254 {
1255 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
1256 }
1257
1258 VmaAllocatorCreateInfo allocatorCreateInfo = {};
1259 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
1260 allocatorCreateInfo.device = g_hDevice;
1261 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
1262
1263 VmaAllocator hAllocator;
1264 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
1265 assert(res == VK_SUCCESS);
1266
1267 struct Item
1268 {
1269 VkBuffer hBuf;
1270 VmaAllocation hAlloc;
1271 };
1272 std::vector<Item> items;
1273
1274 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1275 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1276
1277 // 1. Allocate two blocks of Own Memory, half the size of BLOCK_SIZE.
1278 VmaAllocationInfo ownAllocInfo;
1279 {
1280 VmaAllocationCreateInfo allocCreateInfo = {};
1281 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1282 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
1283
1284 bufCreateInfo.size = BLOCK_SIZE / 2;
1285
1286 for(size_t i = 0; i < 2; ++i)
1287 {
1288 Item item;
1289 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &ownAllocInfo);
1290 assert(res == VK_SUCCESS);
1291 items.push_back(item);
1292 }
1293 }
1294
1295 // Create pool to make sure allocations must be out of this memory type.
1296 VmaPoolCreateInfo poolCreateInfo = {};
1297 poolCreateInfo.memoryTypeIndex = ownAllocInfo.memoryType;
1298 poolCreateInfo.blockSize = BLOCK_SIZE;
1299
1300 VmaPool hPool;
1301 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
1302 assert(res == VK_SUCCESS);
1303
1304 // 2. Allocate normal buffers from all the remaining memory.
1305 {
1306 VmaAllocationCreateInfo allocCreateInfo = {};
1307 allocCreateInfo.pool = hPool;
1308
1309 bufCreateInfo.size = BLOCK_SIZE / 2;
1310
1311 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
1312 for(size_t i = 0; i < bufCount; ++i)
1313 {
1314 Item item;
1315 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
1316 assert(res == VK_SUCCESS);
1317 items.push_back(item);
1318 }
1319 }
1320
1321 // 3. Allocation of one more (even small) buffer should fail.
1322 {
1323 VmaAllocationCreateInfo allocCreateInfo = {};
1324 allocCreateInfo.pool = hPool;
1325
1326 bufCreateInfo.size = 128;
1327
1328 VkBuffer hBuf;
1329 VmaAllocation hAlloc;
1330 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
1331 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1332 }
1333
1334 // Destroy everything.
1335 for(size_t i = items.size(); i--; )
1336 {
1337 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
1338 }
1339
1340 vmaDestroyPool(hAllocator, hPool);
1341
1342 vmaDestroyAllocator(hAllocator);
1343}
1344
Adam Sawicki212a4a62018-06-14 15:44:45 +02001345#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02001346static void TestDebugMargin()
1347{
1348 if(VMA_DEBUG_MARGIN == 0)
1349 {
1350 return;
1351 }
1352
1353 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02001354 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02001355
1356 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02001357 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02001358
1359 // Create few buffers of different size.
1360 const size_t BUF_COUNT = 10;
1361 BufferInfo buffers[BUF_COUNT];
1362 VmaAllocationInfo allocInfo[BUF_COUNT];
1363 for(size_t i = 0; i < 10; ++i)
1364 {
1365 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02001366 // Last one will be mapped.
1367 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02001368
1369 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
1370 assert(res == VK_SUCCESS);
1371 // Margin is preserved also at the beginning of a block.
1372 assert(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02001373
1374 if(i == BUF_COUNT - 1)
1375 {
1376 // Fill with data.
1377 assert(allocInfo[i].pMappedData != nullptr);
1378 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
1379 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
1380 }
Adam Sawicki73b16652018-06-11 16:39:25 +02001381 }
1382
1383 // Check if their offsets preserve margin between them.
1384 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
1385 {
1386 if(lhs.deviceMemory != rhs.deviceMemory)
1387 {
1388 return lhs.deviceMemory < rhs.deviceMemory;
1389 }
1390 return lhs.offset < rhs.offset;
1391 });
1392 for(size_t i = 1; i < BUF_COUNT; ++i)
1393 {
1394 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
1395 {
1396 assert(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
1397 }
1398 }
1399
Adam Sawicki212a4a62018-06-14 15:44:45 +02001400 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
1401 assert(res == VK_SUCCESS);
1402
Adam Sawicki73b16652018-06-11 16:39:25 +02001403 // Destroy all buffers.
1404 for(size_t i = BUF_COUNT; i--; )
1405 {
1406 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
1407 }
1408}
Adam Sawicki212a4a62018-06-14 15:44:45 +02001409#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02001410
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001411static void TestLinearAllocator()
1412{
1413 wprintf(L"Test linear allocator\n");
1414
1415 RandomNumberGenerator rand{645332};
1416
1417 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1418 sampleBufCreateInfo.size = 1024; // Whatever.
1419 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1420
1421 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1422 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1423
1424 VmaPoolCreateInfo poolCreateInfo = {};
1425 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1426 assert(res == VK_SUCCESS);
1427
Adam Sawickiee082772018-06-20 17:45:49 +02001428 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001429 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1430 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1431
1432 VmaPool pool = nullptr;
1433 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1434 assert(res == VK_SUCCESS);
1435
1436 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1437
1438 VmaAllocationCreateInfo allocCreateInfo = {};
1439 allocCreateInfo.pool = pool;
1440
1441 constexpr size_t maxBufCount = 100;
1442 std::vector<BufferInfo> bufInfo;
1443
1444 constexpr VkDeviceSize bufSizeMin = 16;
1445 constexpr VkDeviceSize bufSizeMax = 1024;
1446 VmaAllocationInfo allocInfo;
1447 VkDeviceSize prevOffset = 0;
1448
1449 // Test one-time free.
1450 for(size_t i = 0; i < 2; ++i)
1451 {
1452 // Allocate number of buffers of varying size that surely fit into this block.
1453 VkDeviceSize bufSumSize = 0;
1454 for(size_t i = 0; i < maxBufCount; ++i)
1455 {
1456 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1457 BufferInfo newBufInfo;
1458 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1459 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1460 assert(res == VK_SUCCESS);
1461 assert(i == 0 || allocInfo.offset > prevOffset);
1462 bufInfo.push_back(newBufInfo);
1463 prevOffset = allocInfo.offset;
1464 bufSumSize += bufCreateInfo.size;
1465 }
1466
1467 // Validate pool stats.
1468 VmaPoolStats stats;
1469 vmaGetPoolStats(g_hAllocator, pool, &stats);
1470 assert(stats.size == poolCreateInfo.blockSize);
1471 assert(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
1472 assert(stats.allocationCount == bufInfo.size());
1473
1474 // Destroy the buffers in random order.
1475 while(!bufInfo.empty())
1476 {
1477 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1478 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1479 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1480 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1481 }
1482 }
1483
1484 // Test stack.
1485 {
1486 // Allocate number of buffers of varying size that surely fit into this block.
1487 for(size_t i = 0; i < maxBufCount; ++i)
1488 {
1489 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1490 BufferInfo newBufInfo;
1491 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1492 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1493 assert(res == VK_SUCCESS);
1494 assert(i == 0 || allocInfo.offset > prevOffset);
1495 bufInfo.push_back(newBufInfo);
1496 prevOffset = allocInfo.offset;
1497 }
1498
1499 // Destroy few buffers from top of the stack.
1500 for(size_t i = 0; i < maxBufCount / 5; ++i)
1501 {
1502 const BufferInfo& currBufInfo = bufInfo.back();
1503 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1504 bufInfo.pop_back();
1505 }
1506
1507 // Create some more
1508 for(size_t i = 0; i < maxBufCount / 5; ++i)
1509 {
1510 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1511 BufferInfo newBufInfo;
1512 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1513 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1514 assert(res == VK_SUCCESS);
1515 assert(i == 0 || allocInfo.offset > prevOffset);
1516 bufInfo.push_back(newBufInfo);
1517 prevOffset = allocInfo.offset;
1518 }
1519
1520 // Destroy the buffers in reverse order.
1521 while(!bufInfo.empty())
1522 {
1523 const BufferInfo& currBufInfo = bufInfo.back();
1524 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1525 bufInfo.pop_back();
1526 }
1527 }
1528
Adam Sawickiee082772018-06-20 17:45:49 +02001529 // Test ring buffer.
1530 {
1531 // Allocate number of buffers that surely fit into this block.
1532 bufCreateInfo.size = bufSizeMax;
1533 for(size_t i = 0; i < maxBufCount; ++i)
1534 {
1535 BufferInfo newBufInfo;
1536 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1537 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1538 assert(res == VK_SUCCESS);
1539 assert(i == 0 || allocInfo.offset > prevOffset);
1540 bufInfo.push_back(newBufInfo);
1541 prevOffset = allocInfo.offset;
1542 }
1543
1544 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
1545 const size_t buffersPerIter = maxBufCount / 10 - 1;
1546 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
1547 for(size_t iter = 0; iter < iterCount; ++iter)
1548 {
1549 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1550 {
1551 const BufferInfo& currBufInfo = bufInfo.front();
1552 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1553 bufInfo.erase(bufInfo.begin());
1554 }
1555 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
1556 {
1557 BufferInfo newBufInfo;
1558 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1559 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1560 assert(res == VK_SUCCESS);
1561 bufInfo.push_back(newBufInfo);
1562 }
1563 }
1564
1565 // Allocate buffers until we reach out-of-memory.
1566 uint32_t debugIndex = 0;
1567 while(res == VK_SUCCESS)
1568 {
1569 BufferInfo newBufInfo;
1570 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1571 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1572 if(res == VK_SUCCESS)
1573 {
1574 bufInfo.push_back(newBufInfo);
1575 }
1576 else
1577 {
1578 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
1579 }
1580 ++debugIndex;
1581 }
1582
1583 // Destroy the buffers in random order.
1584 while(!bufInfo.empty())
1585 {
1586 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1587 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1588 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1589 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1590 }
1591 }
1592
Adam Sawicki680b2252018-08-22 14:47:32 +02001593 // Test double stack.
1594 {
1595 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
1596 VkDeviceSize prevOffsetLower = 0;
1597 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
1598 for(size_t i = 0; i < maxBufCount; ++i)
1599 {
1600 const bool upperAddress = (i % 2) != 0;
1601 if(upperAddress)
1602 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1603 else
1604 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1605 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1606 BufferInfo newBufInfo;
1607 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1608 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1609 assert(res == VK_SUCCESS);
1610 if(upperAddress)
1611 {
1612 assert(allocInfo.offset < prevOffsetUpper);
1613 prevOffsetUpper = allocInfo.offset;
1614 }
1615 else
1616 {
1617 assert(allocInfo.offset >= prevOffsetLower);
1618 prevOffsetLower = allocInfo.offset;
1619 }
1620 assert(prevOffsetLower < prevOffsetUpper);
1621 bufInfo.push_back(newBufInfo);
1622 }
1623
1624 // Destroy few buffers from top of the stack.
1625 for(size_t i = 0; i < maxBufCount / 5; ++i)
1626 {
1627 const BufferInfo& currBufInfo = bufInfo.back();
1628 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1629 bufInfo.pop_back();
1630 }
1631
1632 // Create some more
1633 for(size_t i = 0; i < maxBufCount / 5; ++i)
1634 {
1635 const bool upperAddress = (i % 2) != 0;
1636 if(upperAddress)
1637 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1638 else
1639 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1640 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1641 BufferInfo newBufInfo;
1642 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1643 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1644 assert(res == VK_SUCCESS);
1645 bufInfo.push_back(newBufInfo);
1646 }
1647
1648 // Destroy the buffers in reverse order.
1649 while(!bufInfo.empty())
1650 {
1651 const BufferInfo& currBufInfo = bufInfo.back();
1652 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1653 bufInfo.pop_back();
1654 }
1655
1656 // Create buffers on both sides until we reach out of memory.
1657 prevOffsetLower = 0;
1658 prevOffsetUpper = poolCreateInfo.blockSize;
1659 res = VK_SUCCESS;
1660 for(size_t i = 0; res == VK_SUCCESS; ++i)
1661 {
1662 const bool upperAddress = (i % 2) != 0;
1663 if(upperAddress)
1664 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1665 else
1666 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1667 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1668 BufferInfo newBufInfo;
1669 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1670 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1671 if(res == VK_SUCCESS)
1672 {
1673 if(upperAddress)
1674 {
1675 assert(allocInfo.offset < prevOffsetUpper);
1676 prevOffsetUpper = allocInfo.offset;
1677 }
1678 else
1679 {
1680 assert(allocInfo.offset >= prevOffsetLower);
1681 prevOffsetLower = allocInfo.offset;
1682 }
1683 assert(prevOffsetLower < prevOffsetUpper);
1684 bufInfo.push_back(newBufInfo);
1685 }
1686 }
1687
1688 // Destroy the buffers in random order.
1689 while(!bufInfo.empty())
1690 {
1691 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1692 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1693 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1694 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1695 }
1696
1697 // Create buffers on upper side only, constant size, until we reach out of memory.
1698 prevOffsetUpper = poolCreateInfo.blockSize;
1699 res = VK_SUCCESS;
1700 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1701 bufCreateInfo.size = bufSizeMax;
1702 for(size_t i = 0; res == VK_SUCCESS; ++i)
1703 {
1704 BufferInfo newBufInfo;
1705 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1706 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1707 if(res == VK_SUCCESS)
1708 {
1709 assert(allocInfo.offset < prevOffsetUpper);
1710 prevOffsetUpper = allocInfo.offset;
1711 bufInfo.push_back(newBufInfo);
1712 }
1713 }
1714
1715 // Destroy the buffers in reverse order.
1716 while(!bufInfo.empty())
1717 {
1718 const BufferInfo& currBufInfo = bufInfo.back();
1719 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1720 bufInfo.pop_back();
1721 }
1722 }
1723
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001724 // Test ring buffer with lost allocations.
1725 {
1726 // Allocate number of buffers until pool is full.
1727 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
1728 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
1729 res = VK_SUCCESS;
1730 for(size_t i = 0; res == VK_SUCCESS; ++i)
1731 {
1732 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1733
1734 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1735
1736 BufferInfo newBufInfo;
1737 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1738 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1739 if(res == VK_SUCCESS)
1740 bufInfo.push_back(newBufInfo);
1741 }
1742
1743 // Free first half of it.
1744 {
1745 const size_t buffersToDelete = bufInfo.size() / 2;
1746 for(size_t i = 0; i < buffersToDelete; ++i)
1747 {
1748 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1749 }
1750 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
1751 }
1752
1753 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001754 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001755 res = VK_SUCCESS;
1756 for(size_t i = 0; res == VK_SUCCESS; ++i)
1757 {
1758 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1759
1760 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1761
1762 BufferInfo newBufInfo;
1763 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1764 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1765 if(res == VK_SUCCESS)
1766 bufInfo.push_back(newBufInfo);
1767 }
1768
1769 VkDeviceSize firstNewOffset;
1770 {
1771 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1772
1773 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
1774 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1775 bufCreateInfo.size = bufSizeMax;
1776
1777 BufferInfo newBufInfo;
1778 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1779 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1780 assert(res == VK_SUCCESS);
1781 bufInfo.push_back(newBufInfo);
1782 firstNewOffset = allocInfo.offset;
1783
1784 // Make sure at least one buffer from the beginning became lost.
1785 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
1786 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
1787 }
1788
1789 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
1790 size_t newCount = 1;
1791 for(;;)
1792 {
1793 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1794
1795 bufCreateInfo.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
1796
1797 BufferInfo newBufInfo;
1798 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1799 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1800 assert(res == VK_SUCCESS);
1801 bufInfo.push_back(newBufInfo);
1802 ++newCount;
1803 if(allocInfo.offset < firstNewOffset)
1804 break;
1805 }
1806
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02001807 // Delete buffers that are lost.
1808 for(size_t i = bufInfo.size(); i--; )
1809 {
1810 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1811 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1812 {
1813 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1814 bufInfo.erase(bufInfo.begin() + i);
1815 }
1816 }
1817
1818 // Test vmaMakePoolAllocationsLost
1819 {
1820 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
1821
1822 size_t lostAllocCount = SIZE_MAX;
1823 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
1824 assert(lostAllocCount > 0);
1825
1826 size_t realLostAllocCount = 0;
1827 for(size_t i = 0; i < bufInfo.size(); ++i)
1828 {
1829 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
1830 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
1831 ++realLostAllocCount;
1832 }
1833 assert(realLostAllocCount == lostAllocCount);
1834 }
1835
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02001836 // Destroy all the buffers in forward order.
1837 for(size_t i = 0; i < bufInfo.size(); ++i)
1838 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
1839 bufInfo.clear();
1840 }
1841
Adam Sawickif799c4f2018-08-23 10:40:30 +02001842 // Try to create pool with maxBlockCount higher than 1. It should fail.
1843 {
1844 VmaPoolCreateInfo altPoolCreateInfo = poolCreateInfo;
1845 altPoolCreateInfo.maxBlockCount = 2;
1846
1847 VmaPool altPool = nullptr;
1848 res = vmaCreatePool(g_hAllocator, &altPoolCreateInfo, &altPool);
1849 assert(res != VK_SUCCESS);
1850 }
1851
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001852 vmaDestroyPool(g_hAllocator, pool);
1853}
1854
Adam Sawickifd11d752018-08-22 15:02:10 +02001855static void ManuallyTestLinearAllocator()
1856{
1857 VmaStats origStats;
1858 vmaCalculateStats(g_hAllocator, &origStats);
1859
1860 wprintf(L"Manually test linear allocator\n");
1861
1862 RandomNumberGenerator rand{645332};
1863
1864 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1865 sampleBufCreateInfo.size = 1024; // Whatever.
1866 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1867
1868 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1869 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1870
1871 VmaPoolCreateInfo poolCreateInfo = {};
1872 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1873 assert(res == VK_SUCCESS);
1874
1875 poolCreateInfo.blockSize = 10 * 1024;
1876 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1877 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1878
1879 VmaPool pool = nullptr;
1880 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1881 assert(res == VK_SUCCESS);
1882
1883 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1884
1885 VmaAllocationCreateInfo allocCreateInfo = {};
1886 allocCreateInfo.pool = pool;
1887
1888 std::vector<BufferInfo> bufInfo;
1889 VmaAllocationInfo allocInfo;
1890 BufferInfo newBufInfo;
1891
1892 // Test double stack.
1893 {
1894 /*
1895 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
1896 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
1897
1898 Totally:
1899 1 block allocated
1900 10240 Vulkan bytes
1901 6 new allocations
1902 2256 bytes in allocations
1903 */
1904
1905 bufCreateInfo.size = 32;
1906 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1907 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1908 assert(res == VK_SUCCESS);
1909 bufInfo.push_back(newBufInfo);
1910
1911 bufCreateInfo.size = 1024;
1912 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1913 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1914 assert(res == VK_SUCCESS);
1915 bufInfo.push_back(newBufInfo);
1916
1917 bufCreateInfo.size = 32;
1918 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1919 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1920 assert(res == VK_SUCCESS);
1921 bufInfo.push_back(newBufInfo);
1922
1923 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
1924
1925 bufCreateInfo.size = 128;
1926 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1927 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1928 assert(res == VK_SUCCESS);
1929 bufInfo.push_back(newBufInfo);
1930
1931 bufCreateInfo.size = 1024;
1932 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1933 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1934 assert(res == VK_SUCCESS);
1935 bufInfo.push_back(newBufInfo);
1936
1937 bufCreateInfo.size = 16;
1938 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1939 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1940 assert(res == VK_SUCCESS);
1941 bufInfo.push_back(newBufInfo);
1942
1943 VmaStats currStats;
1944 vmaCalculateStats(g_hAllocator, &currStats);
1945 VmaPoolStats poolStats;
1946 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1947
1948 char* statsStr = nullptr;
1949 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
1950
1951 // PUT BREAKPOINT HERE TO CHECK.
1952 // Inspect: currStats versus origStats, poolStats, statsStr.
1953 int I = 0;
1954
1955 vmaFreeStatsString(g_hAllocator, statsStr);
1956
1957 // Destroy the buffers in reverse order.
1958 while(!bufInfo.empty())
1959 {
1960 const BufferInfo& currBufInfo = bufInfo.back();
1961 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1962 bufInfo.pop_back();
1963 }
1964 }
1965
1966 vmaDestroyPool(g_hAllocator, pool);
1967}
1968
Adam Sawicki0a607132018-08-24 11:18:41 +02001969static void BenchmarkLinearAllocatorCase(bool linear, bool empty, FREE_ORDER freeOrder)
1970{
1971 RandomNumberGenerator rand{16223};
1972
1973 const VkDeviceSize bufSizeMin = 32;
1974 const VkDeviceSize bufSizeMax = 1024;
1975 const size_t maxBufCapacity = 10000;
1976 const uint32_t iterationCount = 10;
1977
1978 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1979 sampleBufCreateInfo.size = bufSizeMax;
1980 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1981
1982 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1983 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1984
1985 VmaPoolCreateInfo poolCreateInfo = {};
1986 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1987 assert(res == VK_SUCCESS);
1988
1989 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
1990 if(linear)
1991 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1992 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1993
1994 VmaPool pool = nullptr;
1995 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1996 assert(res == VK_SUCCESS);
1997
1998 // Buffer created just to get memory requirements. Never bound to any memory.
1999 VkBuffer dummyBuffer = VK_NULL_HANDLE;
2000 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
2001 assert(res == VK_SUCCESS && dummyBuffer);
2002
2003 VkMemoryRequirements memReq = {};
2004 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2005
2006 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2007
2008 VmaAllocationCreateInfo allocCreateInfo = {};
2009 allocCreateInfo.pool = pool;
2010
2011 VmaAllocation alloc;
2012 std::vector<VmaAllocation> baseAllocations;
2013
2014 if(!empty)
2015 {
2016 // Make allocations up to half of pool size.
2017 VkDeviceSize totalSize = 0;
2018 while(totalSize < poolCreateInfo.blockSize / 2)
2019 {
2020 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2021 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2022 assert(res == VK_SUCCESS);
2023 baseAllocations.push_back(alloc);
2024 totalSize += memReq.size;
2025 }
2026
2027 // Delete half of them, choose randomly.
2028 size_t allocsToDelete = baseAllocations.size() / 2;
2029 for(size_t i = 0; i < allocsToDelete; ++i)
2030 {
2031 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2032 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2033 baseAllocations.erase(baseAllocations.begin() + index);
2034 }
2035 }
2036
2037 // BENCHMARK
2038 const size_t allocCount = maxBufCapacity / 2;
2039 std::vector<VmaAllocation> testAllocations;
2040 testAllocations.reserve(allocCount);
2041 duration allocTotalDuration = duration::zero();
2042 duration freeTotalDuration = duration::zero();
2043 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2044 {
2045 // Allocations
2046 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2047 for(size_t i = 0; i < allocCount; ++i)
2048 {
2049 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2050 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2051 assert(res == VK_SUCCESS);
2052 testAllocations.push_back(alloc);
2053 }
2054 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2055
2056 // Deallocations
2057 switch(freeOrder)
2058 {
2059 case FREE_ORDER::FORWARD:
2060 // Leave testAllocations unchanged.
2061 break;
2062 case FREE_ORDER::BACKWARD:
2063 std::reverse(testAllocations.begin(), testAllocations.end());
2064 break;
2065 case FREE_ORDER::RANDOM:
2066 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2067 break;
2068 default: assert(0);
2069 }
2070
2071 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2072 for(size_t i = 0; i < allocCount; ++i)
2073 vmaFreeMemory(g_hAllocator, testAllocations[i]);
2074 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2075
2076 testAllocations.clear();
2077 }
2078
2079 // Delete baseAllocations
2080 while(!baseAllocations.empty())
2081 {
2082 vmaFreeMemory(g_hAllocator, baseAllocations.back());
2083 baseAllocations.pop_back();
2084 }
2085
2086 vmaDestroyPool(g_hAllocator, pool);
2087
2088 wprintf(L" LinearAlgorithm=%u %s FreeOrder=%s: allocations %g s, free %g s\n",
2089 linear ? 1 : 0,
2090 empty ? L"Empty" : L"Not empty",
2091 FREE_ORDER_NAMES[(size_t)freeOrder],
2092 ToFloatSeconds(allocTotalDuration),
2093 ToFloatSeconds(freeTotalDuration));
2094}
2095
2096static void BenchmarkLinearAllocator()
2097{
2098 wprintf(L"Benchmark linear allocator\n");
2099
2100 uint32_t freeOrderCount = 1;
2101 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2102 freeOrderCount = 3;
2103 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2104 freeOrderCount = 2;
2105
2106 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
2107
2108 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2109 {
2110 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2111 switch(freeOrderIndex)
2112 {
2113 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2114 case 1: freeOrder = FREE_ORDER::FORWARD; break;
2115 case 2: freeOrder = FREE_ORDER::RANDOM; break;
2116 default: assert(0);
2117 }
2118
2119 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2120 {
2121 for(uint32_t linearIndex = 0; linearIndex < 2; ++linearIndex)
2122 {
2123 BenchmarkLinearAllocatorCase(
2124 linearIndex ? 1 : 0, // linear
2125 emptyIndex ? 0 : 1, // empty
2126 freeOrder); // freeOrder
2127 }
2128 }
2129 }
2130}
2131
Adam Sawickib8333fb2018-03-13 16:15:53 +01002132static void TestPool_SameSize()
2133{
2134 const VkDeviceSize BUF_SIZE = 1024 * 1024;
2135 const size_t BUF_COUNT = 100;
2136 VkResult res;
2137
2138 RandomNumberGenerator rand{123};
2139
2140 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2141 bufferInfo.size = BUF_SIZE;
2142 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2143
2144 uint32_t memoryTypeBits = UINT32_MAX;
2145 {
2146 VkBuffer dummyBuffer;
2147 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2148 assert(res == VK_SUCCESS);
2149
2150 VkMemoryRequirements memReq;
2151 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2152 memoryTypeBits = memReq.memoryTypeBits;
2153
2154 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2155 }
2156
2157 VmaAllocationCreateInfo poolAllocInfo = {};
2158 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2159 uint32_t memTypeIndex;
2160 res = vmaFindMemoryTypeIndex(
2161 g_hAllocator,
2162 memoryTypeBits,
2163 &poolAllocInfo,
2164 &memTypeIndex);
2165
2166 VmaPoolCreateInfo poolCreateInfo = {};
2167 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2168 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
2169 poolCreateInfo.minBlockCount = 1;
2170 poolCreateInfo.maxBlockCount = 4;
2171 poolCreateInfo.frameInUseCount = 0;
2172
2173 VmaPool pool;
2174 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2175 assert(res == VK_SUCCESS);
2176
2177 vmaSetCurrentFrameIndex(g_hAllocator, 1);
2178
2179 VmaAllocationCreateInfo allocInfo = {};
2180 allocInfo.pool = pool;
2181 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2182 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2183
2184 struct BufItem
2185 {
2186 VkBuffer Buf;
2187 VmaAllocation Alloc;
2188 };
2189 std::vector<BufItem> items;
2190
2191 // Fill entire pool.
2192 for(size_t i = 0; i < BUF_COUNT; ++i)
2193 {
2194 BufItem item;
2195 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2196 assert(res == VK_SUCCESS);
2197 items.push_back(item);
2198 }
2199
2200 // Make sure that another allocation would fail.
2201 {
2202 BufItem item;
2203 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2204 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
2205 }
2206
2207 // Validate that no buffer is lost. Also check that they are not mapped.
2208 for(size_t i = 0; i < items.size(); ++i)
2209 {
2210 VmaAllocationInfo allocInfo;
2211 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2212 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2213 assert(allocInfo.pMappedData == nullptr);
2214 }
2215
2216 // Free some percent of random items.
2217 {
2218 const size_t PERCENT_TO_FREE = 10;
2219 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
2220 for(size_t i = 0; i < itemsToFree; ++i)
2221 {
2222 size_t index = (size_t)rand.Generate() % items.size();
2223 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2224 items.erase(items.begin() + index);
2225 }
2226 }
2227
2228 // Randomly allocate and free items.
2229 {
2230 const size_t OPERATION_COUNT = BUF_COUNT;
2231 for(size_t i = 0; i < OPERATION_COUNT; ++i)
2232 {
2233 bool allocate = rand.Generate() % 2 != 0;
2234 if(allocate)
2235 {
2236 if(items.size() < BUF_COUNT)
2237 {
2238 BufItem item;
2239 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2240 assert(res == VK_SUCCESS);
2241 items.push_back(item);
2242 }
2243 }
2244 else // Free
2245 {
2246 if(!items.empty())
2247 {
2248 size_t index = (size_t)rand.Generate() % items.size();
2249 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2250 items.erase(items.begin() + index);
2251 }
2252 }
2253 }
2254 }
2255
2256 // Allocate up to maximum.
2257 while(items.size() < BUF_COUNT)
2258 {
2259 BufItem item;
2260 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2261 assert(res == VK_SUCCESS);
2262 items.push_back(item);
2263 }
2264
2265 // Validate that no buffer is lost.
2266 for(size_t i = 0; i < items.size(); ++i)
2267 {
2268 VmaAllocationInfo allocInfo;
2269 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2270 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2271 }
2272
2273 // Next frame.
2274 vmaSetCurrentFrameIndex(g_hAllocator, 2);
2275
2276 // Allocate another BUF_COUNT buffers.
2277 for(size_t i = 0; i < BUF_COUNT; ++i)
2278 {
2279 BufItem item;
2280 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2281 assert(res == VK_SUCCESS);
2282 items.push_back(item);
2283 }
2284
2285 // Make sure the first BUF_COUNT is lost. Delete them.
2286 for(size_t i = 0; i < BUF_COUNT; ++i)
2287 {
2288 VmaAllocationInfo allocInfo;
2289 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2290 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
2291 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2292 }
2293 items.erase(items.begin(), items.begin() + BUF_COUNT);
2294
2295 // Validate that no buffer is lost.
2296 for(size_t i = 0; i < items.size(); ++i)
2297 {
2298 VmaAllocationInfo allocInfo;
2299 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2300 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2301 }
2302
2303 // Free one item.
2304 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
2305 items.pop_back();
2306
2307 // Validate statistics.
2308 {
2309 VmaPoolStats poolStats = {};
2310 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2311 assert(poolStats.allocationCount == items.size());
2312 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
2313 assert(poolStats.unusedRangeCount == 1);
2314 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
2315 assert(poolStats.unusedSize == BUF_SIZE);
2316 }
2317
2318 // Free all remaining items.
2319 for(size_t i = items.size(); i--; )
2320 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2321 items.clear();
2322
2323 // Allocate maximum items again.
2324 for(size_t i = 0; i < BUF_COUNT; ++i)
2325 {
2326 BufItem item;
2327 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2328 assert(res == VK_SUCCESS);
2329 items.push_back(item);
2330 }
2331
2332 // Delete every other item.
2333 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
2334 {
2335 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2336 items.erase(items.begin() + i);
2337 }
2338
2339 // Defragment!
2340 {
2341 std::vector<VmaAllocation> allocationsToDefragment(items.size());
2342 for(size_t i = 0; i < items.size(); ++i)
2343 allocationsToDefragment[i] = items[i].Alloc;
2344
2345 VmaDefragmentationStats defragmentationStats;
2346 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
2347 assert(res == VK_SUCCESS);
2348 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
2349 }
2350
2351 // Free all remaining items.
2352 for(size_t i = items.size(); i--; )
2353 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2354 items.clear();
2355
2356 ////////////////////////////////////////////////////////////////////////////////
2357 // Test for vmaMakePoolAllocationsLost
2358
2359 // Allocate 4 buffers on frame 10.
2360 vmaSetCurrentFrameIndex(g_hAllocator, 10);
2361 for(size_t i = 0; i < 4; ++i)
2362 {
2363 BufItem item;
2364 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2365 assert(res == VK_SUCCESS);
2366 items.push_back(item);
2367 }
2368
2369 // Touch first 2 of them on frame 11.
2370 vmaSetCurrentFrameIndex(g_hAllocator, 11);
2371 for(size_t i = 0; i < 2; ++i)
2372 {
2373 VmaAllocationInfo allocInfo;
2374 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2375 }
2376
2377 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
2378 size_t lostCount = 0xDEADC0DE;
2379 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2380 assert(lostCount == 2);
2381
2382 // Make another call. Now 0 should be lost.
2383 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2384 assert(lostCount == 0);
2385
2386 // Make another call, with null count. Should not crash.
2387 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
2388
2389 // END: Free all remaining items.
2390 for(size_t i = items.size(); i--; )
2391 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2392
2393 items.clear();
2394
Adam Sawickid2924172018-06-11 12:48:46 +02002395 ////////////////////////////////////////////////////////////////////////////////
2396 // Test for allocation too large for pool
2397
2398 {
2399 VmaAllocationCreateInfo allocCreateInfo = {};
2400 allocCreateInfo.pool = pool;
2401
2402 VkMemoryRequirements memReq;
2403 memReq.memoryTypeBits = UINT32_MAX;
2404 memReq.alignment = 1;
2405 memReq.size = poolCreateInfo.blockSize + 4;
2406
2407 VmaAllocation alloc = nullptr;
2408 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2409 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
2410 }
2411
Adam Sawickib8333fb2018-03-13 16:15:53 +01002412 vmaDestroyPool(g_hAllocator, pool);
2413}
2414
Adam Sawickie44c6262018-06-15 14:30:39 +02002415static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2416{
2417 const uint8_t* pBytes = (const uint8_t*)pMemory;
2418 for(size_t i = 0; i < size; ++i)
2419 {
2420 if(pBytes[i] != pattern)
2421 {
2422 return false;
2423 }
2424 }
2425 return true;
2426}
2427
2428static void TestAllocationsInitialization()
2429{
2430 VkResult res;
2431
2432 const size_t BUF_SIZE = 1024;
2433
2434 // Create pool.
2435
2436 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2437 bufInfo.size = BUF_SIZE;
2438 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2439
2440 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2441 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2442
2443 VmaPoolCreateInfo poolCreateInfo = {};
2444 poolCreateInfo.blockSize = BUF_SIZE * 10;
2445 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2446 poolCreateInfo.maxBlockCount = 1;
2447 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2448 assert(res == VK_SUCCESS);
2449
2450 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2451 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
2452 assert(res == VK_SUCCESS);
2453
2454 // Create one persistently mapped buffer to keep memory of this block mapped,
2455 // so that pointer to mapped data will remain (more or less...) valid even
2456 // after destruction of other allocations.
2457
2458 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2459 VkBuffer firstBuf;
2460 VmaAllocation firstAlloc;
2461 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
2462 assert(res == VK_SUCCESS);
2463
2464 // Test buffers.
2465
2466 for(uint32_t i = 0; i < 2; ++i)
2467 {
2468 const bool persistentlyMapped = i == 0;
2469 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2470 VkBuffer buf;
2471 VmaAllocation alloc;
2472 VmaAllocationInfo allocInfo;
2473 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
2474 assert(res == VK_SUCCESS);
2475
2476 void* pMappedData;
2477 if(!persistentlyMapped)
2478 {
2479 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
2480 assert(res == VK_SUCCESS);
2481 }
2482 else
2483 {
2484 pMappedData = allocInfo.pMappedData;
2485 }
2486
2487 // Validate initialized content
2488 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
2489 assert(valid);
2490
2491 if(!persistentlyMapped)
2492 {
2493 vmaUnmapMemory(g_hAllocator, alloc);
2494 }
2495
2496 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2497
2498 // Validate freed content
2499 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
2500 assert(valid);
2501 }
2502
2503 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2504 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2505}
2506
Adam Sawickib8333fb2018-03-13 16:15:53 +01002507static void TestPool_Benchmark(
2508 PoolTestResult& outResult,
2509 const PoolTestConfig& config)
2510{
2511 assert(config.ThreadCount > 0);
2512
2513 RandomNumberGenerator mainRand{config.RandSeed};
2514
2515 uint32_t allocationSizeProbabilitySum = std::accumulate(
2516 config.AllocationSizes.begin(),
2517 config.AllocationSizes.end(),
2518 0u,
2519 [](uint32_t sum, const AllocationSize& allocSize) {
2520 return sum + allocSize.Probability;
2521 });
2522
2523 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2524 bufferInfo.size = 256; // Whatever.
2525 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2526
2527 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2528 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2529 imageInfo.extent.width = 256; // Whatever.
2530 imageInfo.extent.height = 256; // Whatever.
2531 imageInfo.extent.depth = 1;
2532 imageInfo.mipLevels = 1;
2533 imageInfo.arrayLayers = 1;
2534 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2535 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2536 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2537 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2538 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2539
2540 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2541 {
2542 VkBuffer dummyBuffer;
2543 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2544 assert(res == VK_SUCCESS);
2545
2546 VkMemoryRequirements memReq;
2547 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2548 bufferMemoryTypeBits = memReq.memoryTypeBits;
2549
2550 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2551 }
2552
2553 uint32_t imageMemoryTypeBits = UINT32_MAX;
2554 {
2555 VkImage dummyImage;
2556 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
2557 assert(res == VK_SUCCESS);
2558
2559 VkMemoryRequirements memReq;
2560 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2561 imageMemoryTypeBits = memReq.memoryTypeBits;
2562
2563 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2564 }
2565
2566 uint32_t memoryTypeBits = 0;
2567 if(config.UsesBuffers() && config.UsesImages())
2568 {
2569 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2570 if(memoryTypeBits == 0)
2571 {
2572 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2573 return;
2574 }
2575 }
2576 else if(config.UsesBuffers())
2577 memoryTypeBits = bufferMemoryTypeBits;
2578 else if(config.UsesImages())
2579 memoryTypeBits = imageMemoryTypeBits;
2580 else
2581 assert(0);
2582
2583 VmaPoolCreateInfo poolCreateInfo = {};
2584 poolCreateInfo.memoryTypeIndex = 0;
2585 poolCreateInfo.minBlockCount = 1;
2586 poolCreateInfo.maxBlockCount = 1;
2587 poolCreateInfo.blockSize = config.PoolSize;
2588 poolCreateInfo.frameInUseCount = 1;
2589
2590 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2591 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2592 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2593
2594 VmaPool pool;
2595 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2596 assert(res == VK_SUCCESS);
2597
2598 // Start time measurement - after creating pool and initializing data structures.
2599 time_point timeBeg = std::chrono::high_resolution_clock::now();
2600
2601 ////////////////////////////////////////////////////////////////////////////////
2602 // ThreadProc
2603 auto ThreadProc = [&](
2604 PoolTestThreadResult* outThreadResult,
2605 uint32_t randSeed,
2606 HANDLE frameStartEvent,
2607 HANDLE frameEndEvent) -> void
2608 {
2609 RandomNumberGenerator threadRand{randSeed};
2610
2611 outThreadResult->AllocationTimeMin = duration::max();
2612 outThreadResult->AllocationTimeSum = duration::zero();
2613 outThreadResult->AllocationTimeMax = duration::min();
2614 outThreadResult->DeallocationTimeMin = duration::max();
2615 outThreadResult->DeallocationTimeSum = duration::zero();
2616 outThreadResult->DeallocationTimeMax = duration::min();
2617 outThreadResult->AllocationCount = 0;
2618 outThreadResult->DeallocationCount = 0;
2619 outThreadResult->LostAllocationCount = 0;
2620 outThreadResult->LostAllocationTotalSize = 0;
2621 outThreadResult->FailedAllocationCount = 0;
2622 outThreadResult->FailedAllocationTotalSize = 0;
2623
2624 struct Item
2625 {
2626 VkDeviceSize BufferSize;
2627 VkExtent2D ImageSize;
2628 VkBuffer Buf;
2629 VkImage Image;
2630 VmaAllocation Alloc;
2631
2632 VkDeviceSize CalcSizeBytes() const
2633 {
2634 return BufferSize +
2635 ImageSize.width * ImageSize.height * 4;
2636 }
2637 };
2638 std::vector<Item> unusedItems, usedItems;
2639
2640 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2641
2642 // Create all items - all unused, not yet allocated.
2643 for(size_t i = 0; i < threadTotalItemCount; ++i)
2644 {
2645 Item item = {};
2646
2647 uint32_t allocSizeIndex = 0;
2648 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2649 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2650 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2651
2652 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2653 if(allocSize.BufferSizeMax > 0)
2654 {
2655 assert(allocSize.BufferSizeMin > 0);
2656 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
2657 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2658 item.BufferSize = allocSize.BufferSizeMin;
2659 else
2660 {
2661 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2662 item.BufferSize = item.BufferSize / 16 * 16;
2663 }
2664 }
2665 else
2666 {
2667 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
2668 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2669 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2670 else
2671 {
2672 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2673 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2674 }
2675 }
2676
2677 unusedItems.push_back(item);
2678 }
2679
2680 auto Allocate = [&](Item& item) -> VkResult
2681 {
2682 VmaAllocationCreateInfo allocCreateInfo = {};
2683 allocCreateInfo.pool = pool;
2684 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2685 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2686
2687 if(item.BufferSize)
2688 {
2689 bufferInfo.size = item.BufferSize;
2690 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2691 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2692 }
2693 else
2694 {
2695 assert(item.ImageSize.width && item.ImageSize.height);
2696
2697 imageInfo.extent.width = item.ImageSize.width;
2698 imageInfo.extent.height = item.ImageSize.height;
2699 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2700 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
2701 }
2702 };
2703
2704 ////////////////////////////////////////////////////////////////////////////////
2705 // Frames
2706 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2707 {
2708 WaitForSingleObject(frameStartEvent, INFINITE);
2709
2710 // Always make some percent of used bufs unused, to choose different used ones.
2711 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
2712 for(size_t i = 0; i < bufsToMakeUnused; ++i)
2713 {
2714 size_t index = threadRand.Generate() % usedItems.size();
2715 unusedItems.push_back(usedItems[index]);
2716 usedItems.erase(usedItems.begin() + index);
2717 }
2718
2719 // Determine which bufs we want to use in this frame.
2720 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
2721 / config.ThreadCount;
2722 assert(usedBufCount < usedItems.size() + unusedItems.size());
2723 // Move some used to unused.
2724 while(usedBufCount < usedItems.size())
2725 {
2726 size_t index = threadRand.Generate() % usedItems.size();
2727 unusedItems.push_back(usedItems[index]);
2728 usedItems.erase(usedItems.begin() + index);
2729 }
2730 // Move some unused to used.
2731 while(usedBufCount > usedItems.size())
2732 {
2733 size_t index = threadRand.Generate() % unusedItems.size();
2734 usedItems.push_back(unusedItems[index]);
2735 unusedItems.erase(unusedItems.begin() + index);
2736 }
2737
2738 uint32_t touchExistingCount = 0;
2739 uint32_t touchLostCount = 0;
2740 uint32_t createSucceededCount = 0;
2741 uint32_t createFailedCount = 0;
2742
2743 // Touch all used bufs. If not created or lost, allocate.
2744 for(size_t i = 0; i < usedItems.size(); ++i)
2745 {
2746 Item& item = usedItems[i];
2747 // Not yet created.
2748 if(item.Alloc == VK_NULL_HANDLE)
2749 {
2750 res = Allocate(item);
2751 ++outThreadResult->AllocationCount;
2752 if(res != VK_SUCCESS)
2753 {
2754 item.Alloc = VK_NULL_HANDLE;
2755 item.Buf = VK_NULL_HANDLE;
2756 ++outThreadResult->FailedAllocationCount;
2757 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2758 ++createFailedCount;
2759 }
2760 else
2761 ++createSucceededCount;
2762 }
2763 else
2764 {
2765 // Touch.
2766 VmaAllocationInfo allocInfo;
2767 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
2768 // Lost.
2769 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
2770 {
2771 ++touchLostCount;
2772
2773 // Destroy.
2774 {
2775 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2776 if(item.Buf)
2777 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
2778 else
2779 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
2780 ++outThreadResult->DeallocationCount;
2781 }
2782 item.Alloc = VK_NULL_HANDLE;
2783 item.Buf = VK_NULL_HANDLE;
2784
2785 ++outThreadResult->LostAllocationCount;
2786 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
2787
2788 // Recreate.
2789 res = Allocate(item);
2790 ++outThreadResult->AllocationCount;
2791 // Creation failed.
2792 if(res != VK_SUCCESS)
2793 {
2794 ++outThreadResult->FailedAllocationCount;
2795 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2796 ++createFailedCount;
2797 }
2798 else
2799 ++createSucceededCount;
2800 }
2801 else
2802 ++touchExistingCount;
2803 }
2804 }
2805
2806 /*
2807 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
2808 randSeed, frameIndex,
2809 touchExistingCount, touchLostCount,
2810 createSucceededCount, createFailedCount);
2811 */
2812
2813 SetEvent(frameEndEvent);
2814 }
2815
2816 // Free all remaining items.
2817 for(size_t i = usedItems.size(); i--; )
2818 {
2819 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2820 if(usedItems[i].Buf)
2821 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
2822 else
2823 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
2824 ++outThreadResult->DeallocationCount;
2825 }
2826 for(size_t i = unusedItems.size(); i--; )
2827 {
2828 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
2829 if(unusedItems[i].Buf)
2830 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
2831 else
2832 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
2833 ++outThreadResult->DeallocationCount;
2834 }
2835 };
2836
2837 // Launch threads.
2838 uint32_t threadRandSeed = mainRand.Generate();
2839 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
2840 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
2841 std::vector<std::thread> bkgThreads;
2842 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
2843 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2844 {
2845 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2846 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2847 bkgThreads.emplace_back(std::bind(
2848 ThreadProc,
2849 &threadResults[threadIndex],
2850 threadRandSeed + threadIndex,
2851 frameStartEvents[threadIndex],
2852 frameEndEvents[threadIndex]));
2853 }
2854
2855 // Execute frames.
2856 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
2857 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2858 {
2859 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
2860 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2861 SetEvent(frameStartEvents[threadIndex]);
2862 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
2863 }
2864
2865 // Wait for threads finished
2866 for(size_t i = 0; i < bkgThreads.size(); ++i)
2867 {
2868 bkgThreads[i].join();
2869 CloseHandle(frameEndEvents[i]);
2870 CloseHandle(frameStartEvents[i]);
2871 }
2872 bkgThreads.clear();
2873
2874 // Finish time measurement - before destroying pool.
2875 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
2876
2877 vmaDestroyPool(g_hAllocator, pool);
2878
2879 outResult.AllocationTimeMin = duration::max();
2880 outResult.AllocationTimeAvg = duration::zero();
2881 outResult.AllocationTimeMax = duration::min();
2882 outResult.DeallocationTimeMin = duration::max();
2883 outResult.DeallocationTimeAvg = duration::zero();
2884 outResult.DeallocationTimeMax = duration::min();
2885 outResult.LostAllocationCount = 0;
2886 outResult.LostAllocationTotalSize = 0;
2887 outResult.FailedAllocationCount = 0;
2888 outResult.FailedAllocationTotalSize = 0;
2889 size_t allocationCount = 0;
2890 size_t deallocationCount = 0;
2891 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2892 {
2893 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
2894 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
2895 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
2896 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
2897 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
2898 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
2899 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
2900 allocationCount += threadResult.AllocationCount;
2901 deallocationCount += threadResult.DeallocationCount;
2902 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
2903 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
2904 outResult.LostAllocationCount += threadResult.LostAllocationCount;
2905 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
2906 }
2907 if(allocationCount)
2908 outResult.AllocationTimeAvg /= allocationCount;
2909 if(deallocationCount)
2910 outResult.DeallocationTimeAvg /= deallocationCount;
2911}
2912
2913static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
2914{
2915 if(ptr1 < ptr2)
2916 return ptr1 + size1 > ptr2;
2917 else if(ptr2 < ptr1)
2918 return ptr2 + size2 > ptr1;
2919 else
2920 return true;
2921}
2922
2923static void TestMapping()
2924{
2925 wprintf(L"Testing mapping...\n");
2926
2927 VkResult res;
2928 uint32_t memTypeIndex = UINT32_MAX;
2929
2930 enum TEST
2931 {
2932 TEST_NORMAL,
2933 TEST_POOL,
2934 TEST_DEDICATED,
2935 TEST_COUNT
2936 };
2937 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
2938 {
2939 VmaPool pool = nullptr;
2940 if(testIndex == TEST_POOL)
2941 {
2942 assert(memTypeIndex != UINT32_MAX);
2943 VmaPoolCreateInfo poolInfo = {};
2944 poolInfo.memoryTypeIndex = memTypeIndex;
2945 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
2946 assert(res == VK_SUCCESS);
2947 }
2948
2949 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2950 bufInfo.size = 0x10000;
2951 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2952
2953 VmaAllocationCreateInfo allocCreateInfo = {};
2954 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2955 allocCreateInfo.pool = pool;
2956 if(testIndex == TEST_DEDICATED)
2957 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2958
2959 VmaAllocationInfo allocInfo;
2960
2961 // Mapped manually
2962
2963 // Create 2 buffers.
2964 BufferInfo bufferInfos[3];
2965 for(size_t i = 0; i < 2; ++i)
2966 {
2967 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
2968 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
2969 assert(res == VK_SUCCESS);
2970 assert(allocInfo.pMappedData == nullptr);
2971 memTypeIndex = allocInfo.memoryType;
2972 }
2973
2974 // Map buffer 0.
2975 char* data00 = nullptr;
2976 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
2977 assert(res == VK_SUCCESS && data00 != nullptr);
2978 data00[0xFFFF] = data00[0];
2979
2980 // Map buffer 0 second time.
2981 char* data01 = nullptr;
2982 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
2983 assert(res == VK_SUCCESS && data01 == data00);
2984
2985 // Map buffer 1.
2986 char* data1 = nullptr;
2987 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
2988 assert(res == VK_SUCCESS && data1 != nullptr);
2989 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
2990 data1[0xFFFF] = data1[0];
2991
2992 // Unmap buffer 0 two times.
2993 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2994 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
2995 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
2996 assert(allocInfo.pMappedData == nullptr);
2997
2998 // Unmap buffer 1.
2999 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3000 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
3001 assert(allocInfo.pMappedData == nullptr);
3002
3003 // Create 3rd buffer - persistently mapped.
3004 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3005 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3006 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
3007 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
3008
3009 // Map buffer 2.
3010 char* data2 = nullptr;
3011 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
3012 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
3013 data2[0xFFFF] = data2[0];
3014
3015 // Unmap buffer 2.
3016 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3017 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
3018 assert(allocInfo.pMappedData == data2);
3019
3020 // Destroy all buffers.
3021 for(size_t i = 3; i--; )
3022 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3023
3024 vmaDestroyPool(g_hAllocator, pool);
3025 }
3026}
3027
3028static void TestMappingMultithreaded()
3029{
3030 wprintf(L"Testing mapping multithreaded...\n");
3031
3032 static const uint32_t threadCount = 16;
3033 static const uint32_t bufferCount = 1024;
3034 static const uint32_t threadBufferCount = bufferCount / threadCount;
3035
3036 VkResult res;
3037 volatile uint32_t memTypeIndex = UINT32_MAX;
3038
3039 enum TEST
3040 {
3041 TEST_NORMAL,
3042 TEST_POOL,
3043 TEST_DEDICATED,
3044 TEST_COUNT
3045 };
3046 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3047 {
3048 VmaPool pool = nullptr;
3049 if(testIndex == TEST_POOL)
3050 {
3051 assert(memTypeIndex != UINT32_MAX);
3052 VmaPoolCreateInfo poolInfo = {};
3053 poolInfo.memoryTypeIndex = memTypeIndex;
3054 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3055 assert(res == VK_SUCCESS);
3056 }
3057
3058 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3059 bufCreateInfo.size = 0x10000;
3060 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3061
3062 VmaAllocationCreateInfo allocCreateInfo = {};
3063 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3064 allocCreateInfo.pool = pool;
3065 if(testIndex == TEST_DEDICATED)
3066 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3067
3068 std::thread threads[threadCount];
3069 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3070 {
3071 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3072 // ======== THREAD FUNCTION ========
3073
3074 RandomNumberGenerator rand{threadIndex};
3075
3076 enum class MODE
3077 {
3078 // Don't map this buffer at all.
3079 DONT_MAP,
3080 // Map and quickly unmap.
3081 MAP_FOR_MOMENT,
3082 // Map and unmap before destruction.
3083 MAP_FOR_LONGER,
3084 // Map two times. Quickly unmap, second unmap before destruction.
3085 MAP_TWO_TIMES,
3086 // Create this buffer as persistently mapped.
3087 PERSISTENTLY_MAPPED,
3088 COUNT
3089 };
3090 std::vector<BufferInfo> bufInfos{threadBufferCount};
3091 std::vector<MODE> bufModes{threadBufferCount};
3092
3093 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3094 {
3095 BufferInfo& bufInfo = bufInfos[bufferIndex];
3096 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3097 bufModes[bufferIndex] = mode;
3098
3099 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3100 if(mode == MODE::PERSISTENTLY_MAPPED)
3101 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3102
3103 VmaAllocationInfo allocInfo;
3104 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3105 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
3106 assert(res == VK_SUCCESS);
3107
3108 if(memTypeIndex == UINT32_MAX)
3109 memTypeIndex = allocInfo.memoryType;
3110
3111 char* data = nullptr;
3112
3113 if(mode == MODE::PERSISTENTLY_MAPPED)
3114 {
3115 data = (char*)allocInfo.pMappedData;
3116 assert(data != nullptr);
3117 }
3118 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3119 mode == MODE::MAP_TWO_TIMES)
3120 {
3121 assert(data == nullptr);
3122 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
3123 assert(res == VK_SUCCESS && data != nullptr);
3124
3125 if(mode == MODE::MAP_TWO_TIMES)
3126 {
3127 char* data2 = nullptr;
3128 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
3129 assert(res == VK_SUCCESS && data2 == data);
3130 }
3131 }
3132 else if(mode == MODE::DONT_MAP)
3133 {
3134 assert(allocInfo.pMappedData == nullptr);
3135 }
3136 else
3137 assert(0);
3138
3139 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3140 if(data)
3141 data[0xFFFF] = data[0];
3142
3143 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3144 {
3145 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3146
3147 VmaAllocationInfo allocInfo;
3148 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3149 if(mode == MODE::MAP_FOR_MOMENT)
3150 assert(allocInfo.pMappedData == nullptr);
3151 else
3152 assert(allocInfo.pMappedData == data);
3153 }
3154
3155 switch(rand.Generate() % 3)
3156 {
3157 case 0: Sleep(0); break; // Yield.
3158 case 1: Sleep(10); break; // 10 ms
3159 // default: No sleep.
3160 }
3161
3162 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3163 if(data)
3164 data[0xFFFF] = data[0];
3165 }
3166
3167 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3168 {
3169 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3170 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3171 {
3172 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3173
3174 VmaAllocationInfo allocInfo;
3175 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
3176 assert(allocInfo.pMappedData == nullptr);
3177 }
3178
3179 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3180 }
3181 });
3182 }
3183
3184 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3185 threads[threadIndex].join();
3186
3187 vmaDestroyPool(g_hAllocator, pool);
3188 }
3189}
3190
3191static void WriteMainTestResultHeader(FILE* file)
3192{
3193 fprintf(file,
3194 "Code,Test,Time,"
3195 "Config,"
3196 "Total Time (us),"
3197 "Allocation Time Min (us),"
3198 "Allocation Time Avg (us),"
3199 "Allocation Time Max (us),"
3200 "Deallocation Time Min (us),"
3201 "Deallocation Time Avg (us),"
3202 "Deallocation Time Max (us),"
3203 "Total Memory Allocated (B),"
3204 "Free Range Size Avg (B),"
3205 "Free Range Size Max (B)\n");
3206}
3207
3208static void WriteMainTestResult(
3209 FILE* file,
3210 const char* codeDescription,
3211 const char* testDescription,
3212 const Config& config, const Result& result)
3213{
3214 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3215 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3216 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3217 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3218 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3219 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3220 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3221
3222 time_t rawTime; time(&rawTime);
3223 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3224 char timeStr[128];
3225 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3226
3227 fprintf(file,
3228 "%s,%s,%s,"
3229 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%d,"
3230 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3231 codeDescription,
3232 testDescription,
3233 timeStr,
3234 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, (uint32_t)config.FreeOrder,
3235 totalTimeSeconds * 1e6f,
3236 allocationTimeMinSeconds * 1e6f,
3237 allocationTimeAvgSeconds * 1e6f,
3238 allocationTimeMaxSeconds * 1e6f,
3239 deallocationTimeMinSeconds * 1e6f,
3240 deallocationTimeAvgSeconds * 1e6f,
3241 deallocationTimeMaxSeconds * 1e6f,
3242 result.TotalMemoryAllocated,
3243 result.FreeRangeSizeAvg,
3244 result.FreeRangeSizeMax);
3245}
3246
3247static void WritePoolTestResultHeader(FILE* file)
3248{
3249 fprintf(file,
3250 "Code,Test,Time,"
3251 "Config,"
3252 "Total Time (us),"
3253 "Allocation Time Min (us),"
3254 "Allocation Time Avg (us),"
3255 "Allocation Time Max (us),"
3256 "Deallocation Time Min (us),"
3257 "Deallocation Time Avg (us),"
3258 "Deallocation Time Max (us),"
3259 "Lost Allocation Count,"
3260 "Lost Allocation Total Size (B),"
3261 "Failed Allocation Count,"
3262 "Failed Allocation Total Size (B)\n");
3263}
3264
3265static void WritePoolTestResult(
3266 FILE* file,
3267 const char* codeDescription,
3268 const char* testDescription,
3269 const PoolTestConfig& config,
3270 const PoolTestResult& result)
3271{
3272 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3273 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3274 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3275 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3276 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3277 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3278 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3279
3280 time_t rawTime; time(&rawTime);
3281 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3282 char timeStr[128];
3283 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3284
3285 fprintf(file,
3286 "%s,%s,%s,"
3287 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3288 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3289 // General
3290 codeDescription,
3291 testDescription,
3292 timeStr,
3293 // Config
3294 config.ThreadCount,
3295 (unsigned long long)config.PoolSize,
3296 config.FrameCount,
3297 config.TotalItemCount,
3298 config.UsedItemCountMin,
3299 config.UsedItemCountMax,
3300 config.ItemsToMakeUnusedPercent,
3301 // Results
3302 totalTimeSeconds * 1e6f,
3303 allocationTimeMinSeconds * 1e6f,
3304 allocationTimeAvgSeconds * 1e6f,
3305 allocationTimeMaxSeconds * 1e6f,
3306 deallocationTimeMinSeconds * 1e6f,
3307 deallocationTimeAvgSeconds * 1e6f,
3308 deallocationTimeMaxSeconds * 1e6f,
3309 result.LostAllocationCount,
3310 result.LostAllocationTotalSize,
3311 result.FailedAllocationCount,
3312 result.FailedAllocationTotalSize);
3313}
3314
3315static void PerformCustomMainTest(FILE* file)
3316{
3317 Config config{};
3318 config.RandSeed = 65735476;
3319 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3320 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3321 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3322 config.FreeOrder = FREE_ORDER::FORWARD;
3323 config.ThreadCount = 16;
3324 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3325
3326 // Buffers
3327 //config.AllocationSizes.push_back({4, 16, 1024});
3328 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3329
3330 // Images
3331 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3332 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3333
3334 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3335 config.AdditionalOperationCount = 1024;
3336
3337 Result result{};
3338 VkResult res = MainTest(result, config);
3339 assert(res == VK_SUCCESS);
3340 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3341}
3342
3343static void PerformCustomPoolTest(FILE* file)
3344{
3345 PoolTestConfig config;
3346 config.PoolSize = 100 * 1024 * 1024;
3347 config.RandSeed = 2345764;
3348 config.ThreadCount = 1;
3349 config.FrameCount = 200;
3350 config.ItemsToMakeUnusedPercent = 2;
3351
3352 AllocationSize allocSize = {};
3353 allocSize.BufferSizeMin = 1024;
3354 allocSize.BufferSizeMax = 1024 * 1024;
3355 allocSize.Probability = 1;
3356 config.AllocationSizes.push_back(allocSize);
3357
3358 allocSize.BufferSizeMin = 0;
3359 allocSize.BufferSizeMax = 0;
3360 allocSize.ImageSizeMin = 128;
3361 allocSize.ImageSizeMax = 1024;
3362 allocSize.Probability = 1;
3363 config.AllocationSizes.push_back(allocSize);
3364
3365 config.PoolSize = config.CalcAvgResourceSize() * 200;
3366 config.UsedItemCountMax = 160;
3367 config.TotalItemCount = config.UsedItemCountMax * 10;
3368 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3369
3370 g_MemoryAliasingWarningEnabled = false;
3371 PoolTestResult result = {};
3372 TestPool_Benchmark(result, config);
3373 g_MemoryAliasingWarningEnabled = true;
3374
3375 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3376}
3377
Adam Sawickib8333fb2018-03-13 16:15:53 +01003378static const char* CODE_DESCRIPTION = "Foo";
3379
3380static void PerformMainTests(FILE* file)
3381{
3382 uint32_t repeatCount = 1;
3383 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3384
3385 Config config{};
3386 config.RandSeed = 65735476;
3387 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3388 config.FreeOrder = FREE_ORDER::FORWARD;
3389
3390 size_t threadCountCount = 1;
3391 switch(ConfigType)
3392 {
3393 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3394 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3395 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3396 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3397 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3398 default: assert(0);
3399 }
3400 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3401 {
3402 std::string desc1;
3403
3404 switch(threadCountIndex)
3405 {
3406 case 0:
3407 desc1 += "1_thread";
3408 config.ThreadCount = 1;
3409 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3410 break;
3411 case 1:
3412 desc1 += "16_threads+0%_common";
3413 config.ThreadCount = 16;
3414 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3415 break;
3416 case 2:
3417 desc1 += "16_threads+50%_common";
3418 config.ThreadCount = 16;
3419 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3420 break;
3421 case 3:
3422 desc1 += "16_threads+100%_common";
3423 config.ThreadCount = 16;
3424 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3425 break;
3426 case 4:
3427 desc1 += "2_threads+0%_common";
3428 config.ThreadCount = 2;
3429 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3430 break;
3431 case 5:
3432 desc1 += "2_threads+50%_common";
3433 config.ThreadCount = 2;
3434 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3435 break;
3436 case 6:
3437 desc1 += "2_threads+100%_common";
3438 config.ThreadCount = 2;
3439 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3440 break;
3441 default:
3442 assert(0);
3443 }
3444
3445 // 0 = buffers, 1 = images, 2 = buffers and images
3446 size_t buffersVsImagesCount = 2;
3447 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3448 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3449 {
3450 std::string desc2 = desc1;
3451 switch(buffersVsImagesIndex)
3452 {
3453 case 0: desc2 += " Buffers"; break;
3454 case 1: desc2 += " Images"; break;
3455 case 2: desc2 += " Buffers+Images"; break;
3456 default: assert(0);
3457 }
3458
3459 // 0 = small, 1 = large, 2 = small and large
3460 size_t smallVsLargeCount = 2;
3461 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3462 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3463 {
3464 std::string desc3 = desc2;
3465 switch(smallVsLargeIndex)
3466 {
3467 case 0: desc3 += " Small"; break;
3468 case 1: desc3 += " Large"; break;
3469 case 2: desc3 += " Small+Large"; break;
3470 default: assert(0);
3471 }
3472
3473 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3474 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3475 else
3476 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3477
3478 // 0 = varying sizes min...max, 1 = set of constant sizes
3479 size_t constantSizesCount = 1;
3480 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3481 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3482 {
3483 std::string desc4 = desc3;
3484 switch(constantSizesIndex)
3485 {
3486 case 0: desc4 += " Varying_sizes"; break;
3487 case 1: desc4 += " Constant_sizes"; break;
3488 default: assert(0);
3489 }
3490
3491 config.AllocationSizes.clear();
3492 // Buffers present
3493 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3494 {
3495 // Small
3496 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3497 {
3498 // Varying size
3499 if(constantSizesIndex == 0)
3500 config.AllocationSizes.push_back({4, 16, 1024});
3501 // Constant sizes
3502 else
3503 {
3504 config.AllocationSizes.push_back({1, 16, 16});
3505 config.AllocationSizes.push_back({1, 64, 64});
3506 config.AllocationSizes.push_back({1, 256, 256});
3507 config.AllocationSizes.push_back({1, 1024, 1024});
3508 }
3509 }
3510 // Large
3511 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3512 {
3513 // Varying size
3514 if(constantSizesIndex == 0)
3515 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3516 // Constant sizes
3517 else
3518 {
3519 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3520 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3521 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3522 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3523 }
3524 }
3525 }
3526 // Images present
3527 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3528 {
3529 // Small
3530 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3531 {
3532 // Varying size
3533 if(constantSizesIndex == 0)
3534 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3535 // Constant sizes
3536 else
3537 {
3538 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3539 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3540 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3541 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3542 }
3543 }
3544 // Large
3545 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3546 {
3547 // Varying size
3548 if(constantSizesIndex == 0)
3549 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3550 // Constant sizes
3551 else
3552 {
3553 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3554 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3555 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3556 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3557 }
3558 }
3559 }
3560
3561 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3562 size_t beginBytesToAllocateCount = 1;
3563 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3564 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3565 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3566 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3567 {
3568 std::string desc5 = desc4;
3569
3570 switch(beginBytesToAllocateIndex)
3571 {
3572 case 0:
3573 desc5 += " Allocate_100%";
3574 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3575 config.AdditionalOperationCount = 0;
3576 break;
3577 case 1:
3578 desc5 += " Allocate_50%+Operations";
3579 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3580 config.AdditionalOperationCount = 1024;
3581 break;
3582 case 2:
3583 desc5 += " Allocate_5%+Operations";
3584 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3585 config.AdditionalOperationCount = 1024;
3586 break;
3587 case 3:
3588 desc5 += " Allocate_95%+Operations";
3589 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3590 config.AdditionalOperationCount = 1024;
3591 break;
3592 default:
3593 assert(0);
3594 }
3595
3596 const char* testDescription = desc5.c_str();
3597
3598 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3599 {
3600 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3601
3602 Result result{};
3603 VkResult res = MainTest(result, config);
3604 assert(res == VK_SUCCESS);
3605 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3606 }
3607 }
3608 }
3609 }
3610 }
3611 }
3612}
3613
3614static void PerformPoolTests(FILE* file)
3615{
3616 const size_t AVG_RESOURCES_PER_POOL = 300;
3617
3618 uint32_t repeatCount = 1;
3619 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3620
3621 PoolTestConfig config{};
3622 config.RandSeed = 2346343;
3623 config.FrameCount = 200;
3624 config.ItemsToMakeUnusedPercent = 2;
3625
3626 size_t threadCountCount = 1;
3627 switch(ConfigType)
3628 {
3629 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3630 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3631 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3632 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3633 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3634 default: assert(0);
3635 }
3636 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3637 {
3638 std::string desc1;
3639
3640 switch(threadCountIndex)
3641 {
3642 case 0:
3643 desc1 += "1_thread";
3644 config.ThreadCount = 1;
3645 break;
3646 case 1:
3647 desc1 += "16_threads";
3648 config.ThreadCount = 16;
3649 break;
3650 case 2:
3651 desc1 += "2_threads";
3652 config.ThreadCount = 2;
3653 break;
3654 default:
3655 assert(0);
3656 }
3657
3658 // 0 = buffers, 1 = images, 2 = buffers and images
3659 size_t buffersVsImagesCount = 2;
3660 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3661 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3662 {
3663 std::string desc2 = desc1;
3664 switch(buffersVsImagesIndex)
3665 {
3666 case 0: desc2 += " Buffers"; break;
3667 case 1: desc2 += " Images"; break;
3668 case 2: desc2 += " Buffers+Images"; break;
3669 default: assert(0);
3670 }
3671
3672 // 0 = small, 1 = large, 2 = small and large
3673 size_t smallVsLargeCount = 2;
3674 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3675 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3676 {
3677 std::string desc3 = desc2;
3678 switch(smallVsLargeIndex)
3679 {
3680 case 0: desc3 += " Small"; break;
3681 case 1: desc3 += " Large"; break;
3682 case 2: desc3 += " Small+Large"; break;
3683 default: assert(0);
3684 }
3685
3686 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3687 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3688 else
3689 config.PoolSize = 4ull * 1024 * 1024;
3690
3691 // 0 = varying sizes min...max, 1 = set of constant sizes
3692 size_t constantSizesCount = 1;
3693 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3694 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3695 {
3696 std::string desc4 = desc3;
3697 switch(constantSizesIndex)
3698 {
3699 case 0: desc4 += " Varying_sizes"; break;
3700 case 1: desc4 += " Constant_sizes"; break;
3701 default: assert(0);
3702 }
3703
3704 config.AllocationSizes.clear();
3705 // Buffers present
3706 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3707 {
3708 // Small
3709 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3710 {
3711 // Varying size
3712 if(constantSizesIndex == 0)
3713 config.AllocationSizes.push_back({4, 16, 1024});
3714 // Constant sizes
3715 else
3716 {
3717 config.AllocationSizes.push_back({1, 16, 16});
3718 config.AllocationSizes.push_back({1, 64, 64});
3719 config.AllocationSizes.push_back({1, 256, 256});
3720 config.AllocationSizes.push_back({1, 1024, 1024});
3721 }
3722 }
3723 // Large
3724 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3725 {
3726 // Varying size
3727 if(constantSizesIndex == 0)
3728 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3729 // Constant sizes
3730 else
3731 {
3732 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3733 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3734 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3735 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3736 }
3737 }
3738 }
3739 // Images present
3740 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3741 {
3742 // Small
3743 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3744 {
3745 // Varying size
3746 if(constantSizesIndex == 0)
3747 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3748 // Constant sizes
3749 else
3750 {
3751 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3752 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3753 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3754 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3755 }
3756 }
3757 // Large
3758 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3759 {
3760 // Varying size
3761 if(constantSizesIndex == 0)
3762 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3763 // Constant sizes
3764 else
3765 {
3766 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3767 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3768 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3769 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3770 }
3771 }
3772 }
3773
3774 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
3775 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
3776
3777 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
3778 size_t subscriptionModeCount;
3779 switch(ConfigType)
3780 {
3781 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
3782 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
3783 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
3784 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
3785 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
3786 default: assert(0);
3787 }
3788 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
3789 {
3790 std::string desc5 = desc4;
3791
3792 switch(subscriptionModeIndex)
3793 {
3794 case 0:
3795 desc5 += " Subscription_66%";
3796 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
3797 break;
3798 case 1:
3799 desc5 += " Subscription_133%";
3800 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
3801 break;
3802 case 2:
3803 desc5 += " Subscription_100%";
3804 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
3805 break;
3806 case 3:
3807 desc5 += " Subscription_33%";
3808 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
3809 break;
3810 case 4:
3811 desc5 += " Subscription_166%";
3812 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
3813 break;
3814 default:
3815 assert(0);
3816 }
3817
3818 config.TotalItemCount = config.UsedItemCountMax * 5;
3819 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3820
3821 const char* testDescription = desc5.c_str();
3822
3823 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3824 {
3825 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3826
3827 PoolTestResult result{};
3828 g_MemoryAliasingWarningEnabled = false;
3829 TestPool_Benchmark(result, config);
3830 g_MemoryAliasingWarningEnabled = true;
3831 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3832 }
3833 }
3834 }
3835 }
3836 }
3837 }
3838}
3839
3840void Test()
3841{
3842 wprintf(L"TESTING:\n");
3843
3844 // # Simple tests
3845
3846 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02003847#if VMA_DEBUG_MARGIN
3848 TestDebugMargin();
3849#else
3850 TestPool_SameSize();
3851 TestHeapSizeLimit();
3852#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02003853#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
3854 TestAllocationsInitialization();
3855#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01003856 TestMapping();
3857 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003858 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003859 ManuallyTestLinearAllocator();
Adam Sawicki0a607132018-08-24 11:18:41 +02003860 BenchmarkLinearAllocator();
Adam Sawickib8333fb2018-03-13 16:15:53 +01003861 TestDefragmentationSimple();
3862 TestDefragmentationFull();
3863
3864 // # Detailed tests
3865 FILE* file;
3866 fopen_s(&file, "Results.csv", "w");
3867 assert(file != NULL);
3868
3869 WriteMainTestResultHeader(file);
3870 PerformMainTests(file);
3871 //PerformCustomMainTest(file);
3872
3873 WritePoolTestResultHeader(file);
3874 PerformPoolTests(file);
3875 //PerformCustomPoolTest(file);
3876
3877 fclose(file);
3878
3879 wprintf(L"Done.\n");
3880}
3881
Adam Sawickif1a793c2018-03-13 15:42:22 +01003882#endif // #ifdef _WIN32