blob: 842fba2283782713ba042a6f153006a92075a78e [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 Sawicki70a683e2018-08-24 15:36:32 +02001842 vmaDestroyPool(g_hAllocator, pool);
1843}
Adam Sawickif799c4f2018-08-23 10:40:30 +02001844
Adam Sawicki70a683e2018-08-24 15:36:32 +02001845static void TestLinearAllocatorMultiBlock()
1846{
1847 wprintf(L"Test linear allocator multi block\n");
1848
1849 RandomNumberGenerator rand{345673};
1850
1851 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1852 sampleBufCreateInfo.size = 1024 * 1024;
1853 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1854
1855 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1856 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1857
1858 VmaPoolCreateInfo poolCreateInfo = {};
1859 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1860 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1861 assert(res == VK_SUCCESS);
1862
1863 VmaPool pool = nullptr;
1864 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1865 assert(res == VK_SUCCESS);
1866
1867 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
1868
1869 VmaAllocationCreateInfo allocCreateInfo = {};
1870 allocCreateInfo.pool = pool;
1871
1872 std::vector<BufferInfo> bufInfo;
1873 VmaAllocationInfo allocInfo;
1874
1875 // Test one-time free.
1876 {
1877 // Allocate buffers until we move to a second block.
1878 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1879 for(uint32_t i = 0; ; ++i)
1880 {
1881 BufferInfo newBufInfo;
1882 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1883 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1884 assert(res == VK_SUCCESS);
1885 bufInfo.push_back(newBufInfo);
1886 if(lastMem && allocInfo.deviceMemory != lastMem)
1887 {
1888 break;
1889 }
1890 lastMem = allocInfo.deviceMemory;
1891 }
1892
1893 assert(bufInfo.size() > 2);
1894
1895 // Make sure that pool has now two blocks.
1896 VmaPoolStats poolStats = {};
1897 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1898 assert(poolStats.blockCount == 2);
1899
1900 // Destroy all the buffers in random order.
1901 while(!bufInfo.empty())
1902 {
1903 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
1904 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
1905 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1906 bufInfo.erase(bufInfo.begin() + indexToDestroy);
1907 }
1908
1909 // Make sure that pool has now at most one block.
1910 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1911 assert(poolStats.blockCount <= 1);
1912 }
1913
1914 // Test stack.
1915 {
1916 // Allocate buffers until we move to a second block.
1917 VkDeviceMemory lastMem = VK_NULL_HANDLE;
1918 for(uint32_t i = 0; ; ++i)
1919 {
1920 BufferInfo newBufInfo;
1921 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1922 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1923 assert(res == VK_SUCCESS);
1924 bufInfo.push_back(newBufInfo);
1925 if(lastMem && allocInfo.deviceMemory != lastMem)
1926 {
1927 break;
1928 }
1929 lastMem = allocInfo.deviceMemory;
1930 }
1931
1932 assert(bufInfo.size() > 2);
1933
1934 // Add few more buffers.
1935 for(uint32_t i = 0; i < 5; ++i)
1936 {
1937 BufferInfo newBufInfo;
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
1944 // Make sure that pool has now two blocks.
1945 VmaPoolStats poolStats = {};
1946 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1947 assert(poolStats.blockCount == 2);
1948
1949 // Delete half of buffers, LIFO.
1950 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
1951 {
1952 const BufferInfo& currBufInfo = bufInfo.back();
1953 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1954 bufInfo.pop_back();
1955 }
1956
1957 // Add one more buffer.
1958 BufferInfo newBufInfo;
1959 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
1960 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
1961 assert(res == VK_SUCCESS);
1962 bufInfo.push_back(newBufInfo);
1963
1964 // Make sure that pool has now one block.
1965 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
1966 assert(poolStats.blockCount == 1);
1967
1968 // Delete all the remaining buffers, LIFO.
1969 while(!bufInfo.empty())
1970 {
1971 const BufferInfo& currBufInfo = bufInfo.back();
1972 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
1973 bufInfo.pop_back();
1974 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02001975 }
1976
Adam Sawicki0876c0d2018-06-20 15:18:11 +02001977 vmaDestroyPool(g_hAllocator, pool);
1978}
1979
Adam Sawickifd11d752018-08-22 15:02:10 +02001980static void ManuallyTestLinearAllocator()
1981{
1982 VmaStats origStats;
1983 vmaCalculateStats(g_hAllocator, &origStats);
1984
1985 wprintf(L"Manually test linear allocator\n");
1986
1987 RandomNumberGenerator rand{645332};
1988
1989 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1990 sampleBufCreateInfo.size = 1024; // Whatever.
1991 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1992
1993 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1994 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1995
1996 VmaPoolCreateInfo poolCreateInfo = {};
1997 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1998 assert(res == VK_SUCCESS);
1999
2000 poolCreateInfo.blockSize = 10 * 1024;
2001 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2002 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2003
2004 VmaPool pool = nullptr;
2005 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2006 assert(res == VK_SUCCESS);
2007
2008 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2009
2010 VmaAllocationCreateInfo allocCreateInfo = {};
2011 allocCreateInfo.pool = pool;
2012
2013 std::vector<BufferInfo> bufInfo;
2014 VmaAllocationInfo allocInfo;
2015 BufferInfo newBufInfo;
2016
2017 // Test double stack.
2018 {
2019 /*
2020 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
2021 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
2022
2023 Totally:
2024 1 block allocated
2025 10240 Vulkan bytes
2026 6 new allocations
2027 2256 bytes in allocations
2028 */
2029
2030 bufCreateInfo.size = 32;
2031 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2032 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2033 assert(res == VK_SUCCESS);
2034 bufInfo.push_back(newBufInfo);
2035
2036 bufCreateInfo.size = 1024;
2037 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2038 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2039 assert(res == VK_SUCCESS);
2040 bufInfo.push_back(newBufInfo);
2041
2042 bufCreateInfo.size = 32;
2043 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2044 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2045 assert(res == VK_SUCCESS);
2046 bufInfo.push_back(newBufInfo);
2047
2048 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
2049
2050 bufCreateInfo.size = 128;
2051 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2052 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2053 assert(res == VK_SUCCESS);
2054 bufInfo.push_back(newBufInfo);
2055
2056 bufCreateInfo.size = 1024;
2057 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2058 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2059 assert(res == VK_SUCCESS);
2060 bufInfo.push_back(newBufInfo);
2061
2062 bufCreateInfo.size = 16;
2063 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
2064 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
2065 assert(res == VK_SUCCESS);
2066 bufInfo.push_back(newBufInfo);
2067
2068 VmaStats currStats;
2069 vmaCalculateStats(g_hAllocator, &currStats);
2070 VmaPoolStats poolStats;
2071 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2072
2073 char* statsStr = nullptr;
2074 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
2075
2076 // PUT BREAKPOINT HERE TO CHECK.
2077 // Inspect: currStats versus origStats, poolStats, statsStr.
2078 int I = 0;
2079
2080 vmaFreeStatsString(g_hAllocator, statsStr);
2081
2082 // Destroy the buffers in reverse order.
2083 while(!bufInfo.empty())
2084 {
2085 const BufferInfo& currBufInfo = bufInfo.back();
2086 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
2087 bufInfo.pop_back();
2088 }
2089 }
2090
2091 vmaDestroyPool(g_hAllocator, pool);
2092}
2093
Adam Sawicki0a607132018-08-24 11:18:41 +02002094static void BenchmarkLinearAllocatorCase(bool linear, bool empty, FREE_ORDER freeOrder)
2095{
2096 RandomNumberGenerator rand{16223};
2097
2098 const VkDeviceSize bufSizeMin = 32;
2099 const VkDeviceSize bufSizeMax = 1024;
2100 const size_t maxBufCapacity = 10000;
2101 const uint32_t iterationCount = 10;
2102
2103 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2104 sampleBufCreateInfo.size = bufSizeMax;
2105 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2106
2107 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2108 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2109
2110 VmaPoolCreateInfo poolCreateInfo = {};
2111 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2112 assert(res == VK_SUCCESS);
2113
2114 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
2115 if(linear)
2116 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2117 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2118
2119 VmaPool pool = nullptr;
2120 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2121 assert(res == VK_SUCCESS);
2122
2123 // Buffer created just to get memory requirements. Never bound to any memory.
2124 VkBuffer dummyBuffer = VK_NULL_HANDLE;
2125 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
2126 assert(res == VK_SUCCESS && dummyBuffer);
2127
2128 VkMemoryRequirements memReq = {};
2129 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2130
2131 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2132
2133 VmaAllocationCreateInfo allocCreateInfo = {};
2134 allocCreateInfo.pool = pool;
2135
2136 VmaAllocation alloc;
2137 std::vector<VmaAllocation> baseAllocations;
2138
2139 if(!empty)
2140 {
2141 // Make allocations up to half of pool size.
2142 VkDeviceSize totalSize = 0;
2143 while(totalSize < poolCreateInfo.blockSize / 2)
2144 {
2145 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2146 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2147 assert(res == VK_SUCCESS);
2148 baseAllocations.push_back(alloc);
2149 totalSize += memReq.size;
2150 }
2151
2152 // Delete half of them, choose randomly.
2153 size_t allocsToDelete = baseAllocations.size() / 2;
2154 for(size_t i = 0; i < allocsToDelete; ++i)
2155 {
2156 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2157 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2158 baseAllocations.erase(baseAllocations.begin() + index);
2159 }
2160 }
2161
2162 // BENCHMARK
2163 const size_t allocCount = maxBufCapacity / 2;
2164 std::vector<VmaAllocation> testAllocations;
2165 testAllocations.reserve(allocCount);
2166 duration allocTotalDuration = duration::zero();
2167 duration freeTotalDuration = duration::zero();
2168 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2169 {
2170 // Allocations
2171 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2172 for(size_t i = 0; i < allocCount; ++i)
2173 {
2174 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2175 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2176 assert(res == VK_SUCCESS);
2177 testAllocations.push_back(alloc);
2178 }
2179 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2180
2181 // Deallocations
2182 switch(freeOrder)
2183 {
2184 case FREE_ORDER::FORWARD:
2185 // Leave testAllocations unchanged.
2186 break;
2187 case FREE_ORDER::BACKWARD:
2188 std::reverse(testAllocations.begin(), testAllocations.end());
2189 break;
2190 case FREE_ORDER::RANDOM:
2191 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2192 break;
2193 default: assert(0);
2194 }
2195
2196 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2197 for(size_t i = 0; i < allocCount; ++i)
2198 vmaFreeMemory(g_hAllocator, testAllocations[i]);
2199 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2200
2201 testAllocations.clear();
2202 }
2203
2204 // Delete baseAllocations
2205 while(!baseAllocations.empty())
2206 {
2207 vmaFreeMemory(g_hAllocator, baseAllocations.back());
2208 baseAllocations.pop_back();
2209 }
2210
2211 vmaDestroyPool(g_hAllocator, pool);
2212
2213 wprintf(L" LinearAlgorithm=%u %s FreeOrder=%s: allocations %g s, free %g s\n",
2214 linear ? 1 : 0,
2215 empty ? L"Empty" : L"Not empty",
2216 FREE_ORDER_NAMES[(size_t)freeOrder],
2217 ToFloatSeconds(allocTotalDuration),
2218 ToFloatSeconds(freeTotalDuration));
2219}
2220
2221static void BenchmarkLinearAllocator()
2222{
2223 wprintf(L"Benchmark linear allocator\n");
2224
2225 uint32_t freeOrderCount = 1;
2226 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2227 freeOrderCount = 3;
2228 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2229 freeOrderCount = 2;
2230
2231 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
2232
2233 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2234 {
2235 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2236 switch(freeOrderIndex)
2237 {
2238 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2239 case 1: freeOrder = FREE_ORDER::FORWARD; break;
2240 case 2: freeOrder = FREE_ORDER::RANDOM; break;
2241 default: assert(0);
2242 }
2243
2244 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2245 {
2246 for(uint32_t linearIndex = 0; linearIndex < 2; ++linearIndex)
2247 {
2248 BenchmarkLinearAllocatorCase(
2249 linearIndex ? 1 : 0, // linear
2250 emptyIndex ? 0 : 1, // empty
2251 freeOrder); // freeOrder
2252 }
2253 }
2254 }
2255}
2256
Adam Sawickib8333fb2018-03-13 16:15:53 +01002257static void TestPool_SameSize()
2258{
2259 const VkDeviceSize BUF_SIZE = 1024 * 1024;
2260 const size_t BUF_COUNT = 100;
2261 VkResult res;
2262
2263 RandomNumberGenerator rand{123};
2264
2265 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2266 bufferInfo.size = BUF_SIZE;
2267 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2268
2269 uint32_t memoryTypeBits = UINT32_MAX;
2270 {
2271 VkBuffer dummyBuffer;
2272 res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2273 assert(res == VK_SUCCESS);
2274
2275 VkMemoryRequirements memReq;
2276 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2277 memoryTypeBits = memReq.memoryTypeBits;
2278
2279 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2280 }
2281
2282 VmaAllocationCreateInfo poolAllocInfo = {};
2283 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2284 uint32_t memTypeIndex;
2285 res = vmaFindMemoryTypeIndex(
2286 g_hAllocator,
2287 memoryTypeBits,
2288 &poolAllocInfo,
2289 &memTypeIndex);
2290
2291 VmaPoolCreateInfo poolCreateInfo = {};
2292 poolCreateInfo.memoryTypeIndex = memTypeIndex;
2293 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
2294 poolCreateInfo.minBlockCount = 1;
2295 poolCreateInfo.maxBlockCount = 4;
2296 poolCreateInfo.frameInUseCount = 0;
2297
2298 VmaPool pool;
2299 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2300 assert(res == VK_SUCCESS);
2301
2302 vmaSetCurrentFrameIndex(g_hAllocator, 1);
2303
2304 VmaAllocationCreateInfo allocInfo = {};
2305 allocInfo.pool = pool;
2306 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2307 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2308
2309 struct BufItem
2310 {
2311 VkBuffer Buf;
2312 VmaAllocation Alloc;
2313 };
2314 std::vector<BufItem> items;
2315
2316 // Fill entire pool.
2317 for(size_t i = 0; i < BUF_COUNT; ++i)
2318 {
2319 BufItem item;
2320 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2321 assert(res == VK_SUCCESS);
2322 items.push_back(item);
2323 }
2324
2325 // Make sure that another allocation would fail.
2326 {
2327 BufItem item;
2328 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2329 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
2330 }
2331
2332 // Validate that no buffer is lost. Also check that they are not mapped.
2333 for(size_t i = 0; i < items.size(); ++i)
2334 {
2335 VmaAllocationInfo allocInfo;
2336 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2337 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2338 assert(allocInfo.pMappedData == nullptr);
2339 }
2340
2341 // Free some percent of random items.
2342 {
2343 const size_t PERCENT_TO_FREE = 10;
2344 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
2345 for(size_t i = 0; i < itemsToFree; ++i)
2346 {
2347 size_t index = (size_t)rand.Generate() % items.size();
2348 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2349 items.erase(items.begin() + index);
2350 }
2351 }
2352
2353 // Randomly allocate and free items.
2354 {
2355 const size_t OPERATION_COUNT = BUF_COUNT;
2356 for(size_t i = 0; i < OPERATION_COUNT; ++i)
2357 {
2358 bool allocate = rand.Generate() % 2 != 0;
2359 if(allocate)
2360 {
2361 if(items.size() < BUF_COUNT)
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 else // Free
2370 {
2371 if(!items.empty())
2372 {
2373 size_t index = (size_t)rand.Generate() % items.size();
2374 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
2375 items.erase(items.begin() + index);
2376 }
2377 }
2378 }
2379 }
2380
2381 // Allocate up to maximum.
2382 while(items.size() < BUF_COUNT)
2383 {
2384 BufItem item;
2385 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2386 assert(res == VK_SUCCESS);
2387 items.push_back(item);
2388 }
2389
2390 // Validate that no buffer is lost.
2391 for(size_t i = 0; i < items.size(); ++i)
2392 {
2393 VmaAllocationInfo allocInfo;
2394 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2395 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2396 }
2397
2398 // Next frame.
2399 vmaSetCurrentFrameIndex(g_hAllocator, 2);
2400
2401 // Allocate another BUF_COUNT buffers.
2402 for(size_t i = 0; i < BUF_COUNT; ++i)
2403 {
2404 BufItem item;
2405 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2406 assert(res == VK_SUCCESS);
2407 items.push_back(item);
2408 }
2409
2410 // Make sure the first BUF_COUNT is lost. Delete them.
2411 for(size_t i = 0; i < BUF_COUNT; ++i)
2412 {
2413 VmaAllocationInfo allocInfo;
2414 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2415 assert(allocInfo.deviceMemory == VK_NULL_HANDLE);
2416 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2417 }
2418 items.erase(items.begin(), items.begin() + BUF_COUNT);
2419
2420 // Validate that no buffer is lost.
2421 for(size_t i = 0; i < items.size(); ++i)
2422 {
2423 VmaAllocationInfo allocInfo;
2424 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2425 assert(allocInfo.deviceMemory != VK_NULL_HANDLE);
2426 }
2427
2428 // Free one item.
2429 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
2430 items.pop_back();
2431
2432 // Validate statistics.
2433 {
2434 VmaPoolStats poolStats = {};
2435 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
2436 assert(poolStats.allocationCount == items.size());
2437 assert(poolStats.size = BUF_COUNT * BUF_SIZE);
2438 assert(poolStats.unusedRangeCount == 1);
2439 assert(poolStats.unusedRangeSizeMax == BUF_SIZE);
2440 assert(poolStats.unusedSize == BUF_SIZE);
2441 }
2442
2443 // Free all remaining items.
2444 for(size_t i = items.size(); i--; )
2445 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2446 items.clear();
2447
2448 // Allocate maximum items again.
2449 for(size_t i = 0; i < BUF_COUNT; ++i)
2450 {
2451 BufItem item;
2452 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2453 assert(res == VK_SUCCESS);
2454 items.push_back(item);
2455 }
2456
2457 // Delete every other item.
2458 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
2459 {
2460 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2461 items.erase(items.begin() + i);
2462 }
2463
2464 // Defragment!
2465 {
2466 std::vector<VmaAllocation> allocationsToDefragment(items.size());
2467 for(size_t i = 0; i < items.size(); ++i)
2468 allocationsToDefragment[i] = items[i].Alloc;
2469
2470 VmaDefragmentationStats defragmentationStats;
2471 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
2472 assert(res == VK_SUCCESS);
2473 assert(defragmentationStats.deviceMemoryBlocksFreed == 2);
2474 }
2475
2476 // Free all remaining items.
2477 for(size_t i = items.size(); i--; )
2478 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2479 items.clear();
2480
2481 ////////////////////////////////////////////////////////////////////////////////
2482 // Test for vmaMakePoolAllocationsLost
2483
2484 // Allocate 4 buffers on frame 10.
2485 vmaSetCurrentFrameIndex(g_hAllocator, 10);
2486 for(size_t i = 0; i < 4; ++i)
2487 {
2488 BufItem item;
2489 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
2490 assert(res == VK_SUCCESS);
2491 items.push_back(item);
2492 }
2493
2494 // Touch first 2 of them on frame 11.
2495 vmaSetCurrentFrameIndex(g_hAllocator, 11);
2496 for(size_t i = 0; i < 2; ++i)
2497 {
2498 VmaAllocationInfo allocInfo;
2499 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
2500 }
2501
2502 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
2503 size_t lostCount = 0xDEADC0DE;
2504 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2505 assert(lostCount == 2);
2506
2507 // Make another call. Now 0 should be lost.
2508 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
2509 assert(lostCount == 0);
2510
2511 // Make another call, with null count. Should not crash.
2512 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
2513
2514 // END: Free all remaining items.
2515 for(size_t i = items.size(); i--; )
2516 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
2517
2518 items.clear();
2519
Adam Sawickid2924172018-06-11 12:48:46 +02002520 ////////////////////////////////////////////////////////////////////////////////
2521 // Test for allocation too large for pool
2522
2523 {
2524 VmaAllocationCreateInfo allocCreateInfo = {};
2525 allocCreateInfo.pool = pool;
2526
2527 VkMemoryRequirements memReq;
2528 memReq.memoryTypeBits = UINT32_MAX;
2529 memReq.alignment = 1;
2530 memReq.size = poolCreateInfo.blockSize + 4;
2531
2532 VmaAllocation alloc = nullptr;
2533 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2534 assert(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
2535 }
2536
Adam Sawickib8333fb2018-03-13 16:15:53 +01002537 vmaDestroyPool(g_hAllocator, pool);
2538}
2539
Adam Sawickie44c6262018-06-15 14:30:39 +02002540static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
2541{
2542 const uint8_t* pBytes = (const uint8_t*)pMemory;
2543 for(size_t i = 0; i < size; ++i)
2544 {
2545 if(pBytes[i] != pattern)
2546 {
2547 return false;
2548 }
2549 }
2550 return true;
2551}
2552
2553static void TestAllocationsInitialization()
2554{
2555 VkResult res;
2556
2557 const size_t BUF_SIZE = 1024;
2558
2559 // Create pool.
2560
2561 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2562 bufInfo.size = BUF_SIZE;
2563 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2564
2565 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
2566 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2567
2568 VmaPoolCreateInfo poolCreateInfo = {};
2569 poolCreateInfo.blockSize = BUF_SIZE * 10;
2570 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
2571 poolCreateInfo.maxBlockCount = 1;
2572 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2573 assert(res == VK_SUCCESS);
2574
2575 VmaAllocationCreateInfo bufAllocCreateInfo = {};
2576 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
2577 assert(res == VK_SUCCESS);
2578
2579 // Create one persistently mapped buffer to keep memory of this block mapped,
2580 // so that pointer to mapped data will remain (more or less...) valid even
2581 // after destruction of other allocations.
2582
2583 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2584 VkBuffer firstBuf;
2585 VmaAllocation firstAlloc;
2586 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
2587 assert(res == VK_SUCCESS);
2588
2589 // Test buffers.
2590
2591 for(uint32_t i = 0; i < 2; ++i)
2592 {
2593 const bool persistentlyMapped = i == 0;
2594 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
2595 VkBuffer buf;
2596 VmaAllocation alloc;
2597 VmaAllocationInfo allocInfo;
2598 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
2599 assert(res == VK_SUCCESS);
2600
2601 void* pMappedData;
2602 if(!persistentlyMapped)
2603 {
2604 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
2605 assert(res == VK_SUCCESS);
2606 }
2607 else
2608 {
2609 pMappedData = allocInfo.pMappedData;
2610 }
2611
2612 // Validate initialized content
2613 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
2614 assert(valid);
2615
2616 if(!persistentlyMapped)
2617 {
2618 vmaUnmapMemory(g_hAllocator, alloc);
2619 }
2620
2621 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2622
2623 // Validate freed content
2624 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
2625 assert(valid);
2626 }
2627
2628 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
2629 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
2630}
2631
Adam Sawickib8333fb2018-03-13 16:15:53 +01002632static void TestPool_Benchmark(
2633 PoolTestResult& outResult,
2634 const PoolTestConfig& config)
2635{
2636 assert(config.ThreadCount > 0);
2637
2638 RandomNumberGenerator mainRand{config.RandSeed};
2639
2640 uint32_t allocationSizeProbabilitySum = std::accumulate(
2641 config.AllocationSizes.begin(),
2642 config.AllocationSizes.end(),
2643 0u,
2644 [](uint32_t sum, const AllocationSize& allocSize) {
2645 return sum + allocSize.Probability;
2646 });
2647
2648 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2649 bufferInfo.size = 256; // Whatever.
2650 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2651
2652 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2653 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2654 imageInfo.extent.width = 256; // Whatever.
2655 imageInfo.extent.height = 256; // Whatever.
2656 imageInfo.extent.depth = 1;
2657 imageInfo.mipLevels = 1;
2658 imageInfo.arrayLayers = 1;
2659 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2660 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
2661 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2662 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
2663 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2664
2665 uint32_t bufferMemoryTypeBits = UINT32_MAX;
2666 {
2667 VkBuffer dummyBuffer;
2668 VkResult res = vkCreateBuffer(g_hDevice, &bufferInfo, nullptr, &dummyBuffer);
2669 assert(res == VK_SUCCESS);
2670
2671 VkMemoryRequirements memReq;
2672 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2673 bufferMemoryTypeBits = memReq.memoryTypeBits;
2674
2675 vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2676 }
2677
2678 uint32_t imageMemoryTypeBits = UINT32_MAX;
2679 {
2680 VkImage dummyImage;
2681 VkResult res = vkCreateImage(g_hDevice, &imageInfo, nullptr, &dummyImage);
2682 assert(res == VK_SUCCESS);
2683
2684 VkMemoryRequirements memReq;
2685 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
2686 imageMemoryTypeBits = memReq.memoryTypeBits;
2687
2688 vkDestroyImage(g_hDevice, dummyImage, nullptr);
2689 }
2690
2691 uint32_t memoryTypeBits = 0;
2692 if(config.UsesBuffers() && config.UsesImages())
2693 {
2694 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
2695 if(memoryTypeBits == 0)
2696 {
2697 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
2698 return;
2699 }
2700 }
2701 else if(config.UsesBuffers())
2702 memoryTypeBits = bufferMemoryTypeBits;
2703 else if(config.UsesImages())
2704 memoryTypeBits = imageMemoryTypeBits;
2705 else
2706 assert(0);
2707
2708 VmaPoolCreateInfo poolCreateInfo = {};
2709 poolCreateInfo.memoryTypeIndex = 0;
2710 poolCreateInfo.minBlockCount = 1;
2711 poolCreateInfo.maxBlockCount = 1;
2712 poolCreateInfo.blockSize = config.PoolSize;
2713 poolCreateInfo.frameInUseCount = 1;
2714
2715 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
2716 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2717 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2718
2719 VmaPool pool;
2720 VkResult res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2721 assert(res == VK_SUCCESS);
2722
2723 // Start time measurement - after creating pool and initializing data structures.
2724 time_point timeBeg = std::chrono::high_resolution_clock::now();
2725
2726 ////////////////////////////////////////////////////////////////////////////////
2727 // ThreadProc
2728 auto ThreadProc = [&](
2729 PoolTestThreadResult* outThreadResult,
2730 uint32_t randSeed,
2731 HANDLE frameStartEvent,
2732 HANDLE frameEndEvent) -> void
2733 {
2734 RandomNumberGenerator threadRand{randSeed};
2735
2736 outThreadResult->AllocationTimeMin = duration::max();
2737 outThreadResult->AllocationTimeSum = duration::zero();
2738 outThreadResult->AllocationTimeMax = duration::min();
2739 outThreadResult->DeallocationTimeMin = duration::max();
2740 outThreadResult->DeallocationTimeSum = duration::zero();
2741 outThreadResult->DeallocationTimeMax = duration::min();
2742 outThreadResult->AllocationCount = 0;
2743 outThreadResult->DeallocationCount = 0;
2744 outThreadResult->LostAllocationCount = 0;
2745 outThreadResult->LostAllocationTotalSize = 0;
2746 outThreadResult->FailedAllocationCount = 0;
2747 outThreadResult->FailedAllocationTotalSize = 0;
2748
2749 struct Item
2750 {
2751 VkDeviceSize BufferSize;
2752 VkExtent2D ImageSize;
2753 VkBuffer Buf;
2754 VkImage Image;
2755 VmaAllocation Alloc;
2756
2757 VkDeviceSize CalcSizeBytes() const
2758 {
2759 return BufferSize +
2760 ImageSize.width * ImageSize.height * 4;
2761 }
2762 };
2763 std::vector<Item> unusedItems, usedItems;
2764
2765 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
2766
2767 // Create all items - all unused, not yet allocated.
2768 for(size_t i = 0; i < threadTotalItemCount; ++i)
2769 {
2770 Item item = {};
2771
2772 uint32_t allocSizeIndex = 0;
2773 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
2774 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
2775 r -= config.AllocationSizes[allocSizeIndex++].Probability;
2776
2777 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
2778 if(allocSize.BufferSizeMax > 0)
2779 {
2780 assert(allocSize.BufferSizeMin > 0);
2781 assert(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
2782 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
2783 item.BufferSize = allocSize.BufferSizeMin;
2784 else
2785 {
2786 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
2787 item.BufferSize = item.BufferSize / 16 * 16;
2788 }
2789 }
2790 else
2791 {
2792 assert(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
2793 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
2794 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
2795 else
2796 {
2797 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2798 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
2799 }
2800 }
2801
2802 unusedItems.push_back(item);
2803 }
2804
2805 auto Allocate = [&](Item& item) -> VkResult
2806 {
2807 VmaAllocationCreateInfo allocCreateInfo = {};
2808 allocCreateInfo.pool = pool;
2809 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
2810 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
2811
2812 if(item.BufferSize)
2813 {
2814 bufferInfo.size = item.BufferSize;
2815 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2816 return vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
2817 }
2818 else
2819 {
2820 assert(item.ImageSize.width && item.ImageSize.height);
2821
2822 imageInfo.extent.width = item.ImageSize.width;
2823 imageInfo.extent.height = item.ImageSize.height;
2824 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2825 return vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
2826 }
2827 };
2828
2829 ////////////////////////////////////////////////////////////////////////////////
2830 // Frames
2831 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2832 {
2833 WaitForSingleObject(frameStartEvent, INFINITE);
2834
2835 // Always make some percent of used bufs unused, to choose different used ones.
2836 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
2837 for(size_t i = 0; i < bufsToMakeUnused; ++i)
2838 {
2839 size_t index = threadRand.Generate() % usedItems.size();
2840 unusedItems.push_back(usedItems[index]);
2841 usedItems.erase(usedItems.begin() + index);
2842 }
2843
2844 // Determine which bufs we want to use in this frame.
2845 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
2846 / config.ThreadCount;
2847 assert(usedBufCount < usedItems.size() + unusedItems.size());
2848 // Move some used to unused.
2849 while(usedBufCount < usedItems.size())
2850 {
2851 size_t index = threadRand.Generate() % usedItems.size();
2852 unusedItems.push_back(usedItems[index]);
2853 usedItems.erase(usedItems.begin() + index);
2854 }
2855 // Move some unused to used.
2856 while(usedBufCount > usedItems.size())
2857 {
2858 size_t index = threadRand.Generate() % unusedItems.size();
2859 usedItems.push_back(unusedItems[index]);
2860 unusedItems.erase(unusedItems.begin() + index);
2861 }
2862
2863 uint32_t touchExistingCount = 0;
2864 uint32_t touchLostCount = 0;
2865 uint32_t createSucceededCount = 0;
2866 uint32_t createFailedCount = 0;
2867
2868 // Touch all used bufs. If not created or lost, allocate.
2869 for(size_t i = 0; i < usedItems.size(); ++i)
2870 {
2871 Item& item = usedItems[i];
2872 // Not yet created.
2873 if(item.Alloc == VK_NULL_HANDLE)
2874 {
2875 res = Allocate(item);
2876 ++outThreadResult->AllocationCount;
2877 if(res != VK_SUCCESS)
2878 {
2879 item.Alloc = VK_NULL_HANDLE;
2880 item.Buf = VK_NULL_HANDLE;
2881 ++outThreadResult->FailedAllocationCount;
2882 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2883 ++createFailedCount;
2884 }
2885 else
2886 ++createSucceededCount;
2887 }
2888 else
2889 {
2890 // Touch.
2891 VmaAllocationInfo allocInfo;
2892 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
2893 // Lost.
2894 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
2895 {
2896 ++touchLostCount;
2897
2898 // Destroy.
2899 {
2900 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2901 if(item.Buf)
2902 vmaDestroyBuffer(g_hAllocator, item.Buf, item.Alloc);
2903 else
2904 vmaDestroyImage(g_hAllocator, item.Image, item.Alloc);
2905 ++outThreadResult->DeallocationCount;
2906 }
2907 item.Alloc = VK_NULL_HANDLE;
2908 item.Buf = VK_NULL_HANDLE;
2909
2910 ++outThreadResult->LostAllocationCount;
2911 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
2912
2913 // Recreate.
2914 res = Allocate(item);
2915 ++outThreadResult->AllocationCount;
2916 // Creation failed.
2917 if(res != VK_SUCCESS)
2918 {
2919 ++outThreadResult->FailedAllocationCount;
2920 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
2921 ++createFailedCount;
2922 }
2923 else
2924 ++createSucceededCount;
2925 }
2926 else
2927 ++touchExistingCount;
2928 }
2929 }
2930
2931 /*
2932 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
2933 randSeed, frameIndex,
2934 touchExistingCount, touchLostCount,
2935 createSucceededCount, createFailedCount);
2936 */
2937
2938 SetEvent(frameEndEvent);
2939 }
2940
2941 // Free all remaining items.
2942 for(size_t i = usedItems.size(); i--; )
2943 {
2944 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
2945 if(usedItems[i].Buf)
2946 vmaDestroyBuffer(g_hAllocator, usedItems[i].Buf, usedItems[i].Alloc);
2947 else
2948 vmaDestroyImage(g_hAllocator, usedItems[i].Image, usedItems[i].Alloc);
2949 ++outThreadResult->DeallocationCount;
2950 }
2951 for(size_t i = unusedItems.size(); i--; )
2952 {
2953 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
2954 if(unusedItems[i].Buf)
2955 vmaDestroyBuffer(g_hAllocator, unusedItems[i].Buf, unusedItems[i].Alloc);
2956 else
2957 vmaDestroyImage(g_hAllocator, unusedItems[i].Image, unusedItems[i].Alloc);
2958 ++outThreadResult->DeallocationCount;
2959 }
2960 };
2961
2962 // Launch threads.
2963 uint32_t threadRandSeed = mainRand.Generate();
2964 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
2965 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
2966 std::vector<std::thread> bkgThreads;
2967 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
2968 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2969 {
2970 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2971 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
2972 bkgThreads.emplace_back(std::bind(
2973 ThreadProc,
2974 &threadResults[threadIndex],
2975 threadRandSeed + threadIndex,
2976 frameStartEvents[threadIndex],
2977 frameEndEvents[threadIndex]));
2978 }
2979
2980 // Execute frames.
2981 assert(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
2982 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
2983 {
2984 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
2985 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
2986 SetEvent(frameStartEvents[threadIndex]);
2987 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
2988 }
2989
2990 // Wait for threads finished
2991 for(size_t i = 0; i < bkgThreads.size(); ++i)
2992 {
2993 bkgThreads[i].join();
2994 CloseHandle(frameEndEvents[i]);
2995 CloseHandle(frameStartEvents[i]);
2996 }
2997 bkgThreads.clear();
2998
2999 // Finish time measurement - before destroying pool.
3000 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
3001
3002 vmaDestroyPool(g_hAllocator, pool);
3003
3004 outResult.AllocationTimeMin = duration::max();
3005 outResult.AllocationTimeAvg = duration::zero();
3006 outResult.AllocationTimeMax = duration::min();
3007 outResult.DeallocationTimeMin = duration::max();
3008 outResult.DeallocationTimeAvg = duration::zero();
3009 outResult.DeallocationTimeMax = duration::min();
3010 outResult.LostAllocationCount = 0;
3011 outResult.LostAllocationTotalSize = 0;
3012 outResult.FailedAllocationCount = 0;
3013 outResult.FailedAllocationTotalSize = 0;
3014 size_t allocationCount = 0;
3015 size_t deallocationCount = 0;
3016 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
3017 {
3018 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
3019 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
3020 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
3021 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
3022 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
3023 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
3024 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
3025 allocationCount += threadResult.AllocationCount;
3026 deallocationCount += threadResult.DeallocationCount;
3027 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
3028 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
3029 outResult.LostAllocationCount += threadResult.LostAllocationCount;
3030 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
3031 }
3032 if(allocationCount)
3033 outResult.AllocationTimeAvg /= allocationCount;
3034 if(deallocationCount)
3035 outResult.DeallocationTimeAvg /= deallocationCount;
3036}
3037
3038static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
3039{
3040 if(ptr1 < ptr2)
3041 return ptr1 + size1 > ptr2;
3042 else if(ptr2 < ptr1)
3043 return ptr2 + size2 > ptr1;
3044 else
3045 return true;
3046}
3047
3048static void TestMapping()
3049{
3050 wprintf(L"Testing mapping...\n");
3051
3052 VkResult res;
3053 uint32_t memTypeIndex = UINT32_MAX;
3054
3055 enum TEST
3056 {
3057 TEST_NORMAL,
3058 TEST_POOL,
3059 TEST_DEDICATED,
3060 TEST_COUNT
3061 };
3062 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3063 {
3064 VmaPool pool = nullptr;
3065 if(testIndex == TEST_POOL)
3066 {
3067 assert(memTypeIndex != UINT32_MAX);
3068 VmaPoolCreateInfo poolInfo = {};
3069 poolInfo.memoryTypeIndex = memTypeIndex;
3070 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3071 assert(res == VK_SUCCESS);
3072 }
3073
3074 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3075 bufInfo.size = 0x10000;
3076 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3077
3078 VmaAllocationCreateInfo allocCreateInfo = {};
3079 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3080 allocCreateInfo.pool = pool;
3081 if(testIndex == TEST_DEDICATED)
3082 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3083
3084 VmaAllocationInfo allocInfo;
3085
3086 // Mapped manually
3087
3088 // Create 2 buffers.
3089 BufferInfo bufferInfos[3];
3090 for(size_t i = 0; i < 2; ++i)
3091 {
3092 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3093 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
3094 assert(res == VK_SUCCESS);
3095 assert(allocInfo.pMappedData == nullptr);
3096 memTypeIndex = allocInfo.memoryType;
3097 }
3098
3099 // Map buffer 0.
3100 char* data00 = nullptr;
3101 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
3102 assert(res == VK_SUCCESS && data00 != nullptr);
3103 data00[0xFFFF] = data00[0];
3104
3105 // Map buffer 0 second time.
3106 char* data01 = nullptr;
3107 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
3108 assert(res == VK_SUCCESS && data01 == data00);
3109
3110 // Map buffer 1.
3111 char* data1 = nullptr;
3112 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
3113 assert(res == VK_SUCCESS && data1 != nullptr);
3114 assert(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
3115 data1[0xFFFF] = data1[0];
3116
3117 // Unmap buffer 0 two times.
3118 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3119 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
3120 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
3121 assert(allocInfo.pMappedData == nullptr);
3122
3123 // Unmap buffer 1.
3124 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
3125 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
3126 assert(allocInfo.pMappedData == nullptr);
3127
3128 // Create 3rd buffer - persistently mapped.
3129 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3130 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
3131 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
3132 assert(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
3133
3134 // Map buffer 2.
3135 char* data2 = nullptr;
3136 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
3137 assert(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
3138 data2[0xFFFF] = data2[0];
3139
3140 // Unmap buffer 2.
3141 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
3142 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
3143 assert(allocInfo.pMappedData == data2);
3144
3145 // Destroy all buffers.
3146 for(size_t i = 3; i--; )
3147 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
3148
3149 vmaDestroyPool(g_hAllocator, pool);
3150 }
3151}
3152
3153static void TestMappingMultithreaded()
3154{
3155 wprintf(L"Testing mapping multithreaded...\n");
3156
3157 static const uint32_t threadCount = 16;
3158 static const uint32_t bufferCount = 1024;
3159 static const uint32_t threadBufferCount = bufferCount / threadCount;
3160
3161 VkResult res;
3162 volatile uint32_t memTypeIndex = UINT32_MAX;
3163
3164 enum TEST
3165 {
3166 TEST_NORMAL,
3167 TEST_POOL,
3168 TEST_DEDICATED,
3169 TEST_COUNT
3170 };
3171 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
3172 {
3173 VmaPool pool = nullptr;
3174 if(testIndex == TEST_POOL)
3175 {
3176 assert(memTypeIndex != UINT32_MAX);
3177 VmaPoolCreateInfo poolInfo = {};
3178 poolInfo.memoryTypeIndex = memTypeIndex;
3179 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
3180 assert(res == VK_SUCCESS);
3181 }
3182
3183 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3184 bufCreateInfo.size = 0x10000;
3185 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3186
3187 VmaAllocationCreateInfo allocCreateInfo = {};
3188 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3189 allocCreateInfo.pool = pool;
3190 if(testIndex == TEST_DEDICATED)
3191 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3192
3193 std::thread threads[threadCount];
3194 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3195 {
3196 threads[threadIndex] = std::thread([=, &memTypeIndex](){
3197 // ======== THREAD FUNCTION ========
3198
3199 RandomNumberGenerator rand{threadIndex};
3200
3201 enum class MODE
3202 {
3203 // Don't map this buffer at all.
3204 DONT_MAP,
3205 // Map and quickly unmap.
3206 MAP_FOR_MOMENT,
3207 // Map and unmap before destruction.
3208 MAP_FOR_LONGER,
3209 // Map two times. Quickly unmap, second unmap before destruction.
3210 MAP_TWO_TIMES,
3211 // Create this buffer as persistently mapped.
3212 PERSISTENTLY_MAPPED,
3213 COUNT
3214 };
3215 std::vector<BufferInfo> bufInfos{threadBufferCount};
3216 std::vector<MODE> bufModes{threadBufferCount};
3217
3218 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
3219 {
3220 BufferInfo& bufInfo = bufInfos[bufferIndex];
3221 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
3222 bufModes[bufferIndex] = mode;
3223
3224 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
3225 if(mode == MODE::PERSISTENTLY_MAPPED)
3226 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
3227
3228 VmaAllocationInfo allocInfo;
3229 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
3230 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
3231 assert(res == VK_SUCCESS);
3232
3233 if(memTypeIndex == UINT32_MAX)
3234 memTypeIndex = allocInfo.memoryType;
3235
3236 char* data = nullptr;
3237
3238 if(mode == MODE::PERSISTENTLY_MAPPED)
3239 {
3240 data = (char*)allocInfo.pMappedData;
3241 assert(data != nullptr);
3242 }
3243 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
3244 mode == MODE::MAP_TWO_TIMES)
3245 {
3246 assert(data == nullptr);
3247 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
3248 assert(res == VK_SUCCESS && data != nullptr);
3249
3250 if(mode == MODE::MAP_TWO_TIMES)
3251 {
3252 char* data2 = nullptr;
3253 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
3254 assert(res == VK_SUCCESS && data2 == data);
3255 }
3256 }
3257 else if(mode == MODE::DONT_MAP)
3258 {
3259 assert(allocInfo.pMappedData == nullptr);
3260 }
3261 else
3262 assert(0);
3263
3264 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3265 if(data)
3266 data[0xFFFF] = data[0];
3267
3268 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
3269 {
3270 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
3271
3272 VmaAllocationInfo allocInfo;
3273 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
3274 if(mode == MODE::MAP_FOR_MOMENT)
3275 assert(allocInfo.pMappedData == nullptr);
3276 else
3277 assert(allocInfo.pMappedData == data);
3278 }
3279
3280 switch(rand.Generate() % 3)
3281 {
3282 case 0: Sleep(0); break; // Yield.
3283 case 1: Sleep(10); break; // 10 ms
3284 // default: No sleep.
3285 }
3286
3287 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
3288 if(data)
3289 data[0xFFFF] = data[0];
3290 }
3291
3292 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
3293 {
3294 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
3295 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
3296 {
3297 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
3298
3299 VmaAllocationInfo allocInfo;
3300 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
3301 assert(allocInfo.pMappedData == nullptr);
3302 }
3303
3304 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
3305 }
3306 });
3307 }
3308
3309 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
3310 threads[threadIndex].join();
3311
3312 vmaDestroyPool(g_hAllocator, pool);
3313 }
3314}
3315
3316static void WriteMainTestResultHeader(FILE* file)
3317{
3318 fprintf(file,
3319 "Code,Test,Time,"
3320 "Config,"
3321 "Total Time (us),"
3322 "Allocation Time Min (us),"
3323 "Allocation Time Avg (us),"
3324 "Allocation Time Max (us),"
3325 "Deallocation Time Min (us),"
3326 "Deallocation Time Avg (us),"
3327 "Deallocation Time Max (us),"
3328 "Total Memory Allocated (B),"
3329 "Free Range Size Avg (B),"
3330 "Free Range Size Max (B)\n");
3331}
3332
3333static void WriteMainTestResult(
3334 FILE* file,
3335 const char* codeDescription,
3336 const char* testDescription,
3337 const Config& config, const Result& result)
3338{
3339 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3340 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3341 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3342 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3343 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3344 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3345 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3346
3347 time_t rawTime; time(&rawTime);
3348 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3349 char timeStr[128];
3350 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3351
3352 fprintf(file,
3353 "%s,%s,%s,"
3354 "BeginBytesToAllocate=%I64u MaxBytesToAllocate=%I64u AdditionalOperationCount=%u ThreadCount=%u FreeOrder=%d,"
3355 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
3356 codeDescription,
3357 testDescription,
3358 timeStr,
3359 config.BeginBytesToAllocate, config.MaxBytesToAllocate, config.AdditionalOperationCount, config.ThreadCount, (uint32_t)config.FreeOrder,
3360 totalTimeSeconds * 1e6f,
3361 allocationTimeMinSeconds * 1e6f,
3362 allocationTimeAvgSeconds * 1e6f,
3363 allocationTimeMaxSeconds * 1e6f,
3364 deallocationTimeMinSeconds * 1e6f,
3365 deallocationTimeAvgSeconds * 1e6f,
3366 deallocationTimeMaxSeconds * 1e6f,
3367 result.TotalMemoryAllocated,
3368 result.FreeRangeSizeAvg,
3369 result.FreeRangeSizeMax);
3370}
3371
3372static void WritePoolTestResultHeader(FILE* file)
3373{
3374 fprintf(file,
3375 "Code,Test,Time,"
3376 "Config,"
3377 "Total Time (us),"
3378 "Allocation Time Min (us),"
3379 "Allocation Time Avg (us),"
3380 "Allocation Time Max (us),"
3381 "Deallocation Time Min (us),"
3382 "Deallocation Time Avg (us),"
3383 "Deallocation Time Max (us),"
3384 "Lost Allocation Count,"
3385 "Lost Allocation Total Size (B),"
3386 "Failed Allocation Count,"
3387 "Failed Allocation Total Size (B)\n");
3388}
3389
3390static void WritePoolTestResult(
3391 FILE* file,
3392 const char* codeDescription,
3393 const char* testDescription,
3394 const PoolTestConfig& config,
3395 const PoolTestResult& result)
3396{
3397 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
3398 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
3399 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
3400 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
3401 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
3402 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
3403 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
3404
3405 time_t rawTime; time(&rawTime);
3406 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
3407 char timeStr[128];
3408 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
3409
3410 fprintf(file,
3411 "%s,%s,%s,"
3412 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
3413 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
3414 // General
3415 codeDescription,
3416 testDescription,
3417 timeStr,
3418 // Config
3419 config.ThreadCount,
3420 (unsigned long long)config.PoolSize,
3421 config.FrameCount,
3422 config.TotalItemCount,
3423 config.UsedItemCountMin,
3424 config.UsedItemCountMax,
3425 config.ItemsToMakeUnusedPercent,
3426 // Results
3427 totalTimeSeconds * 1e6f,
3428 allocationTimeMinSeconds * 1e6f,
3429 allocationTimeAvgSeconds * 1e6f,
3430 allocationTimeMaxSeconds * 1e6f,
3431 deallocationTimeMinSeconds * 1e6f,
3432 deallocationTimeAvgSeconds * 1e6f,
3433 deallocationTimeMaxSeconds * 1e6f,
3434 result.LostAllocationCount,
3435 result.LostAllocationTotalSize,
3436 result.FailedAllocationCount,
3437 result.FailedAllocationTotalSize);
3438}
3439
3440static void PerformCustomMainTest(FILE* file)
3441{
3442 Config config{};
3443 config.RandSeed = 65735476;
3444 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
3445 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3446 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3447 config.FreeOrder = FREE_ORDER::FORWARD;
3448 config.ThreadCount = 16;
3449 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3450
3451 // Buffers
3452 //config.AllocationSizes.push_back({4, 16, 1024});
3453 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3454
3455 // Images
3456 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3457 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3458
3459 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3460 config.AdditionalOperationCount = 1024;
3461
3462 Result result{};
3463 VkResult res = MainTest(result, config);
3464 assert(res == VK_SUCCESS);
3465 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
3466}
3467
3468static void PerformCustomPoolTest(FILE* file)
3469{
3470 PoolTestConfig config;
3471 config.PoolSize = 100 * 1024 * 1024;
3472 config.RandSeed = 2345764;
3473 config.ThreadCount = 1;
3474 config.FrameCount = 200;
3475 config.ItemsToMakeUnusedPercent = 2;
3476
3477 AllocationSize allocSize = {};
3478 allocSize.BufferSizeMin = 1024;
3479 allocSize.BufferSizeMax = 1024 * 1024;
3480 allocSize.Probability = 1;
3481 config.AllocationSizes.push_back(allocSize);
3482
3483 allocSize.BufferSizeMin = 0;
3484 allocSize.BufferSizeMax = 0;
3485 allocSize.ImageSizeMin = 128;
3486 allocSize.ImageSizeMax = 1024;
3487 allocSize.Probability = 1;
3488 config.AllocationSizes.push_back(allocSize);
3489
3490 config.PoolSize = config.CalcAvgResourceSize() * 200;
3491 config.UsedItemCountMax = 160;
3492 config.TotalItemCount = config.UsedItemCountMax * 10;
3493 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3494
3495 g_MemoryAliasingWarningEnabled = false;
3496 PoolTestResult result = {};
3497 TestPool_Benchmark(result, config);
3498 g_MemoryAliasingWarningEnabled = true;
3499
3500 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
3501}
3502
Adam Sawickib8333fb2018-03-13 16:15:53 +01003503static const char* CODE_DESCRIPTION = "Foo";
3504
3505static void PerformMainTests(FILE* file)
3506{
3507 uint32_t repeatCount = 1;
3508 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3509
3510 Config config{};
3511 config.RandSeed = 65735476;
3512 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
3513 config.FreeOrder = FREE_ORDER::FORWARD;
3514
3515 size_t threadCountCount = 1;
3516 switch(ConfigType)
3517 {
3518 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3519 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3520 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
3521 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
3522 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
3523 default: assert(0);
3524 }
3525 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3526 {
3527 std::string desc1;
3528
3529 switch(threadCountIndex)
3530 {
3531 case 0:
3532 desc1 += "1_thread";
3533 config.ThreadCount = 1;
3534 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3535 break;
3536 case 1:
3537 desc1 += "16_threads+0%_common";
3538 config.ThreadCount = 16;
3539 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3540 break;
3541 case 2:
3542 desc1 += "16_threads+50%_common";
3543 config.ThreadCount = 16;
3544 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3545 break;
3546 case 3:
3547 desc1 += "16_threads+100%_common";
3548 config.ThreadCount = 16;
3549 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3550 break;
3551 case 4:
3552 desc1 += "2_threads+0%_common";
3553 config.ThreadCount = 2;
3554 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
3555 break;
3556 case 5:
3557 desc1 += "2_threads+50%_common";
3558 config.ThreadCount = 2;
3559 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
3560 break;
3561 case 6:
3562 desc1 += "2_threads+100%_common";
3563 config.ThreadCount = 2;
3564 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
3565 break;
3566 default:
3567 assert(0);
3568 }
3569
3570 // 0 = buffers, 1 = images, 2 = buffers and images
3571 size_t buffersVsImagesCount = 2;
3572 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3573 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3574 {
3575 std::string desc2 = desc1;
3576 switch(buffersVsImagesIndex)
3577 {
3578 case 0: desc2 += " Buffers"; break;
3579 case 1: desc2 += " Images"; break;
3580 case 2: desc2 += " Buffers+Images"; break;
3581 default: assert(0);
3582 }
3583
3584 // 0 = small, 1 = large, 2 = small and large
3585 size_t smallVsLargeCount = 2;
3586 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3587 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3588 {
3589 std::string desc3 = desc2;
3590 switch(smallVsLargeIndex)
3591 {
3592 case 0: desc3 += " Small"; break;
3593 case 1: desc3 += " Large"; break;
3594 case 2: desc3 += " Small+Large"; break;
3595 default: assert(0);
3596 }
3597
3598 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3599 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
3600 else
3601 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
3602
3603 // 0 = varying sizes min...max, 1 = set of constant sizes
3604 size_t constantSizesCount = 1;
3605 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3606 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3607 {
3608 std::string desc4 = desc3;
3609 switch(constantSizesIndex)
3610 {
3611 case 0: desc4 += " Varying_sizes"; break;
3612 case 1: desc4 += " Constant_sizes"; break;
3613 default: assert(0);
3614 }
3615
3616 config.AllocationSizes.clear();
3617 // Buffers present
3618 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3619 {
3620 // Small
3621 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3622 {
3623 // Varying size
3624 if(constantSizesIndex == 0)
3625 config.AllocationSizes.push_back({4, 16, 1024});
3626 // Constant sizes
3627 else
3628 {
3629 config.AllocationSizes.push_back({1, 16, 16});
3630 config.AllocationSizes.push_back({1, 64, 64});
3631 config.AllocationSizes.push_back({1, 256, 256});
3632 config.AllocationSizes.push_back({1, 1024, 1024});
3633 }
3634 }
3635 // Large
3636 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3637 {
3638 // Varying size
3639 if(constantSizesIndex == 0)
3640 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3641 // Constant sizes
3642 else
3643 {
3644 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3645 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3646 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3647 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3648 }
3649 }
3650 }
3651 // Images present
3652 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3653 {
3654 // Small
3655 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3656 {
3657 // Varying size
3658 if(constantSizesIndex == 0)
3659 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3660 // Constant sizes
3661 else
3662 {
3663 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3664 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3665 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3666 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3667 }
3668 }
3669 // Large
3670 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3671 {
3672 // Varying size
3673 if(constantSizesIndex == 0)
3674 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3675 // Constant sizes
3676 else
3677 {
3678 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3679 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3680 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3681 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3682 }
3683 }
3684 }
3685
3686 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
3687 size_t beginBytesToAllocateCount = 1;
3688 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
3689 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
3690 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
3691 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
3692 {
3693 std::string desc5 = desc4;
3694
3695 switch(beginBytesToAllocateIndex)
3696 {
3697 case 0:
3698 desc5 += " Allocate_100%";
3699 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
3700 config.AdditionalOperationCount = 0;
3701 break;
3702 case 1:
3703 desc5 += " Allocate_50%+Operations";
3704 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
3705 config.AdditionalOperationCount = 1024;
3706 break;
3707 case 2:
3708 desc5 += " Allocate_5%+Operations";
3709 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
3710 config.AdditionalOperationCount = 1024;
3711 break;
3712 case 3:
3713 desc5 += " Allocate_95%+Operations";
3714 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
3715 config.AdditionalOperationCount = 1024;
3716 break;
3717 default:
3718 assert(0);
3719 }
3720
3721 const char* testDescription = desc5.c_str();
3722
3723 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3724 {
3725 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3726
3727 Result result{};
3728 VkResult res = MainTest(result, config);
3729 assert(res == VK_SUCCESS);
3730 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3731 }
3732 }
3733 }
3734 }
3735 }
3736 }
3737}
3738
3739static void PerformPoolTests(FILE* file)
3740{
3741 const size_t AVG_RESOURCES_PER_POOL = 300;
3742
3743 uint32_t repeatCount = 1;
3744 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
3745
3746 PoolTestConfig config{};
3747 config.RandSeed = 2346343;
3748 config.FrameCount = 200;
3749 config.ItemsToMakeUnusedPercent = 2;
3750
3751 size_t threadCountCount = 1;
3752 switch(ConfigType)
3753 {
3754 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
3755 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
3756 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
3757 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
3758 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
3759 default: assert(0);
3760 }
3761 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
3762 {
3763 std::string desc1;
3764
3765 switch(threadCountIndex)
3766 {
3767 case 0:
3768 desc1 += "1_thread";
3769 config.ThreadCount = 1;
3770 break;
3771 case 1:
3772 desc1 += "16_threads";
3773 config.ThreadCount = 16;
3774 break;
3775 case 2:
3776 desc1 += "2_threads";
3777 config.ThreadCount = 2;
3778 break;
3779 default:
3780 assert(0);
3781 }
3782
3783 // 0 = buffers, 1 = images, 2 = buffers and images
3784 size_t buffersVsImagesCount = 2;
3785 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
3786 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
3787 {
3788 std::string desc2 = desc1;
3789 switch(buffersVsImagesIndex)
3790 {
3791 case 0: desc2 += " Buffers"; break;
3792 case 1: desc2 += " Images"; break;
3793 case 2: desc2 += " Buffers+Images"; break;
3794 default: assert(0);
3795 }
3796
3797 // 0 = small, 1 = large, 2 = small and large
3798 size_t smallVsLargeCount = 2;
3799 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
3800 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
3801 {
3802 std::string desc3 = desc2;
3803 switch(smallVsLargeIndex)
3804 {
3805 case 0: desc3 += " Small"; break;
3806 case 1: desc3 += " Large"; break;
3807 case 2: desc3 += " Small+Large"; break;
3808 default: assert(0);
3809 }
3810
3811 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3812 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
3813 else
3814 config.PoolSize = 4ull * 1024 * 1024;
3815
3816 // 0 = varying sizes min...max, 1 = set of constant sizes
3817 size_t constantSizesCount = 1;
3818 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
3819 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
3820 {
3821 std::string desc4 = desc3;
3822 switch(constantSizesIndex)
3823 {
3824 case 0: desc4 += " Varying_sizes"; break;
3825 case 1: desc4 += " Constant_sizes"; break;
3826 default: assert(0);
3827 }
3828
3829 config.AllocationSizes.clear();
3830 // Buffers present
3831 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
3832 {
3833 // Small
3834 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3835 {
3836 // Varying size
3837 if(constantSizesIndex == 0)
3838 config.AllocationSizes.push_back({4, 16, 1024});
3839 // Constant sizes
3840 else
3841 {
3842 config.AllocationSizes.push_back({1, 16, 16});
3843 config.AllocationSizes.push_back({1, 64, 64});
3844 config.AllocationSizes.push_back({1, 256, 256});
3845 config.AllocationSizes.push_back({1, 1024, 1024});
3846 }
3847 }
3848 // Large
3849 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3850 {
3851 // Varying size
3852 if(constantSizesIndex == 0)
3853 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
3854 // Constant sizes
3855 else
3856 {
3857 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
3858 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
3859 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
3860 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
3861 }
3862 }
3863 }
3864 // Images present
3865 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
3866 {
3867 // Small
3868 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
3869 {
3870 // Varying size
3871 if(constantSizesIndex == 0)
3872 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
3873 // Constant sizes
3874 else
3875 {
3876 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
3877 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
3878 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
3879 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
3880 }
3881 }
3882 // Large
3883 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
3884 {
3885 // Varying size
3886 if(constantSizesIndex == 0)
3887 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
3888 // Constant sizes
3889 else
3890 {
3891 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
3892 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
3893 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
3894 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
3895 }
3896 }
3897 }
3898
3899 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
3900 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
3901
3902 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
3903 size_t subscriptionModeCount;
3904 switch(ConfigType)
3905 {
3906 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
3907 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
3908 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
3909 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
3910 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
3911 default: assert(0);
3912 }
3913 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
3914 {
3915 std::string desc5 = desc4;
3916
3917 switch(subscriptionModeIndex)
3918 {
3919 case 0:
3920 desc5 += " Subscription_66%";
3921 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
3922 break;
3923 case 1:
3924 desc5 += " Subscription_133%";
3925 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
3926 break;
3927 case 2:
3928 desc5 += " Subscription_100%";
3929 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
3930 break;
3931 case 3:
3932 desc5 += " Subscription_33%";
3933 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
3934 break;
3935 case 4:
3936 desc5 += " Subscription_166%";
3937 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
3938 break;
3939 default:
3940 assert(0);
3941 }
3942
3943 config.TotalItemCount = config.UsedItemCountMax * 5;
3944 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
3945
3946 const char* testDescription = desc5.c_str();
3947
3948 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
3949 {
3950 printf("%s Repeat %u\n", testDescription, (uint32_t)repeat);
3951
3952 PoolTestResult result{};
3953 g_MemoryAliasingWarningEnabled = false;
3954 TestPool_Benchmark(result, config);
3955 g_MemoryAliasingWarningEnabled = true;
3956 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
3957 }
3958 }
3959 }
3960 }
3961 }
3962 }
3963}
3964
3965void Test()
3966{
3967 wprintf(L"TESTING:\n");
3968
Adam Sawicki70a683e2018-08-24 15:36:32 +02003969 if(false)
3970 {
3971 // # Temporarily insert custom tests here
3972TestLinearAllocator();
3973ManuallyTestLinearAllocator();
3974TestLinearAllocatorMultiBlock();
3975BenchmarkLinearAllocator();
3976 return;
3977 }
3978
Adam Sawickib8333fb2018-03-13 16:15:53 +01003979 // # Simple tests
3980
3981 TestBasics();
Adam Sawicki212a4a62018-06-14 15:44:45 +02003982#if VMA_DEBUG_MARGIN
3983 TestDebugMargin();
3984#else
3985 TestPool_SameSize();
3986 TestHeapSizeLimit();
3987#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02003988#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
3989 TestAllocationsInitialization();
3990#endif
Adam Sawickib8333fb2018-03-13 16:15:53 +01003991 TestMapping();
3992 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003993 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003994 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02003995 TestLinearAllocatorMultiBlock();
Adam Sawicki0a607132018-08-24 11:18:41 +02003996 BenchmarkLinearAllocator();
Adam Sawickib8333fb2018-03-13 16:15:53 +01003997 TestDefragmentationSimple();
3998 TestDefragmentationFull();
3999
4000 // # Detailed tests
4001 FILE* file;
4002 fopen_s(&file, "Results.csv", "w");
4003 assert(file != NULL);
4004
4005 WriteMainTestResultHeader(file);
4006 PerformMainTests(file);
4007 //PerformCustomMainTest(file);
4008
4009 WritePoolTestResultHeader(file);
4010 PerformPoolTests(file);
4011 //PerformCustomPoolTest(file);
4012
4013 fclose(file);
4014
4015 wprintf(L"Done.\n");
4016}
4017
Adam Sawickif1a793c2018-03-13 15:42:22 +01004018#endif // #ifdef _WIN32