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