blob: d384781c2a0480fd723525a805b976dba1c5c149 [file] [log] [blame]
Adam Sawickiae5c4662019-01-02 10:23:35 +01001//
Adam Sawickiaa183742021-02-16 17:28:49 +01002// Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved.
Adam Sawickiae5c4662019-01-02 10:23:35 +01003//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
Adam Sawickif1a793c2018-03-13 15:42:22 +010023#include "Tests.h"
24#include "VmaUsage.h"
25#include "Common.h"
Adam Sawickib8333fb2018-03-13 16:15:53 +010026#include <atomic>
27#include <thread>
28#include <mutex>
Adam Sawicki94ce3d72019-04-17 14:59:25 +020029#include <functional>
Adam Sawickif1a793c2018-03-13 15:42:22 +010030
31#ifdef _WIN32
32
Adam Sawicki33d2ce72018-08-27 13:59:13 +020033static const char* CODE_DESCRIPTION = "Foo";
34
Adam Sawickif2975342018-10-16 13:49:02 +020035extern VkCommandBuffer g_hTemporaryCommandBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +020036extern const VkAllocationCallbacks* g_Allocs;
Adam Sawickie73e9882020-03-20 18:05:42 +010037extern bool g_BufferDeviceAddressEnabled;
Adam Sawickif2012052021-01-11 18:04:42 +010038extern bool VK_EXT_memory_priority_enabled;
Adam Sawickie73e9882020-03-20 18:05:42 +010039extern PFN_vkGetBufferDeviceAddressEXT g_vkGetBufferDeviceAddressEXT;
Adam Sawickif2975342018-10-16 13:49:02 +020040void BeginSingleTimeCommands();
41void EndSingleTimeCommands();
Adam Sawicki41b41112021-03-02 15:11:18 +010042void SetDebugUtilsObjectName(VkObjectType type, uint64_t handle, const char* name);
Adam Sawickif2975342018-10-16 13:49:02 +020043
Adam Sawickibdb89a92018-12-13 11:56:30 +010044#ifndef VMA_DEBUG_MARGIN
45 #define VMA_DEBUG_MARGIN 0
46#endif
47
Adam Sawicki0a607132018-08-24 11:18:41 +020048enum CONFIG_TYPE {
49 CONFIG_TYPE_MINIMUM,
50 CONFIG_TYPE_SMALL,
51 CONFIG_TYPE_AVERAGE,
52 CONFIG_TYPE_LARGE,
53 CONFIG_TYPE_MAXIMUM,
54 CONFIG_TYPE_COUNT
55};
56
Adam Sawickif2975342018-10-16 13:49:02 +020057static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
58//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
Adam Sawicki0a607132018-08-24 11:18:41 +020059
Adam Sawickib8333fb2018-03-13 16:15:53 +010060enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
61
Adam Sawicki0667e332018-08-24 17:26:44 +020062static const char* FREE_ORDER_NAMES[] = {
63 "FORWARD",
64 "BACKWARD",
65 "RANDOM",
Adam Sawicki0a607132018-08-24 11:18:41 +020066};
67
Adam Sawicki80927152018-09-07 17:27:23 +020068// Copy of internal VmaAlgorithmToStr.
69static const char* AlgorithmToStr(uint32_t algorithm)
70{
71 switch(algorithm)
72 {
73 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
74 return "Linear";
75 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
76 return "Buddy";
77 case 0:
78 return "Default";
79 default:
80 assert(0);
81 return "";
82 }
83}
84
Adam Sawickib8333fb2018-03-13 16:15:53 +010085struct AllocationSize
86{
87 uint32_t Probability;
88 VkDeviceSize BufferSizeMin, BufferSizeMax;
89 uint32_t ImageSizeMin, ImageSizeMax;
90};
91
92struct Config
93{
94 uint32_t RandSeed;
95 VkDeviceSize BeginBytesToAllocate;
96 uint32_t AdditionalOperationCount;
97 VkDeviceSize MaxBytesToAllocate;
98 uint32_t MemUsageProbability[4]; // For VMA_MEMORY_USAGE_*
99 std::vector<AllocationSize> AllocationSizes;
100 uint32_t ThreadCount;
101 uint32_t ThreadsUsingCommonAllocationsProbabilityPercent;
102 FREE_ORDER FreeOrder;
Adam Sawicki0667e332018-08-24 17:26:44 +0200103 VmaAllocationCreateFlags AllocationStrategy; // For VMA_ALLOCATION_CREATE_STRATEGY_*
Adam Sawickib8333fb2018-03-13 16:15:53 +0100104};
105
106struct Result
107{
108 duration TotalTime;
109 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
110 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
111 VkDeviceSize TotalMemoryAllocated;
112 VkDeviceSize FreeRangeSizeAvg, FreeRangeSizeMax;
113};
114
115void TestDefragmentationSimple();
116void TestDefragmentationFull();
117
118struct PoolTestConfig
119{
120 uint32_t RandSeed;
121 uint32_t ThreadCount;
122 VkDeviceSize PoolSize;
123 uint32_t FrameCount;
124 uint32_t TotalItemCount;
125 // Range for number of items used in each frame.
126 uint32_t UsedItemCountMin, UsedItemCountMax;
127 // Percent of items to make unused, and possibly make some others used in each frame.
128 uint32_t ItemsToMakeUnusedPercent;
129 std::vector<AllocationSize> AllocationSizes;
130
131 VkDeviceSize CalcAvgResourceSize() const
132 {
133 uint32_t probabilitySum = 0;
134 VkDeviceSize sizeSum = 0;
135 for(size_t i = 0; i < AllocationSizes.size(); ++i)
136 {
137 const AllocationSize& allocSize = AllocationSizes[i];
138 if(allocSize.BufferSizeMax > 0)
139 sizeSum += (allocSize.BufferSizeMin + allocSize.BufferSizeMax) / 2 * allocSize.Probability;
140 else
141 {
142 const VkDeviceSize avgDimension = (allocSize.ImageSizeMin + allocSize.ImageSizeMax) / 2;
143 sizeSum += avgDimension * avgDimension * 4 * allocSize.Probability;
144 }
145 probabilitySum += allocSize.Probability;
146 }
147 return sizeSum / probabilitySum;
148 }
149
150 bool UsesBuffers() const
151 {
152 for(size_t i = 0; i < AllocationSizes.size(); ++i)
153 if(AllocationSizes[i].BufferSizeMax > 0)
154 return true;
155 return false;
156 }
157
158 bool UsesImages() const
159 {
160 for(size_t i = 0; i < AllocationSizes.size(); ++i)
161 if(AllocationSizes[i].ImageSizeMax > 0)
162 return true;
163 return false;
164 }
165};
166
167struct PoolTestResult
168{
169 duration TotalTime;
170 duration AllocationTimeMin, AllocationTimeAvg, AllocationTimeMax;
171 duration DeallocationTimeMin, DeallocationTimeAvg, DeallocationTimeMax;
172 size_t LostAllocationCount, LostAllocationTotalSize;
173 size_t FailedAllocationCount, FailedAllocationTotalSize;
174};
175
176static const uint32_t IMAGE_BYTES_PER_PIXEL = 1;
177
Adam Sawicki51fa9662018-10-03 13:44:29 +0200178uint32_t g_FrameIndex = 0;
Adam Sawicki8cfe05f2018-08-22 16:48:17 +0200179
Adam Sawickib8333fb2018-03-13 16:15:53 +0100180struct BufferInfo
181{
182 VkBuffer Buffer = VK_NULL_HANDLE;
183 VmaAllocation Allocation = VK_NULL_HANDLE;
184};
185
Adam Sawicki40ffe982019-10-11 15:56:02 +0200186static uint32_t MemoryTypeToHeap(uint32_t memoryTypeIndex)
187{
188 const VkPhysicalDeviceMemoryProperties* props;
189 vmaGetMemoryProperties(g_hAllocator, &props);
190 return props->memoryTypes[memoryTypeIndex].heapIndex;
191}
192
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +0200193static uint32_t GetAllocationStrategyCount()
194{
195 uint32_t strategyCount = 0;
196 switch(ConfigType)
197 {
198 case CONFIG_TYPE_MINIMUM: strategyCount = 1; break;
199 case CONFIG_TYPE_SMALL: strategyCount = 1; break;
200 case CONFIG_TYPE_AVERAGE: strategyCount = 2; break;
201 case CONFIG_TYPE_LARGE: strategyCount = 2; break;
202 case CONFIG_TYPE_MAXIMUM: strategyCount = 3; break;
203 default: assert(0);
204 }
205 return strategyCount;
206}
207
208static const char* GetAllocationStrategyName(VmaAllocationCreateFlags allocStrategy)
209{
210 switch(allocStrategy)
211 {
212 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: return "BEST_FIT"; break;
213 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: return "WORST_FIT"; break;
214 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: return "FIRST_FIT"; break;
215 case 0: return "Default"; break;
216 default: assert(0); return "";
217 }
218}
219
Adam Sawickib8333fb2018-03-13 16:15:53 +0100220static void InitResult(Result& outResult)
221{
222 outResult.TotalTime = duration::zero();
223 outResult.AllocationTimeMin = duration::max();
224 outResult.AllocationTimeAvg = duration::zero();
225 outResult.AllocationTimeMax = duration::min();
226 outResult.DeallocationTimeMin = duration::max();
227 outResult.DeallocationTimeAvg = duration::zero();
228 outResult.DeallocationTimeMax = duration::min();
229 outResult.TotalMemoryAllocated = 0;
230 outResult.FreeRangeSizeAvg = 0;
231 outResult.FreeRangeSizeMax = 0;
232}
233
234class TimeRegisterObj
235{
236public:
237 TimeRegisterObj(duration& min, duration& sum, duration& max) :
238 m_Min(min),
239 m_Sum(sum),
240 m_Max(max),
241 m_TimeBeg(std::chrono::high_resolution_clock::now())
242 {
243 }
244
245 ~TimeRegisterObj()
246 {
247 duration d = std::chrono::high_resolution_clock::now() - m_TimeBeg;
248 m_Sum += d;
249 if(d < m_Min) m_Min = d;
250 if(d > m_Max) m_Max = d;
251 }
252
253private:
254 duration& m_Min;
255 duration& m_Sum;
256 duration& m_Max;
257 time_point m_TimeBeg;
258};
259
260struct PoolTestThreadResult
261{
262 duration AllocationTimeMin, AllocationTimeSum, AllocationTimeMax;
263 duration DeallocationTimeMin, DeallocationTimeSum, DeallocationTimeMax;
264 size_t AllocationCount, DeallocationCount;
265 size_t LostAllocationCount, LostAllocationTotalSize;
266 size_t FailedAllocationCount, FailedAllocationTotalSize;
267};
268
269class AllocationTimeRegisterObj : public TimeRegisterObj
270{
271public:
272 AllocationTimeRegisterObj(Result& result) :
273 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeAvg, result.AllocationTimeMax)
274 {
275 }
276};
277
278class DeallocationTimeRegisterObj : public TimeRegisterObj
279{
280public:
281 DeallocationTimeRegisterObj(Result& result) :
282 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeAvg, result.DeallocationTimeMax)
283 {
284 }
285};
286
287class PoolAllocationTimeRegisterObj : public TimeRegisterObj
288{
289public:
290 PoolAllocationTimeRegisterObj(PoolTestThreadResult& result) :
291 TimeRegisterObj(result.AllocationTimeMin, result.AllocationTimeSum, result.AllocationTimeMax)
292 {
293 }
294};
295
296class PoolDeallocationTimeRegisterObj : public TimeRegisterObj
297{
298public:
299 PoolDeallocationTimeRegisterObj(PoolTestThreadResult& result) :
300 TimeRegisterObj(result.DeallocationTimeMin, result.DeallocationTimeSum, result.DeallocationTimeMax)
301 {
302 }
303};
304
Adam Sawicki33d2ce72018-08-27 13:59:13 +0200305static void CurrentTimeToStr(std::string& out)
306{
307 time_t rawTime; time(&rawTime);
308 struct tm timeInfo; localtime_s(&timeInfo, &rawTime);
309 char timeStr[128];
310 strftime(timeStr, _countof(timeStr), "%c", &timeInfo);
311 out = timeStr;
312}
313
Adam Sawickib8333fb2018-03-13 16:15:53 +0100314VkResult MainTest(Result& outResult, const Config& config)
315{
316 assert(config.ThreadCount > 0);
317
318 InitResult(outResult);
319
320 RandomNumberGenerator mainRand{config.RandSeed};
321
322 time_point timeBeg = std::chrono::high_resolution_clock::now();
323
324 std::atomic<size_t> allocationCount = 0;
325 VkResult res = VK_SUCCESS;
326
327 uint32_t memUsageProbabilitySum =
328 config.MemUsageProbability[0] + config.MemUsageProbability[1] +
329 config.MemUsageProbability[2] + config.MemUsageProbability[3];
330 assert(memUsageProbabilitySum > 0);
331
332 uint32_t allocationSizeProbabilitySum = std::accumulate(
333 config.AllocationSizes.begin(),
334 config.AllocationSizes.end(),
335 0u,
336 [](uint32_t sum, const AllocationSize& allocSize) {
337 return sum + allocSize.Probability;
338 });
339
340 struct Allocation
341 {
342 VkBuffer Buffer;
343 VkImage Image;
344 VmaAllocation Alloc;
345 };
346
347 std::vector<Allocation> commonAllocations;
348 std::mutex commonAllocationsMutex;
349
350 auto Allocate = [&](
351 VkDeviceSize bufferSize,
352 const VkExtent2D imageExtent,
353 RandomNumberGenerator& localRand,
354 VkDeviceSize& totalAllocatedBytes,
355 std::vector<Allocation>& allocations) -> VkResult
356 {
357 assert((bufferSize == 0) != (imageExtent.width == 0 && imageExtent.height == 0));
358
359 uint32_t memUsageIndex = 0;
360 uint32_t memUsageRand = localRand.Generate() % memUsageProbabilitySum;
361 while(memUsageRand >= config.MemUsageProbability[memUsageIndex])
362 memUsageRand -= config.MemUsageProbability[memUsageIndex++];
363
364 VmaAllocationCreateInfo memReq = {};
365 memReq.usage = (VmaMemoryUsage)(VMA_MEMORY_USAGE_GPU_ONLY + memUsageIndex);
Adam Sawicki0667e332018-08-24 17:26:44 +0200366 memReq.flags |= config.AllocationStrategy;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100367
368 Allocation allocation = {};
369 VmaAllocationInfo allocationInfo;
370
371 // Buffer
372 if(bufferSize > 0)
373 {
374 assert(imageExtent.width == 0);
375 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
376 bufferInfo.size = bufferSize;
377 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
378
379 {
380 AllocationTimeRegisterObj timeRegisterObj{outResult};
381 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &memReq, &allocation.Buffer, &allocation.Alloc, &allocationInfo);
382 }
383 }
384 // Image
385 else
386 {
387 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
388 imageInfo.imageType = VK_IMAGE_TYPE_2D;
389 imageInfo.extent.width = imageExtent.width;
390 imageInfo.extent.height = imageExtent.height;
391 imageInfo.extent.depth = 1;
392 imageInfo.mipLevels = 1;
393 imageInfo.arrayLayers = 1;
394 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
395 imageInfo.tiling = memReq.usage == VMA_MEMORY_USAGE_GPU_ONLY ?
396 VK_IMAGE_TILING_OPTIMAL :
397 VK_IMAGE_TILING_LINEAR;
398 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
399 switch(memReq.usage)
400 {
401 case VMA_MEMORY_USAGE_GPU_ONLY:
402 switch(localRand.Generate() % 3)
403 {
404 case 0:
405 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
406 break;
407 case 1:
408 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
409 break;
410 case 2:
411 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
412 break;
413 }
414 break;
415 case VMA_MEMORY_USAGE_CPU_ONLY:
416 case VMA_MEMORY_USAGE_CPU_TO_GPU:
417 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
418 break;
419 case VMA_MEMORY_USAGE_GPU_TO_CPU:
420 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
421 break;
422 }
423 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
424 imageInfo.flags = 0;
425
426 {
427 AllocationTimeRegisterObj timeRegisterObj{outResult};
428 res = vmaCreateImage(g_hAllocator, &imageInfo, &memReq, &allocation.Image, &allocation.Alloc, &allocationInfo);
429 }
430 }
431
432 if(res == VK_SUCCESS)
433 {
434 ++allocationCount;
435 totalAllocatedBytes += allocationInfo.size;
436 bool useCommonAllocations = localRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
437 if(useCommonAllocations)
438 {
439 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
440 commonAllocations.push_back(allocation);
441 }
442 else
443 allocations.push_back(allocation);
444 }
445 else
446 {
Adam Sawickib8d34d52018-10-03 17:41:20 +0200447 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100448 }
449 return res;
450 };
451
452 auto GetNextAllocationSize = [&](
453 VkDeviceSize& outBufSize,
454 VkExtent2D& outImageSize,
455 RandomNumberGenerator& localRand)
456 {
457 outBufSize = 0;
458 outImageSize = {0, 0};
459
460 uint32_t allocSizeIndex = 0;
461 uint32_t r = localRand.Generate() % allocationSizeProbabilitySum;
462 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
463 r -= config.AllocationSizes[allocSizeIndex++].Probability;
464
465 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
466 if(allocSize.BufferSizeMax > 0)
467 {
468 assert(allocSize.ImageSizeMax == 0);
469 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
470 outBufSize = allocSize.BufferSizeMin;
471 else
472 {
473 outBufSize = allocSize.BufferSizeMin + localRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
474 outBufSize = outBufSize / 16 * 16;
475 }
476 }
477 else
478 {
479 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
480 outImageSize.width = outImageSize.height = allocSize.ImageSizeMax;
481 else
482 {
483 outImageSize.width = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
484 outImageSize.height = allocSize.ImageSizeMin + localRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
485 }
486 }
487 };
488
489 std::atomic<uint32_t> numThreadsReachedMaxAllocations = 0;
490 HANDLE threadsFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
491
492 auto ThreadProc = [&](uint32_t randSeed) -> void
493 {
494 RandomNumberGenerator threadRand(randSeed);
495 VkDeviceSize threadTotalAllocatedBytes = 0;
496 std::vector<Allocation> threadAllocations;
497 VkDeviceSize threadBeginBytesToAllocate = config.BeginBytesToAllocate / config.ThreadCount;
498 VkDeviceSize threadMaxBytesToAllocate = config.MaxBytesToAllocate / config.ThreadCount;
499 uint32_t threadAdditionalOperationCount = config.AdditionalOperationCount / config.ThreadCount;
500
501 // BEGIN ALLOCATIONS
502 for(;;)
503 {
504 VkDeviceSize bufferSize = 0;
505 VkExtent2D imageExtent = {};
506 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
507 if(threadTotalAllocatedBytes + bufferSize + imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
508 threadBeginBytesToAllocate)
509 {
510 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
511 break;
512 }
513 else
514 break;
515 }
516
517 // ADDITIONAL ALLOCATIONS AND FREES
518 for(size_t i = 0; i < threadAdditionalOperationCount; ++i)
519 {
520 VkDeviceSize bufferSize = 0;
521 VkExtent2D imageExtent = {};
522 GetNextAllocationSize(bufferSize, imageExtent, threadRand);
523
524 // true = allocate, false = free
525 bool allocate = threadRand.Generate() % 2 != 0;
526
527 if(allocate)
528 {
529 if(threadTotalAllocatedBytes +
530 bufferSize +
531 imageExtent.width * imageExtent.height * IMAGE_BYTES_PER_PIXEL <
532 threadMaxBytesToAllocate)
533 {
534 if(Allocate(bufferSize, imageExtent, threadRand, threadTotalAllocatedBytes, threadAllocations) != VK_SUCCESS)
535 break;
536 }
537 }
538 else
539 {
540 bool useCommonAllocations = threadRand.Generate() % 100 < config.ThreadsUsingCommonAllocationsProbabilityPercent;
541 if(useCommonAllocations)
542 {
543 std::unique_lock<std::mutex> lock(commonAllocationsMutex);
544 if(!commonAllocations.empty())
545 {
546 size_t indexToFree = threadRand.Generate() % commonAllocations.size();
547 VmaAllocationInfo allocationInfo;
548 vmaGetAllocationInfo(g_hAllocator, commonAllocations[indexToFree].Alloc, &allocationInfo);
549 if(threadTotalAllocatedBytes >= allocationInfo.size)
550 {
551 DeallocationTimeRegisterObj timeRegisterObj{outResult};
552 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
553 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
554 else
555 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
556 threadTotalAllocatedBytes -= allocationInfo.size;
557 commonAllocations.erase(commonAllocations.begin() + indexToFree);
558 }
559 }
560 }
561 else
562 {
563 if(!threadAllocations.empty())
564 {
565 size_t indexToFree = threadRand.Generate() % threadAllocations.size();
566 VmaAllocationInfo allocationInfo;
567 vmaGetAllocationInfo(g_hAllocator, threadAllocations[indexToFree].Alloc, &allocationInfo);
568 if(threadTotalAllocatedBytes >= allocationInfo.size)
569 {
570 DeallocationTimeRegisterObj timeRegisterObj{outResult};
571 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
572 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
573 else
574 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
575 threadTotalAllocatedBytes -= allocationInfo.size;
576 threadAllocations.erase(threadAllocations.begin() + indexToFree);
577 }
578 }
579 }
580 }
581 }
582
583 ++numThreadsReachedMaxAllocations;
584
585 WaitForSingleObject(threadsFinishEvent, INFINITE);
586
587 // DEALLOCATION
588 while(!threadAllocations.empty())
589 {
590 size_t indexToFree = 0;
591 switch(config.FreeOrder)
592 {
593 case FREE_ORDER::FORWARD:
594 indexToFree = 0;
595 break;
596 case FREE_ORDER::BACKWARD:
597 indexToFree = threadAllocations.size() - 1;
598 break;
599 case FREE_ORDER::RANDOM:
600 indexToFree = mainRand.Generate() % threadAllocations.size();
601 break;
602 }
603
604 {
605 DeallocationTimeRegisterObj timeRegisterObj{outResult};
606 if(threadAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
607 vmaDestroyBuffer(g_hAllocator, threadAllocations[indexToFree].Buffer, threadAllocations[indexToFree].Alloc);
608 else
609 vmaDestroyImage(g_hAllocator, threadAllocations[indexToFree].Image, threadAllocations[indexToFree].Alloc);
610 }
611 threadAllocations.erase(threadAllocations.begin() + indexToFree);
612 }
613 };
614
615 uint32_t threadRandSeed = mainRand.Generate();
616 std::vector<std::thread> bkgThreads;
617 for(size_t i = 0; i < config.ThreadCount; ++i)
618 {
619 bkgThreads.emplace_back(std::bind(ThreadProc, threadRandSeed + (uint32_t)i));
620 }
621
622 // Wait for threads reached max allocations
623 while(numThreadsReachedMaxAllocations < config.ThreadCount)
624 Sleep(0);
625
626 // CALCULATE MEMORY STATISTICS ON FINAL USAGE
627 VmaStats vmaStats = {};
628 vmaCalculateStats(g_hAllocator, &vmaStats);
629 outResult.TotalMemoryAllocated = vmaStats.total.usedBytes + vmaStats.total.unusedBytes;
630 outResult.FreeRangeSizeMax = vmaStats.total.unusedRangeSizeMax;
631 outResult.FreeRangeSizeAvg = vmaStats.total.unusedRangeSizeAvg;
632
633 // Signal threads to deallocate
634 SetEvent(threadsFinishEvent);
635
636 // Wait for threads finished
637 for(size_t i = 0; i < bkgThreads.size(); ++i)
638 bkgThreads[i].join();
639 bkgThreads.clear();
640
641 CloseHandle(threadsFinishEvent);
642
643 // Deallocate remaining common resources
644 while(!commonAllocations.empty())
645 {
646 size_t indexToFree = 0;
647 switch(config.FreeOrder)
648 {
649 case FREE_ORDER::FORWARD:
650 indexToFree = 0;
651 break;
652 case FREE_ORDER::BACKWARD:
653 indexToFree = commonAllocations.size() - 1;
654 break;
655 case FREE_ORDER::RANDOM:
656 indexToFree = mainRand.Generate() % commonAllocations.size();
657 break;
658 }
659
660 {
661 DeallocationTimeRegisterObj timeRegisterObj{outResult};
662 if(commonAllocations[indexToFree].Buffer != VK_NULL_HANDLE)
663 vmaDestroyBuffer(g_hAllocator, commonAllocations[indexToFree].Buffer, commonAllocations[indexToFree].Alloc);
664 else
665 vmaDestroyImage(g_hAllocator, commonAllocations[indexToFree].Image, commonAllocations[indexToFree].Alloc);
666 }
667 commonAllocations.erase(commonAllocations.begin() + indexToFree);
668 }
669
670 if(allocationCount)
671 {
672 outResult.AllocationTimeAvg /= allocationCount;
673 outResult.DeallocationTimeAvg /= allocationCount;
674 }
675
676 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
677
678 return res;
679}
680
Adam Sawicki51fa9662018-10-03 13:44:29 +0200681void SaveAllocatorStatsToFile(const wchar_t* filePath)
Adam Sawickib8333fb2018-03-13 16:15:53 +0100682{
Adam Sawicki4d844e22019-01-24 16:21:05 +0100683 wprintf(L"Saving JSON dump to file \"%s\"\n", filePath);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100684 char* stats;
Adam Sawickie44c6262018-06-15 14:30:39 +0200685 vmaBuildStatsString(g_hAllocator, &stats, VK_TRUE);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100686 SaveFile(filePath, stats, strlen(stats));
Adam Sawickie44c6262018-06-15 14:30:39 +0200687 vmaFreeStatsString(g_hAllocator, stats);
Adam Sawickib8333fb2018-03-13 16:15:53 +0100688}
689
690struct AllocInfo
691{
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200692 VmaAllocation m_Allocation = VK_NULL_HANDLE;
693 VkBuffer m_Buffer = VK_NULL_HANDLE;
694 VkImage m_Image = VK_NULL_HANDLE;
Adam Sawickia52012d2019-12-23 15:28:51 +0100695 VkImageLayout m_ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200696 uint32_t m_StartValue = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +0100697 union
698 {
699 VkBufferCreateInfo m_BufferInfo;
700 VkImageCreateInfo m_ImageInfo;
701 };
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200702
Adam Sawickic467e282019-12-23 16:38:31 +0100703 // After defragmentation.
704 VkBuffer m_NewBuffer = VK_NULL_HANDLE;
705 VkImage m_NewImage = VK_NULL_HANDLE;
706
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200707 void CreateBuffer(
708 const VkBufferCreateInfo& bufCreateInfo,
709 const VmaAllocationCreateInfo& allocCreateInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +0100710 void CreateImage(
711 const VkImageCreateInfo& imageCreateInfo,
712 const VmaAllocationCreateInfo& allocCreateInfo,
713 VkImageLayout layout);
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200714 void Destroy();
Adam Sawickib8333fb2018-03-13 16:15:53 +0100715};
716
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200717void AllocInfo::CreateBuffer(
718 const VkBufferCreateInfo& bufCreateInfo,
719 const VmaAllocationCreateInfo& allocCreateInfo)
720{
721 m_BufferInfo = bufCreateInfo;
722 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &m_Buffer, &m_Allocation, nullptr);
723 TEST(res == VK_SUCCESS);
724}
Adam Sawickia52012d2019-12-23 15:28:51 +0100725void AllocInfo::CreateImage(
726 const VkImageCreateInfo& imageCreateInfo,
727 const VmaAllocationCreateInfo& allocCreateInfo,
728 VkImageLayout layout)
729{
730 m_ImageInfo = imageCreateInfo;
731 m_ImageLayout = layout;
732 VkResult res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &m_Image, &m_Allocation, nullptr);
733 TEST(res == VK_SUCCESS);
734}
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200735
736void AllocInfo::Destroy()
737{
738 if(m_Image)
739 {
Adam Sawickic467e282019-12-23 16:38:31 +0100740 assert(!m_Buffer);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200741 vkDestroyImage(g_hDevice, m_Image, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100742 m_Image = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200743 }
744 if(m_Buffer)
745 {
Adam Sawickic467e282019-12-23 16:38:31 +0100746 assert(!m_Image);
Adam Sawicki1f84f622019-07-02 13:40:01 +0200747 vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100748 m_Buffer = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200749 }
750 if(m_Allocation)
751 {
752 vmaFreeMemory(g_hAllocator, m_Allocation);
Adam Sawickiddcbf8c2019-11-22 15:22:42 +0100753 m_Allocation = VK_NULL_HANDLE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +0200754 }
755}
756
Adam Sawickif2975342018-10-16 13:49:02 +0200757class StagingBufferCollection
758{
759public:
760 StagingBufferCollection() { }
761 ~StagingBufferCollection();
762 // Returns false if maximum total size of buffers would be exceeded.
763 bool AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr);
764 void ReleaseAllBuffers();
765
766private:
767 static const VkDeviceSize MAX_TOTAL_SIZE = 256ull * 1024 * 1024;
768 struct BufInfo
769 {
770 VmaAllocation Allocation = VK_NULL_HANDLE;
771 VkBuffer Buffer = VK_NULL_HANDLE;
772 VkDeviceSize Size = VK_WHOLE_SIZE;
773 void* MappedPtr = nullptr;
774 bool Used = false;
775 };
776 std::vector<BufInfo> m_Bufs;
777 // Including both used and unused.
778 VkDeviceSize m_TotalSize = 0;
779};
780
781StagingBufferCollection::~StagingBufferCollection()
782{
783 for(size_t i = m_Bufs.size(); i--; )
784 {
785 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
786 }
787}
788
789bool StagingBufferCollection::AcquireBuffer(VkDeviceSize size, VkBuffer& outBuffer, void*& outMappedPtr)
790{
791 assert(size <= MAX_TOTAL_SIZE);
792
793 // Try to find existing unused buffer with best size.
794 size_t bestIndex = SIZE_MAX;
795 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
796 {
797 BufInfo& currBufInfo = m_Bufs[i];
798 if(!currBufInfo.Used && currBufInfo.Size >= size &&
799 (bestIndex == SIZE_MAX || currBufInfo.Size < m_Bufs[bestIndex].Size))
800 {
801 bestIndex = i;
802 }
803 }
804
805 if(bestIndex != SIZE_MAX)
806 {
807 m_Bufs[bestIndex].Used = true;
808 outBuffer = m_Bufs[bestIndex].Buffer;
809 outMappedPtr = m_Bufs[bestIndex].MappedPtr;
810 return true;
811 }
812
813 // Allocate new buffer with requested size.
814 if(m_TotalSize + size <= MAX_TOTAL_SIZE)
815 {
816 BufInfo bufInfo;
817 bufInfo.Size = size;
818 bufInfo.Used = true;
819
820 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
821 bufCreateInfo.size = size;
822 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
823
824 VmaAllocationCreateInfo allocCreateInfo = {};
825 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
826 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
827
828 VmaAllocationInfo allocInfo;
829 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
830 bufInfo.MappedPtr = allocInfo.pMappedData;
831 TEST(res == VK_SUCCESS && bufInfo.MappedPtr);
832
833 outBuffer = bufInfo.Buffer;
834 outMappedPtr = bufInfo.MappedPtr;
835
836 m_Bufs.push_back(std::move(bufInfo));
837
838 m_TotalSize += size;
839
840 return true;
841 }
842
843 // There are some unused but smaller buffers: Free them and try again.
844 bool hasUnused = false;
845 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
846 {
847 if(!m_Bufs[i].Used)
848 {
849 hasUnused = true;
850 break;
851 }
852 }
853 if(hasUnused)
854 {
855 for(size_t i = m_Bufs.size(); i--; )
856 {
857 if(!m_Bufs[i].Used)
858 {
859 m_TotalSize -= m_Bufs[i].Size;
860 vmaDestroyBuffer(g_hAllocator, m_Bufs[i].Buffer, m_Bufs[i].Allocation);
861 m_Bufs.erase(m_Bufs.begin() + i);
862 }
863 }
864
865 return AcquireBuffer(size, outBuffer, outMappedPtr);
866 }
867
868 return false;
869}
870
871void StagingBufferCollection::ReleaseAllBuffers()
872{
873 for(size_t i = 0, count = m_Bufs.size(); i < count; ++i)
874 {
875 m_Bufs[i].Used = false;
876 }
877}
878
879static void UploadGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
880{
881 StagingBufferCollection stagingBufs;
882
883 bool cmdBufferStarted = false;
884 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
885 {
886 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
887 if(currAllocInfo.m_Buffer)
888 {
889 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
890
891 VkBuffer stagingBuf = VK_NULL_HANDLE;
892 void* stagingBufMappedPtr = nullptr;
893 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
894 {
895 TEST(cmdBufferStarted);
896 EndSingleTimeCommands();
897 stagingBufs.ReleaseAllBuffers();
898 cmdBufferStarted = false;
899
900 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
901 TEST(ok);
902 }
903
904 // Fill staging buffer.
905 {
906 assert(size % sizeof(uint32_t) == 0);
907 uint32_t* stagingValPtr = (uint32_t*)stagingBufMappedPtr;
908 uint32_t val = currAllocInfo.m_StartValue;
909 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
910 {
911 *stagingValPtr = val;
912 ++stagingValPtr;
913 ++val;
914 }
915 }
916
917 // Issue copy command from staging buffer to destination buffer.
918 if(!cmdBufferStarted)
919 {
920 cmdBufferStarted = true;
921 BeginSingleTimeCommands();
922 }
923
924 VkBufferCopy copy = {};
925 copy.srcOffset = 0;
926 copy.dstOffset = 0;
927 copy.size = size;
928 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Buffer, 1, &copy);
929 }
930 else
931 {
Adam Sawickia52012d2019-12-23 15:28:51 +0100932 TEST(currAllocInfo.m_ImageInfo.format == VK_FORMAT_R8G8B8A8_UNORM && "Only RGBA8 images are currently supported.");
933 TEST(currAllocInfo.m_ImageInfo.mipLevels == 1 && "Only single mip images are currently supported.");
934
Adam Sawickic467e282019-12-23 16:38:31 +0100935 const VkDeviceSize size = (VkDeviceSize)currAllocInfo.m_ImageInfo.extent.width * currAllocInfo.m_ImageInfo.extent.height * sizeof(uint32_t);
Adam Sawickia52012d2019-12-23 15:28:51 +0100936
937 VkBuffer stagingBuf = VK_NULL_HANDLE;
938 void* stagingBufMappedPtr = nullptr;
939 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
940 {
941 TEST(cmdBufferStarted);
942 EndSingleTimeCommands();
943 stagingBufs.ReleaseAllBuffers();
944 cmdBufferStarted = false;
945
946 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
947 TEST(ok);
948 }
949
950 // Fill staging buffer.
951 {
952 assert(size % sizeof(uint32_t) == 0);
953 uint32_t *stagingValPtr = (uint32_t *)stagingBufMappedPtr;
954 uint32_t val = currAllocInfo.m_StartValue;
955 for(size_t i = 0; i < size / sizeof(uint32_t); ++i)
956 {
957 *stagingValPtr = val;
958 ++stagingValPtr;
959 ++val;
960 }
961 }
962
963 // Issue copy command from staging buffer to destination buffer.
964 if(!cmdBufferStarted)
965 {
966 cmdBufferStarted = true;
967 BeginSingleTimeCommands();
968 }
969
970
971 // Transfer to transfer dst layout
972 VkImageSubresourceRange subresourceRange = {
973 VK_IMAGE_ASPECT_COLOR_BIT,
974 0, VK_REMAINING_MIP_LEVELS,
975 0, VK_REMAINING_ARRAY_LAYERS
976 };
977
978 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
979 barrier.srcAccessMask = 0;
980 barrier.dstAccessMask = 0;
981 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
982 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
983 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
984 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
985 barrier.image = currAllocInfo.m_Image;
986 barrier.subresourceRange = subresourceRange;
987
988 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
989 0, nullptr,
990 0, nullptr,
991 1, &barrier);
992
993 // Copy image date
994 VkBufferImageCopy copy = {};
995 copy.bufferOffset = 0;
996 copy.bufferRowLength = 0;
997 copy.bufferImageHeight = 0;
998 copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
999 copy.imageSubresource.layerCount = 1;
1000 copy.imageExtent = currAllocInfo.m_ImageInfo.extent;
1001
1002 vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, stagingBuf, currAllocInfo.m_Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
1003
1004 // Transfer to desired layout
1005 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1006 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1007 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1008 barrier.newLayout = currAllocInfo.m_ImageLayout;
1009
1010 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
1011 0, nullptr,
1012 0, nullptr,
1013 1, &barrier);
Adam Sawickif2975342018-10-16 13:49:02 +02001014 }
1015 }
1016
1017 if(cmdBufferStarted)
1018 {
1019 EndSingleTimeCommands();
1020 stagingBufs.ReleaseAllBuffers();
1021 }
1022}
1023
1024static void ValidateGpuData(const AllocInfo* allocInfo, size_t allocInfoCount)
1025{
1026 StagingBufferCollection stagingBufs;
1027
1028 bool cmdBufferStarted = false;
1029 size_t validateAllocIndexOffset = 0;
1030 std::vector<void*> validateStagingBuffers;
1031 for(size_t allocInfoIndex = 0; allocInfoIndex < allocInfoCount; ++allocInfoIndex)
1032 {
1033 const AllocInfo& currAllocInfo = allocInfo[allocInfoIndex];
1034 if(currAllocInfo.m_Buffer)
1035 {
1036 const VkDeviceSize size = currAllocInfo.m_BufferInfo.size;
1037
1038 VkBuffer stagingBuf = VK_NULL_HANDLE;
1039 void* stagingBufMappedPtr = nullptr;
1040 if(!stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr))
1041 {
1042 TEST(cmdBufferStarted);
1043 EndSingleTimeCommands();
1044 cmdBufferStarted = false;
1045
1046 for(size_t validateIndex = 0;
1047 validateIndex < validateStagingBuffers.size();
1048 ++validateIndex)
1049 {
1050 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1051 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1052 TEST(validateSize % sizeof(uint32_t) == 0);
1053 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1054 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1055 bool valid = true;
1056 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1057 {
1058 if(*stagingValPtr != val)
1059 {
1060 valid = false;
1061 break;
1062 }
1063 ++stagingValPtr;
1064 ++val;
1065 }
1066 TEST(valid);
1067 }
1068
1069 stagingBufs.ReleaseAllBuffers();
1070
1071 validateAllocIndexOffset = allocInfoIndex;
1072 validateStagingBuffers.clear();
1073
1074 bool ok = stagingBufs.AcquireBuffer(size, stagingBuf, stagingBufMappedPtr);
1075 TEST(ok);
1076 }
1077
1078 // Issue copy command from staging buffer to destination buffer.
1079 if(!cmdBufferStarted)
1080 {
1081 cmdBufferStarted = true;
1082 BeginSingleTimeCommands();
1083 }
1084
1085 VkBufferCopy copy = {};
1086 copy.srcOffset = 0;
1087 copy.dstOffset = 0;
1088 copy.size = size;
1089 vkCmdCopyBuffer(g_hTemporaryCommandBuffer, currAllocInfo.m_Buffer, stagingBuf, 1, &copy);
1090
1091 // Sava mapped pointer for later validation.
1092 validateStagingBuffers.push_back(stagingBufMappedPtr);
1093 }
1094 else
1095 {
1096 TEST(0 && "Images not currently supported.");
1097 }
1098 }
1099
1100 if(cmdBufferStarted)
1101 {
1102 EndSingleTimeCommands();
1103
1104 for(size_t validateIndex = 0;
1105 validateIndex < validateStagingBuffers.size();
1106 ++validateIndex)
1107 {
1108 const size_t validateAllocIndex = validateIndex + validateAllocIndexOffset;
1109 const VkDeviceSize validateSize = allocInfo[validateAllocIndex].m_BufferInfo.size;
1110 TEST(validateSize % sizeof(uint32_t) == 0);
1111 const uint32_t* stagingValPtr = (const uint32_t*)validateStagingBuffers[validateIndex];
1112 uint32_t val = allocInfo[validateAllocIndex].m_StartValue;
1113 bool valid = true;
1114 for(size_t i = 0; i < validateSize / sizeof(uint32_t); ++i)
1115 {
1116 if(*stagingValPtr != val)
1117 {
1118 valid = false;
1119 break;
1120 }
1121 ++stagingValPtr;
1122 ++val;
1123 }
1124 TEST(valid);
1125 }
1126
1127 stagingBufs.ReleaseAllBuffers();
1128 }
1129}
1130
Adam Sawickib8333fb2018-03-13 16:15:53 +01001131static void GetMemReq(VmaAllocationCreateInfo& outMemReq)
1132{
1133 outMemReq = {};
1134 outMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1135 //outMemReq.flags = VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
1136}
1137
1138static void CreateBuffer(
1139 VmaPool pool,
1140 const VkBufferCreateInfo& bufCreateInfo,
1141 bool persistentlyMapped,
1142 AllocInfo& outAllocInfo)
1143{
1144 outAllocInfo = {};
1145 outAllocInfo.m_BufferInfo = bufCreateInfo;
1146
1147 VmaAllocationCreateInfo allocCreateInfo = {};
1148 allocCreateInfo.pool = pool;
1149 if(persistentlyMapped)
1150 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
1151
1152 VmaAllocationInfo vmaAllocInfo = {};
1153 ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &outAllocInfo.m_Buffer, &outAllocInfo.m_Allocation, &vmaAllocInfo) );
1154
1155 // Setup StartValue and fill.
1156 {
1157 outAllocInfo.m_StartValue = (uint32_t)rand();
1158 uint32_t* data = (uint32_t*)vmaAllocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001159 TEST((data != nullptr) == persistentlyMapped);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001160 if(!persistentlyMapped)
1161 {
1162 ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, outAllocInfo.m_Allocation, (void**)&data) );
1163 }
1164
1165 uint32_t value = outAllocInfo.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001166 TEST(bufCreateInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001167 for(size_t i = 0; i < bufCreateInfo.size / sizeof(uint32_t); ++i)
1168 data[i] = value++;
1169
1170 if(!persistentlyMapped)
1171 vmaUnmapMemory(g_hAllocator, outAllocInfo.m_Allocation);
1172 }
1173}
1174
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001175static void CreateAllocation(AllocInfo& outAllocation)
Adam Sawickib8333fb2018-03-13 16:15:53 +01001176{
1177 outAllocation.m_Allocation = nullptr;
1178 outAllocation.m_Buffer = nullptr;
1179 outAllocation.m_Image = nullptr;
1180 outAllocation.m_StartValue = (uint32_t)rand();
1181
1182 VmaAllocationCreateInfo vmaMemReq;
1183 GetMemReq(vmaMemReq);
1184
1185 VmaAllocationInfo allocInfo;
1186
1187 const bool isBuffer = true;//(rand() & 0x1) != 0;
1188 const bool isLarge = (rand() % 16) == 0;
1189 if(isBuffer)
1190 {
1191 const uint32_t bufferSize = isLarge ?
1192 (rand() % 10 + 1) * (1024 * 1024) : // 1 MB ... 10 MB
1193 (rand() % 1024 + 1) * 1024; // 1 KB ... 1 MB
1194
1195 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1196 bufferInfo.size = bufferSize;
1197 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1198
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001199 VkResult res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &vmaMemReq, &outAllocation.m_Buffer, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001200 outAllocation.m_BufferInfo = bufferInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001201 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001202 }
1203 else
1204 {
1205 const uint32_t imageSizeX = isLarge ?
1206 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1207 rand() % 1024 + 1; // 1 ... 1024
1208 const uint32_t imageSizeY = isLarge ?
1209 1024 + rand() % (4096 - 1024) : // 1024 ... 4096
1210 rand() % 1024 + 1; // 1 ... 1024
1211
1212 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1213 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1214 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1215 imageInfo.extent.width = imageSizeX;
1216 imageInfo.extent.height = imageSizeY;
1217 imageInfo.extent.depth = 1;
1218 imageInfo.mipLevels = 1;
1219 imageInfo.arrayLayers = 1;
1220 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1221 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1222 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
1223 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1224
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001225 VkResult res = vmaCreateImage(g_hAllocator, &imageInfo, &vmaMemReq, &outAllocation.m_Image, &outAllocation.m_Allocation, &allocInfo);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001226 outAllocation.m_ImageInfo = imageInfo;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001227 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001228 }
1229
1230 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1231 if(allocInfo.pMappedData == nullptr)
1232 {
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001233 VkResult res = vmaMapMemory(g_hAllocator, outAllocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001234 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001235 }
1236
1237 uint32_t value = outAllocation.m_StartValue;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001238 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001239 for(size_t i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1240 data[i] = value++;
1241
1242 if(allocInfo.pMappedData == nullptr)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001243 vmaUnmapMemory(g_hAllocator, outAllocation.m_Allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001244}
1245
1246static void DestroyAllocation(const AllocInfo& allocation)
1247{
1248 if(allocation.m_Buffer)
1249 vmaDestroyBuffer(g_hAllocator, allocation.m_Buffer, allocation.m_Allocation);
1250 else
1251 vmaDestroyImage(g_hAllocator, allocation.m_Image, allocation.m_Allocation);
1252}
1253
1254static void DestroyAllAllocations(std::vector<AllocInfo>& allocations)
1255{
1256 for(size_t i = allocations.size(); i--; )
1257 DestroyAllocation(allocations[i]);
1258 allocations.clear();
1259}
1260
1261static void ValidateAllocationData(const AllocInfo& allocation)
1262{
1263 VmaAllocationInfo allocInfo;
1264 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1265
1266 uint32_t* data = (uint32_t*)allocInfo.pMappedData;
1267 if(allocInfo.pMappedData == nullptr)
1268 {
1269 VkResult res = vmaMapMemory(g_hAllocator, allocation.m_Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001270 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001271 }
1272
1273 uint32_t value = allocation.m_StartValue;
1274 bool ok = true;
1275 size_t i;
Adam Sawickib8d34d52018-10-03 17:41:20 +02001276 TEST(allocInfo.size % 4 == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001277 for(i = 0; i < allocInfo.size / sizeof(uint32_t); ++i)
1278 {
1279 if(data[i] != value++)
1280 {
1281 ok = false;
1282 break;
1283 }
1284 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02001285 TEST(ok);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001286
1287 if(allocInfo.pMappedData == nullptr)
1288 vmaUnmapMemory(g_hAllocator, allocation.m_Allocation);
1289}
1290
1291static void RecreateAllocationResource(AllocInfo& allocation)
1292{
1293 VmaAllocationInfo allocInfo;
1294 vmaGetAllocationInfo(g_hAllocator, allocation.m_Allocation, &allocInfo);
1295
1296 if(allocation.m_Buffer)
1297 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001298 vkDestroyBuffer(g_hDevice, allocation.m_Buffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001299
Adam Sawicki1f84f622019-07-02 13:40:01 +02001300 VkResult res = vkCreateBuffer(g_hDevice, &allocation.m_BufferInfo, g_Allocs, &allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001301 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001302
1303 // Just to silence validation layer warnings.
1304 VkMemoryRequirements vkMemReq;
1305 vkGetBufferMemoryRequirements(g_hDevice, allocation.m_Buffer, &vkMemReq);
Adam Sawicki2af57d72018-12-06 15:35:05 +01001306 TEST(vkMemReq.size >= allocation.m_BufferInfo.size);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001307
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001308 res = vmaBindBufferMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Buffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001309 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001310 }
1311 else
1312 {
Adam Sawicki1f84f622019-07-02 13:40:01 +02001313 vkDestroyImage(g_hDevice, allocation.m_Image, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001314
Adam Sawicki1f84f622019-07-02 13:40:01 +02001315 VkResult res = vkCreateImage(g_hDevice, &allocation.m_ImageInfo, g_Allocs, &allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001316 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001317
1318 // Just to silence validation layer warnings.
1319 VkMemoryRequirements vkMemReq;
1320 vkGetImageMemoryRequirements(g_hDevice, allocation.m_Image, &vkMemReq);
1321
Adam Sawickiaf88c1b2019-07-02 12:34:26 +02001322 res = vmaBindImageMemory(g_hAllocator, allocation.m_Allocation, allocation.m_Image);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001323 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001324 }
1325}
1326
1327static void Defragment(AllocInfo* allocs, size_t allocCount,
1328 const VmaDefragmentationInfo* defragmentationInfo = nullptr,
1329 VmaDefragmentationStats* defragmentationStats = nullptr)
1330{
1331 std::vector<VmaAllocation> vmaAllocs(allocCount);
1332 for(size_t i = 0; i < allocCount; ++i)
1333 vmaAllocs[i] = allocs[i].m_Allocation;
1334
1335 std::vector<VkBool32> allocChanged(allocCount);
1336
1337 ERR_GUARD_VULKAN( vmaDefragment(g_hAllocator, vmaAllocs.data(), allocCount, allocChanged.data(),
1338 defragmentationInfo, defragmentationStats) );
1339
1340 for(size_t i = 0; i < allocCount; ++i)
1341 {
1342 if(allocChanged[i])
1343 {
1344 RecreateAllocationResource(allocs[i]);
1345 }
1346 }
1347}
1348
1349static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
1350{
1351 std::for_each(allocs, allocs + allocCount, [](const AllocInfo& allocInfo) {
1352 ValidateAllocationData(allocInfo);
1353 });
1354}
1355
1356void TestDefragmentationSimple()
1357{
1358 wprintf(L"Test defragmentation simple\n");
1359
1360 RandomNumberGenerator rand(667);
1361
1362 const VkDeviceSize BUF_SIZE = 0x10000;
1363 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1364
1365 const VkDeviceSize MIN_BUF_SIZE = 32;
1366 const VkDeviceSize MAX_BUF_SIZE = BUF_SIZE * 4;
1367 auto RandomBufSize = [&]() -> VkDeviceSize {
1368 return align_up<VkDeviceSize>(rand.Generate() % (MAX_BUF_SIZE - MIN_BUF_SIZE + 1) + MIN_BUF_SIZE, 32);
1369 };
1370
1371 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1372 bufCreateInfo.size = BUF_SIZE;
1373 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1374
1375 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1376 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1377
1378 uint32_t memTypeIndex = UINT32_MAX;
1379 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1380
1381 VmaPoolCreateInfo poolCreateInfo = {};
1382 poolCreateInfo.blockSize = BLOCK_SIZE;
1383 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1384
1385 VmaPool pool;
1386 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1387
Adam Sawickie1681912018-11-23 17:50:12 +01001388 // Defragmentation of empty pool.
1389 {
1390 VmaDefragmentationInfo2 defragInfo = {};
1391 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1392 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1393 defragInfo.poolCount = 1;
1394 defragInfo.pPools = &pool;
1395
1396 VmaDefragmentationStats defragStats = {};
1397 VmaDefragmentationContext defragCtx = nullptr;
1398 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats, &defragCtx);
1399 TEST(res >= VK_SUCCESS);
1400 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1401 TEST(defragStats.allocationsMoved == 0 && defragStats.bytesFreed == 0 &&
1402 defragStats.bytesMoved == 0 && defragStats.deviceMemoryBlocksFreed == 0);
1403 }
1404
Adam Sawickib8333fb2018-03-13 16:15:53 +01001405 std::vector<AllocInfo> allocations;
1406
1407 // persistentlyMappedOption = 0 - not persistently mapped.
1408 // persistentlyMappedOption = 1 - persistently mapped.
1409 for(uint32_t persistentlyMappedOption = 0; persistentlyMappedOption < 2; ++persistentlyMappedOption)
1410 {
1411 wprintf(L" Persistently mapped option = %u\n", persistentlyMappedOption);
1412 const bool persistentlyMapped = persistentlyMappedOption != 0;
1413
1414 // # Test 1
1415 // Buffers of fixed size.
1416 // Fill 2 blocks. Remove odd buffers. Defragment everything.
1417 // Expected result: at least 1 block freed.
1418 {
1419 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1420 {
1421 AllocInfo allocInfo;
1422 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1423 allocations.push_back(allocInfo);
1424 }
1425
1426 for(size_t i = 1; i < allocations.size(); ++i)
1427 {
1428 DestroyAllocation(allocations[i]);
1429 allocations.erase(allocations.begin() + i);
1430 }
1431
1432 VmaDefragmentationStats defragStats;
1433 Defragment(allocations.data(), allocations.size(), nullptr, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001434 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
1435 TEST(defragStats.deviceMemoryBlocksFreed >= 1);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001436
1437 ValidateAllocationsData(allocations.data(), allocations.size());
1438
1439 DestroyAllAllocations(allocations);
1440 }
1441
1442 // # Test 2
1443 // Buffers of fixed size.
1444 // Fill 2 blocks. Remove odd buffers. Defragment one buffer at time.
1445 // Expected result: Each of 4 interations makes some progress.
1446 {
1447 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1448 {
1449 AllocInfo allocInfo;
1450 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1451 allocations.push_back(allocInfo);
1452 }
1453
1454 for(size_t i = 1; i < allocations.size(); ++i)
1455 {
1456 DestroyAllocation(allocations[i]);
1457 allocations.erase(allocations.begin() + i);
1458 }
1459
1460 VmaDefragmentationInfo defragInfo = {};
1461 defragInfo.maxAllocationsToMove = 1;
1462 defragInfo.maxBytesToMove = BUF_SIZE;
1463
1464 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE / 2; ++i)
1465 {
1466 VmaDefragmentationStats defragStats;
1467 Defragment(allocations.data(), allocations.size(), &defragInfo, &defragStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001468 TEST(defragStats.allocationsMoved > 0 && defragStats.bytesMoved > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001469 }
1470
1471 ValidateAllocationsData(allocations.data(), allocations.size());
1472
1473 DestroyAllAllocations(allocations);
1474 }
1475
1476 // # Test 3
1477 // Buffers of variable size.
1478 // Create a number of buffers. Remove some percent of them.
1479 // Defragment while having some percent of them unmovable.
1480 // Expected result: Just simple validation.
1481 {
1482 for(size_t i = 0; i < 100; ++i)
1483 {
1484 VkBufferCreateInfo localBufCreateInfo = bufCreateInfo;
1485 localBufCreateInfo.size = RandomBufSize();
1486
1487 AllocInfo allocInfo;
1488 CreateBuffer(pool, bufCreateInfo, persistentlyMapped, allocInfo);
1489 allocations.push_back(allocInfo);
1490 }
1491
1492 const uint32_t percentToDelete = 60;
1493 const size_t numberToDelete = allocations.size() * percentToDelete / 100;
1494 for(size_t i = 0; i < numberToDelete; ++i)
1495 {
1496 size_t indexToDelete = rand.Generate() % (uint32_t)allocations.size();
1497 DestroyAllocation(allocations[indexToDelete]);
1498 allocations.erase(allocations.begin() + indexToDelete);
1499 }
1500
1501 // Non-movable allocations will be at the beginning of allocations array.
1502 const uint32_t percentNonMovable = 20;
1503 const size_t numberNonMovable = allocations.size() * percentNonMovable / 100;
1504 for(size_t i = 0; i < numberNonMovable; ++i)
1505 {
1506 size_t indexNonMovable = i + rand.Generate() % (uint32_t)(allocations.size() - i);
1507 if(indexNonMovable != i)
1508 std::swap(allocations[i], allocations[indexNonMovable]);
1509 }
1510
1511 VmaDefragmentationStats defragStats;
1512 Defragment(
1513 allocations.data() + numberNonMovable,
1514 allocations.size() - numberNonMovable,
1515 nullptr, &defragStats);
1516
1517 ValidateAllocationsData(allocations.data(), allocations.size());
1518
1519 DestroyAllAllocations(allocations);
1520 }
1521 }
1522
Adam Sawicki647cf242018-11-23 17:58:00 +01001523 /*
1524 Allocation that must be move to an overlapping place using memmove().
1525 Create 2 buffers, second slightly bigger than the first. Delete first. Then defragment.
1526 */
Adam Sawickibdb89a92018-12-13 11:56:30 +01001527 if(VMA_DEBUG_MARGIN == 0) // FAST algorithm works only when DEBUG_MARGIN disabled.
Adam Sawicki647cf242018-11-23 17:58:00 +01001528 {
1529 AllocInfo allocInfo[2];
1530
1531 bufCreateInfo.size = BUF_SIZE;
1532 CreateBuffer(pool, bufCreateInfo, false, allocInfo[0]);
1533 const VkDeviceSize biggerBufSize = BUF_SIZE + BUF_SIZE / 256;
1534 bufCreateInfo.size = biggerBufSize;
1535 CreateBuffer(pool, bufCreateInfo, false, allocInfo[1]);
1536
1537 DestroyAllocation(allocInfo[0]);
1538
1539 VmaDefragmentationStats defragStats;
1540 Defragment(&allocInfo[1], 1, nullptr, &defragStats);
1541 // If this fails, it means we couldn't do memmove with overlapping regions.
1542 TEST(defragStats.allocationsMoved == 1 && defragStats.bytesMoved > 0);
1543
1544 ValidateAllocationsData(&allocInfo[1], 1);
1545 DestroyAllocation(allocInfo[1]);
1546 }
1547
Adam Sawickib8333fb2018-03-13 16:15:53 +01001548 vmaDestroyPool(g_hAllocator, pool);
1549}
1550
Adam Sawicki52076eb2018-11-22 16:14:50 +01001551void TestDefragmentationWholePool()
1552{
1553 wprintf(L"Test defragmentation whole pool\n");
1554
1555 RandomNumberGenerator rand(668);
1556
1557 const VkDeviceSize BUF_SIZE = 0x10000;
1558 const VkDeviceSize BLOCK_SIZE = BUF_SIZE * 8;
1559
1560 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1561 bufCreateInfo.size = BUF_SIZE;
1562 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1563
1564 VmaAllocationCreateInfo exampleAllocCreateInfo = {};
1565 exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
1566
1567 uint32_t memTypeIndex = UINT32_MAX;
1568 vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
1569
1570 VmaPoolCreateInfo poolCreateInfo = {};
1571 poolCreateInfo.blockSize = BLOCK_SIZE;
1572 poolCreateInfo.memoryTypeIndex = memTypeIndex;
1573
1574 VmaDefragmentationStats defragStats[2];
1575 for(size_t caseIndex = 0; caseIndex < 2; ++caseIndex)
1576 {
1577 VmaPool pool;
1578 ERR_GUARD_VULKAN( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) );
1579
1580 std::vector<AllocInfo> allocations;
1581
1582 // Buffers of fixed size.
1583 // Fill 2 blocks. Remove odd buffers. Defragment all of them.
1584 for(size_t i = 0; i < BLOCK_SIZE / BUF_SIZE * 2; ++i)
1585 {
1586 AllocInfo allocInfo;
1587 CreateBuffer(pool, bufCreateInfo, false, allocInfo);
1588 allocations.push_back(allocInfo);
1589 }
1590
1591 for(size_t i = 1; i < allocations.size(); ++i)
1592 {
1593 DestroyAllocation(allocations[i]);
1594 allocations.erase(allocations.begin() + i);
1595 }
1596
1597 VmaDefragmentationInfo2 defragInfo = {};
1598 defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
1599 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
1600 std::vector<VmaAllocation> allocationsToDefrag;
1601 if(caseIndex == 0)
1602 {
1603 defragInfo.poolCount = 1;
1604 defragInfo.pPools = &pool;
1605 }
1606 else
1607 {
1608 const size_t allocCount = allocations.size();
1609 allocationsToDefrag.resize(allocCount);
1610 std::transform(
1611 allocations.begin(), allocations.end(),
1612 allocationsToDefrag.begin(),
1613 [](const AllocInfo& allocInfo) { return allocInfo.m_Allocation; });
1614 defragInfo.allocationCount = (uint32_t)allocCount;
1615 defragInfo.pAllocations = allocationsToDefrag.data();
1616 }
1617
1618 VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
1619 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &defragStats[caseIndex], &defragCtx);
1620 TEST(res >= VK_SUCCESS);
1621 vmaDefragmentationEnd(g_hAllocator, defragCtx);
1622
1623 TEST(defragStats[caseIndex].allocationsMoved > 0 && defragStats[caseIndex].bytesMoved > 0);
1624
1625 ValidateAllocationsData(allocations.data(), allocations.size());
1626
1627 DestroyAllAllocations(allocations);
1628
1629 vmaDestroyPool(g_hAllocator, pool);
1630 }
1631
1632 TEST(defragStats[0].bytesMoved == defragStats[1].bytesMoved);
1633 TEST(defragStats[0].allocationsMoved == defragStats[1].allocationsMoved);
1634 TEST(defragStats[0].bytesFreed == defragStats[1].bytesFreed);
1635 TEST(defragStats[0].deviceMemoryBlocksFreed == defragStats[1].deviceMemoryBlocksFreed);
1636}
1637
Adam Sawickib8333fb2018-03-13 16:15:53 +01001638void TestDefragmentationFull()
1639{
1640 std::vector<AllocInfo> allocations;
1641
1642 // Create initial allocations.
1643 for(size_t i = 0; i < 400; ++i)
1644 {
1645 AllocInfo allocation;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001646 CreateAllocation(allocation);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001647 allocations.push_back(allocation);
1648 }
1649
1650 // Delete random allocations
1651 const size_t allocationsToDeletePercent = 80;
1652 size_t allocationsToDelete = allocations.size() * allocationsToDeletePercent / 100;
1653 for(size_t i = 0; i < allocationsToDelete; ++i)
1654 {
1655 size_t index = (size_t)rand() % allocations.size();
1656 DestroyAllocation(allocations[index]);
1657 allocations.erase(allocations.begin() + index);
1658 }
1659
1660 for(size_t i = 0; i < allocations.size(); ++i)
1661 ValidateAllocationData(allocations[i]);
1662
Adam Sawicki0667e332018-08-24 17:26:44 +02001663 //SaveAllocatorStatsToFile(L"Before.csv");
Adam Sawickib8333fb2018-03-13 16:15:53 +01001664
1665 {
1666 std::vector<VmaAllocation> vmaAllocations(allocations.size());
1667 for(size_t i = 0; i < allocations.size(); ++i)
1668 vmaAllocations[i] = allocations[i].m_Allocation;
1669
1670 const size_t nonMovablePercent = 0;
1671 size_t nonMovableCount = vmaAllocations.size() * nonMovablePercent / 100;
1672 for(size_t i = 0; i < nonMovableCount; ++i)
1673 {
1674 size_t index = (size_t)rand() % vmaAllocations.size();
1675 vmaAllocations.erase(vmaAllocations.begin() + index);
1676 }
1677
1678 const uint32_t defragCount = 1;
1679 for(uint32_t defragIndex = 0; defragIndex < defragCount; ++defragIndex)
1680 {
1681 std::vector<VkBool32> allocationsChanged(vmaAllocations.size());
1682
1683 VmaDefragmentationInfo defragmentationInfo;
1684 defragmentationInfo.maxAllocationsToMove = UINT_MAX;
1685 defragmentationInfo.maxBytesToMove = SIZE_MAX;
1686
1687 wprintf(L"Defragmentation #%u\n", defragIndex);
1688
1689 time_point begTime = std::chrono::high_resolution_clock::now();
1690
1691 VmaDefragmentationStats stats;
1692 VkResult res = vmaDefragment(g_hAllocator, vmaAllocations.data(), vmaAllocations.size(), allocationsChanged.data(), &defragmentationInfo, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02001693 TEST(res >= 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001694
1695 float defragmentDuration = ToFloatSeconds(std::chrono::high_resolution_clock::now() - begTime);
1696
1697 wprintf(L"Moved allocations %u, bytes %llu\n", stats.allocationsMoved, stats.bytesMoved);
1698 wprintf(L"Freed blocks %u, bytes %llu\n", stats.deviceMemoryBlocksFreed, stats.bytesFreed);
1699 wprintf(L"Time: %.2f s\n", defragmentDuration);
1700
1701 for(size_t i = 0; i < vmaAllocations.size(); ++i)
1702 {
1703 if(allocationsChanged[i])
1704 {
1705 RecreateAllocationResource(allocations[i]);
1706 }
1707 }
1708
1709 for(size_t i = 0; i < allocations.size(); ++i)
1710 ValidateAllocationData(allocations[i]);
1711
Adam Sawicki0667e332018-08-24 17:26:44 +02001712 //wchar_t fileName[MAX_PATH];
1713 //swprintf(fileName, MAX_PATH, L"After_%02u.csv", defragIndex);
1714 //SaveAllocatorStatsToFile(fileName);
Adam Sawickib8333fb2018-03-13 16:15:53 +01001715 }
1716 }
1717
1718 // Destroy all remaining allocations.
1719 DestroyAllAllocations(allocations);
1720}
1721
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001722static void TestDefragmentationGpu()
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001723{
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001724 wprintf(L"Test defragmentation GPU\n");
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001725
1726 std::vector<AllocInfo> allocations;
1727
1728 // Create that many allocations to surely fill 3 new blocks of 256 MB.
Adam Sawickic6ede152018-11-16 17:04:14 +01001729 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
1730 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001731 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic6ede152018-11-16 17:04:14 +01001732 const size_t bufCount = (size_t)(totalSize / bufSizeMin);
1733 const size_t percentToLeave = 30;
1734 const size_t percentNonMovable = 3;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001735 RandomNumberGenerator rand = { 234522 };
1736
1737 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001738
1739 VmaAllocationCreateInfo allocCreateInfo = {};
1740 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
Adam Sawickic6ede152018-11-16 17:04:14 +01001741 allocCreateInfo.flags = 0;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001742
1743 // Create all intended buffers.
1744 for(size_t i = 0; i < bufCount; ++i)
1745 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001746 bufCreateInfo.size = align_up(rand.Generate() % (bufSizeMax - bufSizeMin) + bufSizeMin, 32ull);
1747
1748 if(rand.Generate() % 100 < percentNonMovable)
1749 {
1750 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
1751 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1752 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1753 allocCreateInfo.pUserData = (void*)(uintptr_t)2;
1754 }
1755 else
1756 {
1757 // Different usage just to see different color in output from VmaDumpVis.
1758 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
1759 VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1760 VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1761 // And in JSON dump.
1762 allocCreateInfo.pUserData = (void*)(uintptr_t)1;
1763 }
1764
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001765 AllocInfo alloc;
1766 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
1767 alloc.m_StartValue = rand.Generate();
1768 allocations.push_back(alloc);
1769 }
1770
1771 // Destroy some percentage of them.
1772 {
1773 const size_t buffersToDestroy = round_div<size_t>(bufCount * (100 - percentToLeave), 100);
1774 for(size_t i = 0; i < buffersToDestroy; ++i)
1775 {
1776 const size_t index = rand.Generate() % allocations.size();
1777 allocations[index].Destroy();
1778 allocations.erase(allocations.begin() + index);
1779 }
1780 }
1781
1782 // Fill them with meaningful data.
1783 UploadGpuData(allocations.data(), allocations.size());
1784
Adam Sawickic6ede152018-11-16 17:04:14 +01001785 wchar_t fileName[MAX_PATH];
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001786 swprintf_s(fileName, L"GPU_defragmentation_A_before.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001787 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001788
1789 // Defragment using GPU only.
1790 {
1791 const size_t allocCount = allocations.size();
Adam Sawicki440307e2018-10-18 15:05:19 +02001792
Adam Sawickic6ede152018-11-16 17:04:14 +01001793 std::vector<VmaAllocation> allocationPtrs;
1794 std::vector<VkBool32> allocationChanged;
1795 std::vector<size_t> allocationOriginalIndex;
1796
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001797 for(size_t i = 0; i < allocCount; ++i)
1798 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001799 VmaAllocationInfo allocInfo = {};
1800 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
1801 if((uintptr_t)allocInfo.pUserData == 1) // Movable
1802 {
1803 allocationPtrs.push_back(allocations[i].m_Allocation);
1804 allocationChanged.push_back(VK_FALSE);
1805 allocationOriginalIndex.push_back(i);
1806 }
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001807 }
Adam Sawickic6ede152018-11-16 17:04:14 +01001808
1809 const size_t movableAllocCount = allocationPtrs.size();
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001810
1811 BeginSingleTimeCommands();
1812
1813 VmaDefragmentationInfo2 defragInfo = {};
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001814 defragInfo.flags = 0;
Adam Sawickic6ede152018-11-16 17:04:14 +01001815 defragInfo.allocationCount = (uint32_t)movableAllocCount;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001816 defragInfo.pAllocations = allocationPtrs.data();
Adam Sawicki440307e2018-10-18 15:05:19 +02001817 defragInfo.pAllocationsChanged = allocationChanged.data();
1818 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001819 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
1820 defragInfo.commandBuffer = g_hTemporaryCommandBuffer;
1821
1822 VmaDefragmentationStats stats = {};
1823 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
1824 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
1825 TEST(res >= VK_SUCCESS);
1826
1827 EndSingleTimeCommands();
1828
1829 vmaDefragmentationEnd(g_hAllocator, ctx);
1830
Adam Sawickic6ede152018-11-16 17:04:14 +01001831 for(size_t i = 0; i < movableAllocCount; ++i)
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001832 {
1833 if(allocationChanged[i])
1834 {
Adam Sawickic6ede152018-11-16 17:04:14 +01001835 const size_t origAllocIndex = allocationOriginalIndex[i];
1836 RecreateAllocationResource(allocations[origAllocIndex]);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001837 }
1838 }
1839
Adam Sawicki4d844e22019-01-24 16:21:05 +01001840 // If corruption detection is enabled, GPU defragmentation may not work on
1841 // memory types that have this detection active, e.g. on Intel.
Adam Sawickia1f727c2019-01-24 16:25:11 +01001842 #if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
Adam Sawicki4d844e22019-01-24 16:21:05 +01001843 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
1844 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
Adam Sawickia1f727c2019-01-24 16:25:11 +01001845 #endif
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001846 }
1847
1848 ValidateGpuData(allocations.data(), allocations.size());
1849
Adam Sawicki9a4f5082018-11-23 17:26:05 +01001850 swprintf_s(fileName, L"GPU_defragmentation_B_after.json");
Adam Sawickic6ede152018-11-16 17:04:14 +01001851 SaveAllocatorStatsToFile(fileName);
Adam Sawickiff0f7b82018-10-18 14:44:05 +02001852
1853 // Destroy all remaining buffers.
1854 for(size_t i = allocations.size(); i--; )
1855 {
1856 allocations[i].Destroy();
1857 }
1858}
1859
Adam Sawickic467e282019-12-23 16:38:31 +01001860static void ProcessDefragmentationStepInfo(VmaDefragmentationPassInfo &stepInfo)
Adam Sawickia52012d2019-12-23 15:28:51 +01001861{
1862 std::vector<VkImageMemoryBarrier> beginImageBarriers;
1863 std::vector<VkImageMemoryBarrier> finalizeImageBarriers;
1864
1865 VkPipelineStageFlags beginSrcStageMask = 0;
1866 VkPipelineStageFlags beginDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1867
1868 VkPipelineStageFlags finalizeSrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
1869 VkPipelineStageFlags finalizeDstStageMask = 0;
1870
1871 bool wantsMemoryBarrier = false;
1872
1873 VkMemoryBarrier beginMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1874 VkMemoryBarrier finalizeMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
1875
Adam Sawickic467e282019-12-23 16:38:31 +01001876 for(uint32_t i = 0; i < stepInfo.moveCount; ++i)
Adam Sawickia52012d2019-12-23 15:28:51 +01001877 {
1878 VmaAllocationInfo info;
1879 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1880
1881 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1882
1883 if(allocInfo->m_Image)
1884 {
1885 VkImage newImage;
1886
1887 const VkResult result = vkCreateImage(g_hDevice, &allocInfo->m_ImageInfo, g_Allocs, &newImage);
1888 TEST(result >= VK_SUCCESS);
1889
1890 vkBindImageMemory(g_hDevice, newImage, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001891 allocInfo->m_NewImage = newImage;
Adam Sawickia52012d2019-12-23 15:28:51 +01001892
1893 // Keep track of our pipeline stages that we need to wait/signal on
1894 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1895 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1896
1897 // We need one pipeline barrier and two image layout transitions here
1898 // First we'll have to turn our newly created image into VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
1899 // And the second one is turning the old image into VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
1900
1901 VkImageSubresourceRange subresourceRange = {
1902 VK_IMAGE_ASPECT_COLOR_BIT,
1903 0, VK_REMAINING_MIP_LEVELS,
1904 0, VK_REMAINING_ARRAY_LAYERS
1905 };
1906
1907 VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
1908 barrier.srcAccessMask = 0;
1909 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1910 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1911 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1912 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1913 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1914 barrier.image = newImage;
1915 barrier.subresourceRange = subresourceRange;
1916
1917 beginImageBarriers.push_back(barrier);
1918
1919 // Second barrier to convert the existing image. This one actually needs a real barrier
1920 barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
1921 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1922 barrier.oldLayout = allocInfo->m_ImageLayout;
1923 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1924 barrier.image = allocInfo->m_Image;
1925
1926 beginImageBarriers.push_back(barrier);
1927
1928 // And lastly we need a barrier that turns our new image into the layout of the old one
1929 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1930 barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
1931 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1932 barrier.newLayout = allocInfo->m_ImageLayout;
1933 barrier.image = newImage;
1934
1935 finalizeImageBarriers.push_back(barrier);
1936 }
1937 else if(allocInfo->m_Buffer)
1938 {
1939 VkBuffer newBuffer;
1940
1941 const VkResult result = vkCreateBuffer(g_hDevice, &allocInfo->m_BufferInfo, g_Allocs, &newBuffer);
1942 TEST(result >= VK_SUCCESS);
1943
1944 vkBindBufferMemory(g_hDevice, newBuffer, stepInfo.pMoves[i].memory, stepInfo.pMoves[i].offset);
Adam Sawickic467e282019-12-23 16:38:31 +01001945 allocInfo->m_NewBuffer = newBuffer;
Adam Sawickia52012d2019-12-23 15:28:51 +01001946
1947 // Keep track of our pipeline stages that we need to wait/signal on
1948 beginSrcStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1949 finalizeDstStageMask |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1950
1951 beginMemoryBarrier.srcAccessMask |= VK_ACCESS_MEMORY_WRITE_BIT;
1952 beginMemoryBarrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
1953
1954 finalizeMemoryBarrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
1955 finalizeMemoryBarrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
1956
1957 wantsMemoryBarrier = true;
1958 }
1959 }
1960
1961 if(!beginImageBarriers.empty() || wantsMemoryBarrier)
1962 {
1963 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
1964
1965 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, beginSrcStageMask, beginDstStageMask, 0,
1966 memoryBarrierCount, &beginMemoryBarrier,
1967 0, nullptr,
1968 (uint32_t)beginImageBarriers.size(), beginImageBarriers.data());
1969 }
1970
1971 for(uint32_t i = 0; i < stepInfo.moveCount; ++ i)
1972 {
1973 VmaAllocationInfo info;
1974 vmaGetAllocationInfo(g_hAllocator, stepInfo.pMoves[i].allocation, &info);
1975
1976 AllocInfo *allocInfo = (AllocInfo *)info.pUserData;
1977
1978 if(allocInfo->m_Image)
1979 {
1980 std::vector<VkImageCopy> imageCopies;
1981
1982 // Copy all mips of the source image into the target image
1983 VkOffset3D offset = { 0, 0, 0 };
1984 VkExtent3D extent = allocInfo->m_ImageInfo.extent;
1985
1986 VkImageSubresourceLayers subresourceLayers = {
1987 VK_IMAGE_ASPECT_COLOR_BIT,
1988 0,
1989 0, 1
1990 };
1991
1992 for(uint32_t mip = 0; mip < allocInfo->m_ImageInfo.mipLevels; ++ mip)
1993 {
1994 subresourceLayers.mipLevel = mip;
1995
1996 VkImageCopy imageCopy{
1997 subresourceLayers,
1998 offset,
1999 subresourceLayers,
2000 offset,
2001 extent
2002 };
2003
2004 imageCopies.push_back(imageCopy);
2005
2006 extent.width = std::max(uint32_t(1), extent.width >> 1);
2007 extent.height = std::max(uint32_t(1), extent.height >> 1);
2008 extent.depth = std::max(uint32_t(1), extent.depth >> 1);
2009 }
2010
2011 vkCmdCopyImage(
2012 g_hTemporaryCommandBuffer,
2013 allocInfo->m_Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
Adam Sawickic467e282019-12-23 16:38:31 +01002014 allocInfo->m_NewImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
Adam Sawickia52012d2019-12-23 15:28:51 +01002015 (uint32_t)imageCopies.size(), imageCopies.data());
Adam Sawickia52012d2019-12-23 15:28:51 +01002016 }
2017 else if(allocInfo->m_Buffer)
2018 {
2019 VkBufferCopy region = {
2020 0,
2021 0,
2022 allocInfo->m_BufferInfo.size };
2023
2024 vkCmdCopyBuffer(g_hTemporaryCommandBuffer,
Adam Sawickic467e282019-12-23 16:38:31 +01002025 allocInfo->m_Buffer, allocInfo->m_NewBuffer,
Adam Sawickia52012d2019-12-23 15:28:51 +01002026 1, &region);
Adam Sawickia52012d2019-12-23 15:28:51 +01002027 }
2028 }
2029
Adam Sawickia52012d2019-12-23 15:28:51 +01002030 if(!finalizeImageBarriers.empty() || wantsMemoryBarrier)
2031 {
2032 const uint32_t memoryBarrierCount = wantsMemoryBarrier ? 1 : 0;
2033
2034 vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, finalizeSrcStageMask, finalizeDstStageMask, 0,
2035 memoryBarrierCount, &finalizeMemoryBarrier,
2036 0, nullptr,
2037 (uint32_t)finalizeImageBarriers.size(), finalizeImageBarriers.data());
2038 }
2039}
2040
2041
2042static void TestDefragmentationIncrementalBasic()
2043{
2044 wprintf(L"Test defragmentation incremental basic\n");
Adam Sawickia52012d2019-12-23 15:28:51 +01002045
2046 std::vector<AllocInfo> allocations;
2047
2048 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2049 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2050 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2051 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2052 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
Adam Sawickic467e282019-12-23 16:38:31 +01002053 const size_t imageCount = totalSize / ((size_t)imageSizes[0] * imageSizes[0] * 4) / 2;
Adam Sawickia52012d2019-12-23 15:28:51 +01002054 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2055 const size_t percentToLeave = 30;
2056 RandomNumberGenerator rand = { 234522 };
2057
2058 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2059 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2060 imageInfo.extent.depth = 1;
2061 imageInfo.mipLevels = 1;
2062 imageInfo.arrayLayers = 1;
2063 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2064 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2065 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2066 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2067 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2068
2069 VmaAllocationCreateInfo allocCreateInfo = {};
2070 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2071 allocCreateInfo.flags = 0;
2072
2073 // Create all intended images.
2074 for(size_t i = 0; i < imageCount; ++i)
2075 {
2076 const uint32_t size = imageSizes[rand.Generate() % 3];
2077
2078 imageInfo.extent.width = size;
2079 imageInfo.extent.height = size;
2080
2081 AllocInfo alloc;
2082 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2083 alloc.m_StartValue = 0;
2084
2085 allocations.push_back(alloc);
2086 }
2087
2088 // And all buffers
2089 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2090
2091 for(size_t i = 0; i < bufCount; ++i)
2092 {
2093 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2094 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2095
2096 AllocInfo alloc;
2097 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2098 alloc.m_StartValue = 0;
2099
2100 allocations.push_back(alloc);
2101 }
2102
2103 // Destroy some percentage of them.
2104 {
2105 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2106 for(size_t i = 0; i < allocationsToDestroy; ++i)
2107 {
2108 const size_t index = rand.Generate() % allocations.size();
2109 allocations[index].Destroy();
2110 allocations.erase(allocations.begin() + index);
2111 }
2112 }
2113
2114 {
2115 // Set our user data pointers. A real application should probably be more clever here
2116 const size_t allocationCount = allocations.size();
2117 for(size_t i = 0; i < allocationCount; ++i)
2118 {
2119 AllocInfo &alloc = allocations[i];
2120 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2121 }
2122 }
2123
2124 // Fill them with meaningful data.
2125 UploadGpuData(allocations.data(), allocations.size());
2126
2127 wchar_t fileName[MAX_PATH];
2128 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_A_before.json");
2129 SaveAllocatorStatsToFile(fileName);
2130
2131 // Defragment using GPU only.
2132 {
2133 const size_t allocCount = allocations.size();
2134
2135 std::vector<VmaAllocation> allocationPtrs;
2136
2137 for(size_t i = 0; i < allocCount; ++i)
2138 {
Adam Sawickia52012d2019-12-23 15:28:51 +01002139 allocationPtrs.push_back(allocations[i].m_Allocation);
2140 }
2141
2142 const size_t movableAllocCount = allocationPtrs.size();
2143
2144 VmaDefragmentationInfo2 defragInfo = {};
2145 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2146 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2147 defragInfo.pAllocations = allocationPtrs.data();
2148 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2149 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2150
2151 VmaDefragmentationStats stats = {};
2152 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2153 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2154 TEST(res >= VK_SUCCESS);
2155
2156 res = VK_NOT_READY;
2157
Adam Sawickic467e282019-12-23 16:38:31 +01002158 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002159 moveInfo.resize(movableAllocCount);
2160
2161 while(res == VK_NOT_READY)
2162 {
Adam Sawickic467e282019-12-23 16:38:31 +01002163 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002164 stepInfo.pMoves = moveInfo.data();
2165 stepInfo.moveCount = (uint32_t)moveInfo.size();
2166
Adam Sawickic467e282019-12-23 16:38:31 +01002167 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002168 TEST(res >= VK_SUCCESS);
2169
2170 BeginSingleTimeCommands();
Adam Sawickic467e282019-12-23 16:38:31 +01002171 std::vector<void*> newHandles;
Adam Sawickia52012d2019-12-23 15:28:51 +01002172 ProcessDefragmentationStepInfo(stepInfo);
2173 EndSingleTimeCommands();
2174
Adam Sawickic467e282019-12-23 16:38:31 +01002175 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2176
2177 // Destroy old buffers/images and replace them with new handles.
2178 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2179 {
2180 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2181 VmaAllocationInfo vmaAllocInfo;
2182 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2183 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2184 if(allocInfo->m_Buffer)
2185 {
2186 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2187 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2188 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2189 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2190 }
2191 else if(allocInfo->m_Image)
2192 {
2193 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2194 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2195 allocInfo->m_Image = allocInfo->m_NewImage;
2196 allocInfo->m_NewImage = VK_NULL_HANDLE;
2197 }
2198 else
2199 assert(0);
2200 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002201 }
2202
2203 TEST(res >= VK_SUCCESS);
2204 vmaDefragmentationEnd(g_hAllocator, ctx);
2205
2206 // If corruption detection is enabled, GPU defragmentation may not work on
2207 // memory types that have this detection active, e.g. on Intel.
2208#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2209 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2210 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2211#endif
2212 }
2213
2214 //ValidateGpuData(allocations.data(), allocations.size());
2215
2216 swprintf_s(fileName, L"GPU_defragmentation_incremental_basic_B_after.json");
2217 SaveAllocatorStatsToFile(fileName);
2218
Adam Sawickic467e282019-12-23 16:38:31 +01002219 // Destroy all remaining buffers and images.
Adam Sawickia52012d2019-12-23 15:28:51 +01002220 for(size_t i = allocations.size(); i--; )
2221 {
2222 allocations[i].Destroy();
2223 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002224}
2225
2226void TestDefragmentationIncrementalComplex()
2227{
2228 wprintf(L"Test defragmentation incremental complex\n");
Adam Sawickidb4c1632020-07-16 16:41:53 +02002229
Adam Sawickia52012d2019-12-23 15:28:51 +01002230 std::vector<AllocInfo> allocations;
2231
2232 // Create that many allocations to surely fill 3 new blocks of 256 MB.
2233 const std::array<uint32_t, 3> imageSizes = { 256, 512, 1024 };
2234 const VkDeviceSize bufSizeMin = 5ull * 1024 * 1024;
2235 const VkDeviceSize bufSizeMax = 10ull * 1024 * 1024;
2236 const VkDeviceSize totalSize = 3ull * 256 * 1024 * 1024;
2237 const size_t imageCount = (size_t)(totalSize / (imageSizes[0] * imageSizes[0] * 4)) / 2;
2238 const size_t bufCount = (size_t)(totalSize / bufSizeMin) / 2;
2239 const size_t percentToLeave = 30;
2240 RandomNumberGenerator rand = { 234522 };
2241
2242 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
2243 imageInfo.imageType = VK_IMAGE_TYPE_2D;
2244 imageInfo.extent.depth = 1;
2245 imageInfo.mipLevels = 1;
2246 imageInfo.arrayLayers = 1;
2247 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
2248 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
2249 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2250 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
2251 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2252
2253 VmaAllocationCreateInfo allocCreateInfo = {};
2254 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2255 allocCreateInfo.flags = 0;
2256
2257 // Create all intended images.
2258 for(size_t i = 0; i < imageCount; ++i)
2259 {
2260 const uint32_t size = imageSizes[rand.Generate() % 3];
2261
2262 imageInfo.extent.width = size;
2263 imageInfo.extent.height = size;
2264
2265 AllocInfo alloc;
2266 alloc.CreateImage(imageInfo, allocCreateInfo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
2267 alloc.m_StartValue = 0;
2268
2269 allocations.push_back(alloc);
2270 }
2271
2272 // And all buffers
2273 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2274
2275 for(size_t i = 0; i < bufCount; ++i)
2276 {
2277 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
2278 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2279
2280 AllocInfo alloc;
2281 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo);
2282 alloc.m_StartValue = 0;
2283
2284 allocations.push_back(alloc);
2285 }
2286
2287 // Destroy some percentage of them.
2288 {
2289 const size_t allocationsToDestroy = round_div<size_t>((imageCount + bufCount) * (100 - percentToLeave), 100);
2290 for(size_t i = 0; i < allocationsToDestroy; ++i)
2291 {
2292 const size_t index = rand.Generate() % allocations.size();
2293 allocations[index].Destroy();
2294 allocations.erase(allocations.begin() + index);
2295 }
2296 }
2297
2298 {
2299 // Set our user data pointers. A real application should probably be more clever here
2300 const size_t allocationCount = allocations.size();
2301 for(size_t i = 0; i < allocationCount; ++i)
2302 {
2303 AllocInfo &alloc = allocations[i];
2304 vmaSetAllocationUserData(g_hAllocator, alloc.m_Allocation, &alloc);
2305 }
2306 }
2307
2308 // Fill them with meaningful data.
2309 UploadGpuData(allocations.data(), allocations.size());
2310
2311 wchar_t fileName[MAX_PATH];
2312 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_A_before.json");
2313 SaveAllocatorStatsToFile(fileName);
2314
2315 std::vector<AllocInfo> additionalAllocations;
2316
2317#define MakeAdditionalAllocation() \
2318 do { \
2319 { \
2320 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16); \
2321 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; \
2322 \
2323 AllocInfo alloc; \
2324 alloc.CreateBuffer(bufCreateInfo, allocCreateInfo); \
2325 \
2326 additionalAllocations.push_back(alloc); \
2327 } \
2328 } while(0)
2329
2330 // Defragment using GPU only.
2331 {
2332 const size_t allocCount = allocations.size();
2333
2334 std::vector<VmaAllocation> allocationPtrs;
2335
2336 for(size_t i = 0; i < allocCount; ++i)
2337 {
2338 VmaAllocationInfo allocInfo = {};
2339 vmaGetAllocationInfo(g_hAllocator, allocations[i].m_Allocation, &allocInfo);
2340
2341 allocationPtrs.push_back(allocations[i].m_Allocation);
2342 }
2343
2344 const size_t movableAllocCount = allocationPtrs.size();
2345
2346 VmaDefragmentationInfo2 defragInfo = {};
2347 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_INCREMENTAL;
2348 defragInfo.allocationCount = (uint32_t)movableAllocCount;
2349 defragInfo.pAllocations = allocationPtrs.data();
2350 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2351 defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2352
2353 VmaDefragmentationStats stats = {};
2354 VmaDefragmentationContext ctx = VK_NULL_HANDLE;
2355 VkResult res = vmaDefragmentationBegin(g_hAllocator, &defragInfo, &stats, &ctx);
2356 TEST(res >= VK_SUCCESS);
2357
2358 res = VK_NOT_READY;
2359
Adam Sawickic467e282019-12-23 16:38:31 +01002360 std::vector<VmaDefragmentationPassMoveInfo> moveInfo;
Adam Sawickia52012d2019-12-23 15:28:51 +01002361 moveInfo.resize(movableAllocCount);
2362
2363 MakeAdditionalAllocation();
2364
2365 while(res == VK_NOT_READY)
2366 {
Adam Sawickic467e282019-12-23 16:38:31 +01002367 VmaDefragmentationPassInfo stepInfo = {};
Adam Sawickia52012d2019-12-23 15:28:51 +01002368 stepInfo.pMoves = moveInfo.data();
2369 stepInfo.moveCount = (uint32_t)moveInfo.size();
2370
Adam Sawickic467e282019-12-23 16:38:31 +01002371 res = vmaBeginDefragmentationPass(g_hAllocator, ctx, &stepInfo);
Adam Sawickia52012d2019-12-23 15:28:51 +01002372 TEST(res >= VK_SUCCESS);
2373
2374 MakeAdditionalAllocation();
2375
2376 BeginSingleTimeCommands();
2377 ProcessDefragmentationStepInfo(stepInfo);
2378 EndSingleTimeCommands();
2379
Adam Sawickic467e282019-12-23 16:38:31 +01002380 res = vmaEndDefragmentationPass(g_hAllocator, ctx);
2381
2382 // Destroy old buffers/images and replace them with new handles.
2383 for(size_t i = 0; i < stepInfo.moveCount; ++i)
2384 {
2385 VmaAllocation const alloc = stepInfo.pMoves[i].allocation;
2386 VmaAllocationInfo vmaAllocInfo;
2387 vmaGetAllocationInfo(g_hAllocator, alloc, &vmaAllocInfo);
2388 AllocInfo* allocInfo = (AllocInfo*)vmaAllocInfo.pUserData;
2389 if(allocInfo->m_Buffer)
2390 {
2391 assert(allocInfo->m_NewBuffer && !allocInfo->m_Image && !allocInfo->m_NewImage);
2392 vkDestroyBuffer(g_hDevice, allocInfo->m_Buffer, g_Allocs);
2393 allocInfo->m_Buffer = allocInfo->m_NewBuffer;
2394 allocInfo->m_NewBuffer = VK_NULL_HANDLE;
2395 }
2396 else if(allocInfo->m_Image)
2397 {
2398 assert(allocInfo->m_NewImage && !allocInfo->m_Buffer && !allocInfo->m_NewBuffer);
2399 vkDestroyImage(g_hDevice, allocInfo->m_Image, g_Allocs);
2400 allocInfo->m_Image = allocInfo->m_NewImage;
2401 allocInfo->m_NewImage = VK_NULL_HANDLE;
2402 }
2403 else
2404 assert(0);
2405 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002406
2407 MakeAdditionalAllocation();
2408 }
2409
2410 TEST(res >= VK_SUCCESS);
2411 vmaDefragmentationEnd(g_hAllocator, ctx);
2412
2413 // If corruption detection is enabled, GPU defragmentation may not work on
2414 // memory types that have this detection active, e.g. on Intel.
2415#if !defined(VMA_DEBUG_DETECT_CORRUPTION) || VMA_DEBUG_DETECT_CORRUPTION == 0
2416 TEST(stats.allocationsMoved > 0 && stats.bytesMoved > 0);
2417 TEST(stats.deviceMemoryBlocksFreed > 0 && stats.bytesFreed > 0);
2418#endif
2419 }
2420
2421 //ValidateGpuData(allocations.data(), allocations.size());
2422
2423 swprintf_s(fileName, L"GPU_defragmentation_incremental_complex_B_after.json");
2424 SaveAllocatorStatsToFile(fileName);
2425
2426 // Destroy all remaining buffers.
2427 for(size_t i = allocations.size(); i--; )
2428 {
2429 allocations[i].Destroy();
2430 }
2431
2432 for(size_t i = additionalAllocations.size(); i--; )
2433 {
2434 additionalAllocations[i].Destroy();
2435 }
Adam Sawickia52012d2019-12-23 15:28:51 +01002436}
2437
2438
Adam Sawickib8333fb2018-03-13 16:15:53 +01002439static void TestUserData()
2440{
2441 VkResult res;
2442
2443 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2444 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2445 bufCreateInfo.size = 0x10000;
2446
2447 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
2448 {
2449 // Opaque pointer
2450 {
2451
2452 void* numberAsPointer = (void*)(size_t)0xC2501FF3u;
2453 void* pointerToSomething = &res;
2454
2455 VmaAllocationCreateInfo allocCreateInfo = {};
2456 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2457 allocCreateInfo.pUserData = numberAsPointer;
2458 if(testIndex == 1)
2459 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2460
2461 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2462 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002463 TEST(res == VK_SUCCESS);
2464 TEST(allocInfo.pUserData = numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002465
2466 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002467 TEST(allocInfo.pUserData == numberAsPointer);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002468
2469 vmaSetAllocationUserData(g_hAllocator, alloc, pointerToSomething);
2470 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002471 TEST(allocInfo.pUserData == pointerToSomething);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002472
2473 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2474 }
2475
2476 // String
2477 {
2478 const char* name1 = "Buffer name \\\"\'<>&% \nSecond line .,;=";
2479 const char* name2 = "2";
2480 const size_t name1Len = strlen(name1);
2481
2482 char* name1Buf = new char[name1Len + 1];
2483 strcpy_s(name1Buf, name1Len + 1, name1);
2484
2485 VmaAllocationCreateInfo allocCreateInfo = {};
2486 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2487 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
2488 allocCreateInfo.pUserData = name1Buf;
2489 if(testIndex == 1)
2490 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2491
2492 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2493 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002494 TEST(res == VK_SUCCESS);
2495 TEST(allocInfo.pUserData != nullptr && allocInfo.pUserData != name1Buf);
2496 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002497
2498 delete[] name1Buf;
2499
2500 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002501 TEST(strcmp(name1, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002502
2503 vmaSetAllocationUserData(g_hAllocator, alloc, (void*)name2);
2504 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002505 TEST(strcmp(name2, (const char*)allocInfo.pUserData) == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002506
2507 vmaSetAllocationUserData(g_hAllocator, alloc, nullptr);
2508 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002509 TEST(allocInfo.pUserData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002510
2511 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2512 }
2513 }
2514}
2515
Adam Sawicki370ab182018-11-08 16:31:00 +01002516static void TestInvalidAllocations()
2517{
2518 VkResult res;
2519
2520 VmaAllocationCreateInfo allocCreateInfo = {};
2521 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2522
2523 // Try to allocate 0 bytes.
2524 {
2525 VkMemoryRequirements memReq = {};
2526 memReq.size = 0; // !!!
2527 memReq.alignment = 4;
2528 memReq.memoryTypeBits = UINT32_MAX;
2529 VmaAllocation alloc = VK_NULL_HANDLE;
2530 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2531 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);
2532 }
2533
2534 // Try to create buffer with size = 0.
2535 {
2536 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2537 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2538 bufCreateInfo.size = 0; // !!!
2539 VkBuffer buf = VK_NULL_HANDLE;
2540 VmaAllocation alloc = VK_NULL_HANDLE;
2541 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
2542 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2543 }
2544
2545 // Try to create image with one dimension = 0.
2546 {
2547 VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2548 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
2549 imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
2550 imageCreateInfo.extent.width = 128;
2551 imageCreateInfo.extent.height = 0; // !!!
2552 imageCreateInfo.extent.depth = 1;
2553 imageCreateInfo.mipLevels = 1;
2554 imageCreateInfo.arrayLayers = 1;
2555 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
2556 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
2557 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
2558 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
2559 VkImage image = VK_NULL_HANDLE;
2560 VmaAllocation alloc = VK_NULL_HANDLE;
2561 res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);
2562 TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);
2563 }
2564}
2565
Adam Sawickib8333fb2018-03-13 16:15:53 +01002566static void TestMemoryRequirements()
2567{
2568 VkResult res;
2569 VkBuffer buf;
2570 VmaAllocation alloc;
2571 VmaAllocationInfo allocInfo;
2572
2573 const VkPhysicalDeviceMemoryProperties* memProps;
2574 vmaGetMemoryProperties(g_hAllocator, &memProps);
2575
2576 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2577 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2578 bufInfo.size = 128;
2579
2580 VmaAllocationCreateInfo allocCreateInfo = {};
2581
2582 // No requirements.
2583 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002584 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002585 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2586
2587 // Usage.
2588 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2589 allocCreateInfo.requiredFlags = 0;
2590 allocCreateInfo.preferredFlags = 0;
2591 allocCreateInfo.memoryTypeBits = UINT32_MAX;
2592
2593 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002594 TEST(res == VK_SUCCESS);
2595 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002596 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2597
2598 // Required flags, preferred flags.
2599 allocCreateInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
2600 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2601 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
2602 allocCreateInfo.memoryTypeBits = 0;
2603
2604 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002605 TEST(res == VK_SUCCESS);
2606 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
2607 TEST(memProps->memoryTypes[allocInfo.memoryType].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002608 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2609
2610 // memoryTypeBits.
2611 const uint32_t memType = allocInfo.memoryType;
2612 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2613 allocCreateInfo.requiredFlags = 0;
2614 allocCreateInfo.preferredFlags = 0;
2615 allocCreateInfo.memoryTypeBits = 1u << memType;
2616
2617 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002618 TEST(res == VK_SUCCESS);
2619 TEST(allocInfo.memoryType == memType);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002620 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2621
2622}
2623
Adam Sawickia1d992f2020-03-02 15:32:10 +01002624static void TestGetAllocatorInfo()
2625{
2626 wprintf(L"Test vnaGetAllocatorInfo\n");
2627
2628 VmaAllocatorInfo allocInfo = {};
2629 vmaGetAllocatorInfo(g_hAllocator, &allocInfo);
2630 TEST(allocInfo.instance == g_hVulkanInstance);
2631 TEST(allocInfo.physicalDevice == g_hPhysicalDevice);
2632 TEST(allocInfo.device == g_hDevice);
2633}
2634
Adam Sawickib8333fb2018-03-13 16:15:53 +01002635static void TestBasics()
2636{
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002637 wprintf(L"Test basics\n");
2638
Adam Sawickib8333fb2018-03-13 16:15:53 +01002639 VkResult res;
2640
Adam Sawickia1d992f2020-03-02 15:32:10 +01002641 TestGetAllocatorInfo();
2642
Adam Sawickib8333fb2018-03-13 16:15:53 +01002643 TestMemoryRequirements();
2644
2645 // Lost allocation
2646 {
2647 VmaAllocation alloc = VK_NULL_HANDLE;
2648 vmaCreateLostAllocation(g_hAllocator, &alloc);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002649 TEST(alloc != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002650
2651 VmaAllocationInfo allocInfo;
2652 vmaGetAllocationInfo(g_hAllocator, alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002653 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
2654 TEST(allocInfo.size == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002655
2656 vmaFreeMemory(g_hAllocator, alloc);
2657 }
2658
2659 // Allocation that is MAPPED and not necessarily HOST_VISIBLE.
2660 {
2661 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2662 bufCreateInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
2663 bufCreateInfo.size = 128;
2664
2665 VmaAllocationCreateInfo allocCreateInfo = {};
2666 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2667 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
2668
2669 VkBuffer buf; VmaAllocation alloc; VmaAllocationInfo allocInfo;
2670 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002671 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002672
2673 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2674
2675 // Same with OWN_MEMORY.
2676 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2677
2678 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002679 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002680
2681 vmaDestroyBuffer(g_hAllocator, buf, alloc);
2682 }
2683
2684 TestUserData();
Adam Sawicki370ab182018-11-08 16:31:00 +01002685
2686 TestInvalidAllocations();
Adam Sawickib8333fb2018-03-13 16:15:53 +01002687}
2688
Adam Sawickiaaa1a562020-06-24 17:41:09 +02002689static void TestAllocationVersusResourceSize()
2690{
2691 wprintf(L"Test allocation versus resource size\n");
2692
2693 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2694 bufCreateInfo.size = 22921; // Prime number
2695 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2696
2697 VmaAllocationCreateInfo allocCreateInfo = {};
2698 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2699
2700 for(uint32_t i = 0; i < 2; ++i)
2701 {
2702 allocCreateInfo.flags = (i == 1) ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0;
2703
2704 AllocInfo info;
2705 info.CreateBuffer(bufCreateInfo, allocCreateInfo);
2706
2707 VmaAllocationInfo allocInfo = {};
2708 vmaGetAllocationInfo(g_hAllocator, info.m_Allocation, &allocInfo);
2709 //wprintf(L" Buffer size = %llu, allocation size = %llu\n", bufCreateInfo.size, allocInfo.size);
2710
2711 // Map and test accessing entire area of the allocation, not only the buffer.
2712 void* mappedPtr = nullptr;
2713 VkResult res = vmaMapMemory(g_hAllocator, info.m_Allocation, &mappedPtr);
2714 TEST(res == VK_SUCCESS);
2715
2716 memset(mappedPtr, 0xCC, (size_t)allocInfo.size);
2717
2718 vmaUnmapMemory(g_hAllocator, info.m_Allocation);
2719
2720 info.Destroy();
2721 }
2722}
2723
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01002724static void TestPool_MinBlockCount()
2725{
2726#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
2727 return;
2728#endif
2729
2730 wprintf(L"Test Pool MinBlockCount\n");
2731 VkResult res;
2732
2733 static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
2734 static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
2735
2736 VmaAllocationCreateInfo allocCreateInfo = {};
2737 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
2738
2739 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2740 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2741 bufCreateInfo.size = ALLOC_SIZE;
2742
2743 VmaPoolCreateInfo poolCreateInfo = {};
2744 poolCreateInfo.blockSize = BLOCK_SIZE;
2745 poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
2746 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2747 TEST(res == VK_SUCCESS);
2748
2749 VmaPool pool = VK_NULL_HANDLE;
2750 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2751 TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
2752
2753 // Check that there are 2 blocks preallocated as requested.
2754 VmaPoolStats begPoolStats = {};
2755 vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
2756 TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
2757
2758 // Allocate 5 buffers to create 3 blocks.
2759 static const uint32_t BUF_COUNT = 5;
2760 allocCreateInfo.pool = pool;
2761 std::vector<AllocInfo> allocs(BUF_COUNT);
2762 for(uint32_t i = 0; i < BUF_COUNT; ++i)
2763 {
2764 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
2765 TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
2766 }
2767
2768 // Check that there are really 3 blocks.
2769 VmaPoolStats poolStats2 = {};
2770 vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
2771 TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
2772
2773 // Free two first allocations to make one block empty.
2774 allocs[0].Destroy();
2775 allocs[1].Destroy();
2776
2777 // Check that there are still 3 blocks due to hysteresis.
2778 VmaPoolStats poolStats3 = {};
2779 vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
2780 TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
2781
2782 // Free the last allocation to make second block empty.
2783 allocs[BUF_COUNT - 1].Destroy();
2784
2785 // Check that there are now 2 blocks only.
2786 VmaPoolStats poolStats4 = {};
2787 vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
2788 TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
2789
2790 // Cleanup.
2791 for(size_t i = allocs.size(); i--; )
2792 {
2793 allocs[i].Destroy();
2794 }
2795 vmaDestroyPool(g_hAllocator, pool);
2796}
2797
Adam Sawickib8333fb2018-03-13 16:15:53 +01002798void TestHeapSizeLimit()
2799{
Adam Sawickifbaccff2020-03-09 17:09:23 +01002800 const VkDeviceSize HEAP_SIZE_LIMIT = 100ull * 1024 * 1024; // 100 MB
2801 const VkDeviceSize BLOCK_SIZE = 10ull * 1024 * 1024; // 10 MB
Adam Sawickib8333fb2018-03-13 16:15:53 +01002802
2803 VkDeviceSize heapSizeLimit[VK_MAX_MEMORY_HEAPS];
2804 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
2805 {
2806 heapSizeLimit[i] = HEAP_SIZE_LIMIT;
2807 }
2808
2809 VmaAllocatorCreateInfo allocatorCreateInfo = {};
2810 allocatorCreateInfo.physicalDevice = g_hPhysicalDevice;
2811 allocatorCreateInfo.device = g_hDevice;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002812 allocatorCreateInfo.instance = g_hVulkanInstance;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002813 allocatorCreateInfo.pHeapSizeLimit = heapSizeLimit;
2814
2815 VmaAllocator hAllocator;
2816 VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &hAllocator);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002817 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002818
2819 struct Item
2820 {
2821 VkBuffer hBuf;
2822 VmaAllocation hAlloc;
2823 };
2824 std::vector<Item> items;
2825
2826 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2827 bufCreateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2828
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002829 // 1. Allocate two blocks of dedicated memory, half the size of BLOCK_SIZE.
2830 VmaAllocationInfo dedicatedAllocInfo;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002831 {
2832 VmaAllocationCreateInfo allocCreateInfo = {};
2833 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2834 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
2835
2836 bufCreateInfo.size = BLOCK_SIZE / 2;
2837
2838 for(size_t i = 0; i < 2; ++i)
2839 {
2840 Item item;
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002841 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, &dedicatedAllocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002842 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002843 items.push_back(item);
2844 }
2845 }
2846
2847 // Create pool to make sure allocations must be out of this memory type.
2848 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01002849 poolCreateInfo.memoryTypeIndex = dedicatedAllocInfo.memoryType;
Adam Sawickib8333fb2018-03-13 16:15:53 +01002850 poolCreateInfo.blockSize = BLOCK_SIZE;
2851
2852 VmaPool hPool;
2853 res = vmaCreatePool(hAllocator, &poolCreateInfo, &hPool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002854 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002855
2856 // 2. Allocate normal buffers from all the remaining memory.
2857 {
2858 VmaAllocationCreateInfo allocCreateInfo = {};
2859 allocCreateInfo.pool = hPool;
2860
2861 bufCreateInfo.size = BLOCK_SIZE / 2;
2862
2863 const size_t bufCount = ((HEAP_SIZE_LIMIT / BLOCK_SIZE) - 1) * 2;
2864 for(size_t i = 0; i < bufCount; ++i)
2865 {
2866 Item item;
2867 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &item.hBuf, &item.hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002868 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002869 items.push_back(item);
2870 }
2871 }
2872
2873 // 3. Allocation of one more (even small) buffer should fail.
2874 {
2875 VmaAllocationCreateInfo allocCreateInfo = {};
2876 allocCreateInfo.pool = hPool;
2877
2878 bufCreateInfo.size = 128;
2879
2880 VkBuffer hBuf;
2881 VmaAllocation hAlloc;
2882 res = vmaCreateBuffer(hAllocator, &bufCreateInfo, &allocCreateInfo, &hBuf, &hAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002883 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01002884 }
2885
2886 // Destroy everything.
2887 for(size_t i = items.size(); i--; )
2888 {
2889 vmaDestroyBuffer(hAllocator, items[i].hBuf, items[i].hAlloc);
2890 }
2891
2892 vmaDestroyPool(hAllocator, hPool);
2893
2894 vmaDestroyAllocator(hAllocator);
2895}
2896
Adam Sawicki212a4a62018-06-14 15:44:45 +02002897#if VMA_DEBUG_MARGIN
Adam Sawicki73b16652018-06-11 16:39:25 +02002898static void TestDebugMargin()
2899{
2900 if(VMA_DEBUG_MARGIN == 0)
2901 {
2902 return;
2903 }
2904
2905 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
Adam Sawicki212a4a62018-06-14 15:44:45 +02002906 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki73b16652018-06-11 16:39:25 +02002907
2908 VmaAllocationCreateInfo allocCreateInfo = {};
Adam Sawicki212a4a62018-06-14 15:44:45 +02002909 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
Adam Sawicki73b16652018-06-11 16:39:25 +02002910
2911 // Create few buffers of different size.
2912 const size_t BUF_COUNT = 10;
2913 BufferInfo buffers[BUF_COUNT];
2914 VmaAllocationInfo allocInfo[BUF_COUNT];
2915 for(size_t i = 0; i < 10; ++i)
2916 {
2917 bufInfo.size = (VkDeviceSize)(i + 1) * 64;
Adam Sawicki212a4a62018-06-14 15:44:45 +02002918 // Last one will be mapped.
2919 allocCreateInfo.flags = (i == BUF_COUNT - 1) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
Adam Sawicki73b16652018-06-11 16:39:25 +02002920
2921 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo, &buffers[i].Buffer, &buffers[i].Allocation, &allocInfo[i]);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002922 TEST(res == VK_SUCCESS);
Adam Sawicki73b16652018-06-11 16:39:25 +02002923 // Margin is preserved also at the beginning of a block.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002924 TEST(allocInfo[i].offset >= VMA_DEBUG_MARGIN);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002925
2926 if(i == BUF_COUNT - 1)
2927 {
2928 // Fill with data.
Adam Sawickib8d34d52018-10-03 17:41:20 +02002929 TEST(allocInfo[i].pMappedData != nullptr);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002930 // Uncomment this "+ 1" to overwrite past end of allocation and check corruption detection.
2931 memset(allocInfo[i].pMappedData, 0xFF, bufInfo.size /* + 1 */);
2932 }
Adam Sawicki73b16652018-06-11 16:39:25 +02002933 }
2934
2935 // Check if their offsets preserve margin between them.
2936 std::sort(allocInfo, allocInfo + BUF_COUNT, [](const VmaAllocationInfo& lhs, const VmaAllocationInfo& rhs) -> bool
2937 {
2938 if(lhs.deviceMemory != rhs.deviceMemory)
2939 {
2940 return lhs.deviceMemory < rhs.deviceMemory;
2941 }
2942 return lhs.offset < rhs.offset;
2943 });
2944 for(size_t i = 1; i < BUF_COUNT; ++i)
2945 {
2946 if(allocInfo[i].deviceMemory == allocInfo[i - 1].deviceMemory)
2947 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02002948 TEST(allocInfo[i].offset >= allocInfo[i - 1].offset + VMA_DEBUG_MARGIN);
Adam Sawicki73b16652018-06-11 16:39:25 +02002949 }
2950 }
2951
Adam Sawicki212a4a62018-06-14 15:44:45 +02002952 VkResult res = vmaCheckCorruption(g_hAllocator, UINT32_MAX);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002953 TEST(res == VK_SUCCESS);
Adam Sawicki212a4a62018-06-14 15:44:45 +02002954
Adam Sawicki73b16652018-06-11 16:39:25 +02002955 // Destroy all buffers.
2956 for(size_t i = BUF_COUNT; i--; )
2957 {
2958 vmaDestroyBuffer(g_hAllocator, buffers[i].Buffer, buffers[i].Allocation);
2959 }
2960}
Adam Sawicki212a4a62018-06-14 15:44:45 +02002961#endif
Adam Sawicki73b16652018-06-11 16:39:25 +02002962
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002963static void TestLinearAllocator()
2964{
2965 wprintf(L"Test linear allocator\n");
2966
2967 RandomNumberGenerator rand{645332};
2968
2969 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2970 sampleBufCreateInfo.size = 1024; // Whatever.
2971 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
2972
2973 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
2974 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
2975
2976 VmaPoolCreateInfo poolCreateInfo = {};
2977 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002978 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002979
Adam Sawickiee082772018-06-20 17:45:49 +02002980 poolCreateInfo.blockSize = 1024 * 300;
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002981 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
2982 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
2983
2984 VmaPool pool = nullptr;
2985 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02002986 TEST(res == VK_SUCCESS);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02002987
2988 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
2989
2990 VmaAllocationCreateInfo allocCreateInfo = {};
2991 allocCreateInfo.pool = pool;
2992
2993 constexpr size_t maxBufCount = 100;
2994 std::vector<BufferInfo> bufInfo;
2995
2996 constexpr VkDeviceSize bufSizeMin = 16;
2997 constexpr VkDeviceSize bufSizeMax = 1024;
2998 VmaAllocationInfo allocInfo;
2999 VkDeviceSize prevOffset = 0;
3000
3001 // Test one-time free.
3002 for(size_t i = 0; i < 2; ++i)
3003 {
3004 // Allocate number of buffers of varying size that surely fit into this block.
3005 VkDeviceSize bufSumSize = 0;
3006 for(size_t i = 0; i < maxBufCount; ++i)
3007 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003008 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003009 BufferInfo newBufInfo;
3010 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3011 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003012 TEST(res == VK_SUCCESS);
3013 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003014 bufInfo.push_back(newBufInfo);
3015 prevOffset = allocInfo.offset;
3016 bufSumSize += bufCreateInfo.size;
3017 }
3018
3019 // Validate pool stats.
3020 VmaPoolStats stats;
3021 vmaGetPoolStats(g_hAllocator, pool, &stats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003022 TEST(stats.size == poolCreateInfo.blockSize);
3023 TEST(stats.unusedSize = poolCreateInfo.blockSize - bufSumSize);
3024 TEST(stats.allocationCount == bufInfo.size());
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003025
3026 // Destroy the buffers in random order.
3027 while(!bufInfo.empty())
3028 {
3029 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3030 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3031 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3032 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3033 }
3034 }
3035
3036 // Test stack.
3037 {
3038 // Allocate number of buffers of varying size that surely fit into this block.
3039 for(size_t i = 0; i < maxBufCount; ++i)
3040 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003041 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003042 BufferInfo newBufInfo;
3043 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3044 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003045 TEST(res == VK_SUCCESS);
3046 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003047 bufInfo.push_back(newBufInfo);
3048 prevOffset = allocInfo.offset;
3049 }
3050
3051 // Destroy few buffers from top of the stack.
3052 for(size_t i = 0; i < maxBufCount / 5; ++i)
3053 {
3054 const BufferInfo& currBufInfo = bufInfo.back();
3055 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3056 bufInfo.pop_back();
3057 }
3058
3059 // Create some more
3060 for(size_t i = 0; i < maxBufCount / 5; ++i)
3061 {
Adam Sawickifd366b62019-01-24 15:26:43 +01003062 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003063 BufferInfo newBufInfo;
3064 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3065 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003066 TEST(res == VK_SUCCESS);
3067 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003068 bufInfo.push_back(newBufInfo);
3069 prevOffset = allocInfo.offset;
3070 }
3071
3072 // Destroy the buffers in reverse order.
3073 while(!bufInfo.empty())
3074 {
3075 const BufferInfo& currBufInfo = bufInfo.back();
3076 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3077 bufInfo.pop_back();
3078 }
3079 }
3080
Adam Sawickiee082772018-06-20 17:45:49 +02003081 // Test ring buffer.
3082 {
3083 // Allocate number of buffers that surely fit into this block.
3084 bufCreateInfo.size = bufSizeMax;
3085 for(size_t i = 0; i < maxBufCount; ++i)
3086 {
3087 BufferInfo newBufInfo;
3088 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3089 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003090 TEST(res == VK_SUCCESS);
3091 TEST(i == 0 || allocInfo.offset > prevOffset);
Adam Sawickiee082772018-06-20 17:45:49 +02003092 bufInfo.push_back(newBufInfo);
3093 prevOffset = allocInfo.offset;
3094 }
3095
3096 // Free and allocate new buffers so many times that we make sure we wrap-around at least once.
3097 const size_t buffersPerIter = maxBufCount / 10 - 1;
3098 const size_t iterCount = poolCreateInfo.blockSize / bufCreateInfo.size / buffersPerIter * 2;
3099 for(size_t iter = 0; iter < iterCount; ++iter)
3100 {
3101 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3102 {
3103 const BufferInfo& currBufInfo = bufInfo.front();
3104 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3105 bufInfo.erase(bufInfo.begin());
3106 }
3107 for(size_t bufPerIter = 0; bufPerIter < buffersPerIter; ++bufPerIter)
3108 {
3109 BufferInfo newBufInfo;
3110 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3111 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003112 TEST(res == VK_SUCCESS);
Adam Sawickiee082772018-06-20 17:45:49 +02003113 bufInfo.push_back(newBufInfo);
3114 }
3115 }
3116
3117 // Allocate buffers until we reach out-of-memory.
3118 uint32_t debugIndex = 0;
3119 while(res == VK_SUCCESS)
3120 {
3121 BufferInfo newBufInfo;
3122 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3123 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3124 if(res == VK_SUCCESS)
3125 {
3126 bufInfo.push_back(newBufInfo);
3127 }
3128 else
3129 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003130 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickiee082772018-06-20 17:45:49 +02003131 }
3132 ++debugIndex;
3133 }
3134
3135 // Destroy the buffers in random order.
3136 while(!bufInfo.empty())
3137 {
3138 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3139 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3140 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3141 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3142 }
3143 }
3144
Adam Sawicki680b2252018-08-22 14:47:32 +02003145 // Test double stack.
3146 {
3147 // Allocate number of buffers of varying size that surely fit into this block, alternate from bottom/top.
3148 VkDeviceSize prevOffsetLower = 0;
3149 VkDeviceSize prevOffsetUpper = poolCreateInfo.blockSize;
3150 for(size_t i = 0; i < maxBufCount; ++i)
3151 {
3152 const bool upperAddress = (i % 2) != 0;
3153 if(upperAddress)
3154 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3155 else
3156 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003157 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003158 BufferInfo newBufInfo;
3159 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3160 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003161 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003162 if(upperAddress)
3163 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003164 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003165 prevOffsetUpper = allocInfo.offset;
3166 }
3167 else
3168 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003169 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003170 prevOffsetLower = allocInfo.offset;
3171 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003172 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003173 bufInfo.push_back(newBufInfo);
3174 }
3175
3176 // Destroy few buffers from top of the stack.
3177 for(size_t i = 0; i < maxBufCount / 5; ++i)
3178 {
3179 const BufferInfo& currBufInfo = bufInfo.back();
3180 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3181 bufInfo.pop_back();
3182 }
3183
3184 // Create some more
3185 for(size_t i = 0; i < maxBufCount / 5; ++i)
3186 {
3187 const bool upperAddress = (i % 2) != 0;
3188 if(upperAddress)
3189 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3190 else
3191 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003192 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003193 BufferInfo newBufInfo;
3194 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3195 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003196 TEST(res == VK_SUCCESS);
Adam Sawicki680b2252018-08-22 14:47:32 +02003197 bufInfo.push_back(newBufInfo);
3198 }
3199
3200 // Destroy the buffers in reverse order.
3201 while(!bufInfo.empty())
3202 {
3203 const BufferInfo& currBufInfo = bufInfo.back();
3204 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3205 bufInfo.pop_back();
3206 }
3207
3208 // Create buffers on both sides until we reach out of memory.
3209 prevOffsetLower = 0;
3210 prevOffsetUpper = poolCreateInfo.blockSize;
3211 res = VK_SUCCESS;
3212 for(size_t i = 0; res == VK_SUCCESS; ++i)
3213 {
3214 const bool upperAddress = (i % 2) != 0;
3215 if(upperAddress)
3216 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3217 else
3218 allocCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
Adam Sawickifd366b62019-01-24 15:26:43 +01003219 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki680b2252018-08-22 14:47:32 +02003220 BufferInfo newBufInfo;
3221 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3222 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3223 if(res == VK_SUCCESS)
3224 {
3225 if(upperAddress)
3226 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003227 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003228 prevOffsetUpper = allocInfo.offset;
3229 }
3230 else
3231 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003232 TEST(allocInfo.offset >= prevOffsetLower);
Adam Sawicki680b2252018-08-22 14:47:32 +02003233 prevOffsetLower = allocInfo.offset;
3234 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003235 TEST(prevOffsetLower < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003236 bufInfo.push_back(newBufInfo);
3237 }
3238 }
3239
3240 // Destroy the buffers in random order.
3241 while(!bufInfo.empty())
3242 {
3243 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3244 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3245 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3246 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3247 }
3248
3249 // Create buffers on upper side only, constant size, until we reach out of memory.
3250 prevOffsetUpper = poolCreateInfo.blockSize;
3251 res = VK_SUCCESS;
3252 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3253 bufCreateInfo.size = bufSizeMax;
3254 for(size_t i = 0; res == VK_SUCCESS; ++i)
3255 {
3256 BufferInfo newBufInfo;
3257 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3258 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3259 if(res == VK_SUCCESS)
3260 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02003261 TEST(allocInfo.offset < prevOffsetUpper);
Adam Sawicki680b2252018-08-22 14:47:32 +02003262 prevOffsetUpper = allocInfo.offset;
3263 bufInfo.push_back(newBufInfo);
3264 }
3265 }
3266
3267 // Destroy the buffers in reverse order.
3268 while(!bufInfo.empty())
3269 {
3270 const BufferInfo& currBufInfo = bufInfo.back();
3271 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3272 bufInfo.pop_back();
3273 }
3274 }
3275
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003276 // Test ring buffer with lost allocations.
3277 {
3278 // Allocate number of buffers until pool is full.
3279 // Notice CAN_BECOME_LOST flag and call to vmaSetCurrentFrameIndex.
3280 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT;
3281 res = VK_SUCCESS;
3282 for(size_t i = 0; res == VK_SUCCESS; ++i)
3283 {
3284 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3285
Adam Sawickifd366b62019-01-24 15:26:43 +01003286 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003287
3288 BufferInfo newBufInfo;
3289 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3290 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3291 if(res == VK_SUCCESS)
3292 bufInfo.push_back(newBufInfo);
3293 }
3294
3295 // Free first half of it.
3296 {
3297 const size_t buffersToDelete = bufInfo.size() / 2;
3298 for(size_t i = 0; i < buffersToDelete; ++i)
3299 {
3300 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3301 }
3302 bufInfo.erase(bufInfo.begin(), bufInfo.begin() + buffersToDelete);
3303 }
3304
3305 // Allocate number of buffers until pool is full again.
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003306 // This way we make sure ring buffers wraps around, front in in the middle.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003307 res = VK_SUCCESS;
3308 for(size_t i = 0; res == VK_SUCCESS; ++i)
3309 {
3310 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3311
Adam Sawickifd366b62019-01-24 15:26:43 +01003312 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003313
3314 BufferInfo newBufInfo;
3315 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3316 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
3317 if(res == VK_SUCCESS)
3318 bufInfo.push_back(newBufInfo);
3319 }
3320
3321 VkDeviceSize firstNewOffset;
3322 {
3323 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3324
3325 // Allocate a large buffer with CAN_MAKE_OTHER_LOST.
3326 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
3327 bufCreateInfo.size = bufSizeMax;
3328
3329 BufferInfo newBufInfo;
3330 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3331 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003332 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003333 bufInfo.push_back(newBufInfo);
3334 firstNewOffset = allocInfo.offset;
3335
3336 // Make sure at least one buffer from the beginning became lost.
3337 vmaGetAllocationInfo(g_hAllocator, bufInfo[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003338 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003339 }
3340
Adam Sawickifd366b62019-01-24 15:26:43 +01003341#if 0 // TODO Fix and uncomment. Failing on Intel.
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003342 // Allocate more buffers that CAN_MAKE_OTHER_LOST until we wrap-around with this.
3343 size_t newCount = 1;
3344 for(;;)
3345 {
3346 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3347
Adam Sawickifd366b62019-01-24 15:26:43 +01003348 bufCreateInfo.size = align_up<VkDeviceSize>(bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin), 16);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003349
3350 BufferInfo newBufInfo;
3351 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3352 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickifd366b62019-01-24 15:26:43 +01003353
Adam Sawickib8d34d52018-10-03 17:41:20 +02003354 TEST(res == VK_SUCCESS);
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003355 bufInfo.push_back(newBufInfo);
3356 ++newCount;
3357 if(allocInfo.offset < firstNewOffset)
3358 break;
3359 }
Adam Sawickifd366b62019-01-24 15:26:43 +01003360#endif
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003361
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003362 // Delete buffers that are lost.
3363 for(size_t i = bufInfo.size(); i--; )
3364 {
3365 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3366 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3367 {
3368 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3369 bufInfo.erase(bufInfo.begin() + i);
3370 }
3371 }
3372
3373 // Test vmaMakePoolAllocationsLost
3374 {
3375 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
3376
Adam Sawicki4d35a5d2019-01-24 15:51:59 +01003377 size_t lostAllocCount = 0;
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003378 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostAllocCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003379 TEST(lostAllocCount > 0);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003380
3381 size_t realLostAllocCount = 0;
3382 for(size_t i = 0; i < bufInfo.size(); ++i)
3383 {
3384 vmaGetAllocationInfo(g_hAllocator, bufInfo[i].Allocation, &allocInfo);
3385 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
3386 ++realLostAllocCount;
3387 }
Adam Sawickib8d34d52018-10-03 17:41:20 +02003388 TEST(realLostAllocCount == lostAllocCount);
Adam Sawicki0ebdf0c2018-08-22 17:02:44 +02003389 }
3390
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02003391 // Destroy all the buffers in forward order.
3392 for(size_t i = 0; i < bufInfo.size(); ++i)
3393 vmaDestroyBuffer(g_hAllocator, bufInfo[i].Buffer, bufInfo[i].Allocation);
3394 bufInfo.clear();
3395 }
3396
Adam Sawicki70a683e2018-08-24 15:36:32 +02003397 vmaDestroyPool(g_hAllocator, pool);
3398}
Adam Sawickif799c4f2018-08-23 10:40:30 +02003399
Adam Sawicki70a683e2018-08-24 15:36:32 +02003400static void TestLinearAllocatorMultiBlock()
3401{
3402 wprintf(L"Test linear allocator multi block\n");
3403
3404 RandomNumberGenerator rand{345673};
3405
3406 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3407 sampleBufCreateInfo.size = 1024 * 1024;
3408 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3409
3410 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3411 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3412
3413 VmaPoolCreateInfo poolCreateInfo = {};
3414 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3415 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003416 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003417
3418 VmaPool pool = nullptr;
3419 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003420 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003421
3422 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3423
3424 VmaAllocationCreateInfo allocCreateInfo = {};
3425 allocCreateInfo.pool = pool;
3426
3427 std::vector<BufferInfo> bufInfo;
3428 VmaAllocationInfo allocInfo;
3429
3430 // Test one-time free.
3431 {
3432 // Allocate buffers until we move to a second block.
3433 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3434 for(uint32_t i = 0; ; ++i)
3435 {
3436 BufferInfo newBufInfo;
3437 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3438 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003439 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003440 bufInfo.push_back(newBufInfo);
3441 if(lastMem && allocInfo.deviceMemory != lastMem)
3442 {
3443 break;
3444 }
3445 lastMem = allocInfo.deviceMemory;
3446 }
3447
Adam Sawickib8d34d52018-10-03 17:41:20 +02003448 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003449
3450 // Make sure that pool has now two blocks.
3451 VmaPoolStats poolStats = {};
3452 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003453 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003454
3455 // Destroy all the buffers in random order.
3456 while(!bufInfo.empty())
3457 {
3458 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
3459 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
3460 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3461 bufInfo.erase(bufInfo.begin() + indexToDestroy);
3462 }
3463
3464 // Make sure that pool has now at most one block.
3465 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003466 TEST(poolStats.blockCount <= 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003467 }
3468
3469 // Test stack.
3470 {
3471 // Allocate buffers until we move to a second block.
3472 VkDeviceMemory lastMem = VK_NULL_HANDLE;
3473 for(uint32_t i = 0; ; ++i)
3474 {
3475 BufferInfo newBufInfo;
3476 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3477 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003478 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003479 bufInfo.push_back(newBufInfo);
3480 if(lastMem && allocInfo.deviceMemory != lastMem)
3481 {
3482 break;
3483 }
3484 lastMem = allocInfo.deviceMemory;
3485 }
3486
Adam Sawickib8d34d52018-10-03 17:41:20 +02003487 TEST(bufInfo.size() > 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003488
3489 // Add few more buffers.
3490 for(uint32_t i = 0; i < 5; ++i)
3491 {
3492 BufferInfo newBufInfo;
3493 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3494 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003495 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003496 bufInfo.push_back(newBufInfo);
3497 }
3498
3499 // Make sure that pool has now two blocks.
3500 VmaPoolStats poolStats = {};
3501 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003502 TEST(poolStats.blockCount == 2);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003503
3504 // Delete half of buffers, LIFO.
3505 for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
3506 {
3507 const BufferInfo& currBufInfo = bufInfo.back();
3508 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3509 bufInfo.pop_back();
3510 }
3511
3512 // Add one more buffer.
3513 BufferInfo newBufInfo;
3514 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3515 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003516 TEST(res == VK_SUCCESS);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003517 bufInfo.push_back(newBufInfo);
3518
3519 // Make sure that pool has now one block.
3520 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003521 TEST(poolStats.blockCount == 1);
Adam Sawicki70a683e2018-08-24 15:36:32 +02003522
3523 // Delete all the remaining buffers, LIFO.
3524 while(!bufInfo.empty())
3525 {
3526 const BufferInfo& currBufInfo = bufInfo.back();
3527 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3528 bufInfo.pop_back();
3529 }
Adam Sawickif799c4f2018-08-23 10:40:30 +02003530 }
3531
Adam Sawicki0876c0d2018-06-20 15:18:11 +02003532 vmaDestroyPool(g_hAllocator, pool);
3533}
3534
Adam Sawickifd11d752018-08-22 15:02:10 +02003535static void ManuallyTestLinearAllocator()
3536{
3537 VmaStats origStats;
3538 vmaCalculateStats(g_hAllocator, &origStats);
3539
3540 wprintf(L"Manually test linear allocator\n");
3541
3542 RandomNumberGenerator rand{645332};
3543
3544 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3545 sampleBufCreateInfo.size = 1024; // Whatever.
3546 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3547
3548 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3549 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3550
3551 VmaPoolCreateInfo poolCreateInfo = {};
3552 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003553 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003554
3555 poolCreateInfo.blockSize = 10 * 1024;
3556 poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3557 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3558
3559 VmaPool pool = nullptr;
3560 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003561 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003562
3563 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
3564
3565 VmaAllocationCreateInfo allocCreateInfo = {};
3566 allocCreateInfo.pool = pool;
3567
3568 std::vector<BufferInfo> bufInfo;
3569 VmaAllocationInfo allocInfo;
3570 BufferInfo newBufInfo;
3571
3572 // Test double stack.
3573 {
3574 /*
3575 Lower: Buffer 32 B, Buffer 1024 B, Buffer 32 B
3576 Upper: Buffer 16 B, Buffer 1024 B, Buffer 128 B
3577
3578 Totally:
3579 1 block allocated
3580 10240 Vulkan bytes
3581 6 new allocations
3582 2256 bytes in allocations
3583 */
3584
3585 bufCreateInfo.size = 32;
3586 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3587 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003588 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003589 bufInfo.push_back(newBufInfo);
3590
3591 bufCreateInfo.size = 1024;
3592 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3593 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003594 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003595 bufInfo.push_back(newBufInfo);
3596
3597 bufCreateInfo.size = 32;
3598 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3599 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003600 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003601 bufInfo.push_back(newBufInfo);
3602
3603 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT;
3604
3605 bufCreateInfo.size = 128;
3606 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3607 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003608 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003609 bufInfo.push_back(newBufInfo);
3610
3611 bufCreateInfo.size = 1024;
3612 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3613 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003614 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003615 bufInfo.push_back(newBufInfo);
3616
3617 bufCreateInfo.size = 16;
3618 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3619 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003620 TEST(res == VK_SUCCESS);
Adam Sawickifd11d752018-08-22 15:02:10 +02003621 bufInfo.push_back(newBufInfo);
3622
3623 VmaStats currStats;
3624 vmaCalculateStats(g_hAllocator, &currStats);
3625 VmaPoolStats poolStats;
3626 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
3627
3628 char* statsStr = nullptr;
3629 vmaBuildStatsString(g_hAllocator, &statsStr, VK_TRUE);
3630
3631 // PUT BREAKPOINT HERE TO CHECK.
3632 // Inspect: currStats versus origStats, poolStats, statsStr.
3633 int I = 0;
3634
3635 vmaFreeStatsString(g_hAllocator, statsStr);
3636
3637 // Destroy the buffers in reverse order.
3638 while(!bufInfo.empty())
3639 {
3640 const BufferInfo& currBufInfo = bufInfo.back();
3641 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
3642 bufInfo.pop_back();
3643 }
3644 }
3645
3646 vmaDestroyPool(g_hAllocator, pool);
3647}
3648
Adam Sawicki80927152018-09-07 17:27:23 +02003649static void BenchmarkAlgorithmsCase(FILE* file,
3650 uint32_t algorithm,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003651 bool empty,
3652 VmaAllocationCreateFlags allocStrategy,
3653 FREE_ORDER freeOrder)
Adam Sawicki0a607132018-08-24 11:18:41 +02003654{
3655 RandomNumberGenerator rand{16223};
3656
3657 const VkDeviceSize bufSizeMin = 32;
3658 const VkDeviceSize bufSizeMax = 1024;
3659 const size_t maxBufCapacity = 10000;
3660 const uint32_t iterationCount = 10;
3661
3662 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3663 sampleBufCreateInfo.size = bufSizeMax;
3664 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3665
3666 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
3667 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3668
3669 VmaPoolCreateInfo poolCreateInfo = {};
3670 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003671 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003672
3673 poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
Adam Sawicki80927152018-09-07 17:27:23 +02003674 poolCreateInfo.flags |= algorithm;
Adam Sawicki0a607132018-08-24 11:18:41 +02003675 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
3676
3677 VmaPool pool = nullptr;
3678 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003679 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003680
3681 // Buffer created just to get memory requirements. Never bound to any memory.
3682 VkBuffer dummyBuffer = VK_NULL_HANDLE;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003683 res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003684 TEST(res == VK_SUCCESS && dummyBuffer);
Adam Sawicki0a607132018-08-24 11:18:41 +02003685
3686 VkMemoryRequirements memReq = {};
3687 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3688
Adam Sawicki1f84f622019-07-02 13:40:01 +02003689 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawicki0a607132018-08-24 11:18:41 +02003690
3691 VmaAllocationCreateInfo allocCreateInfo = {};
3692 allocCreateInfo.pool = pool;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003693 allocCreateInfo.flags = allocStrategy;
Adam Sawicki0a607132018-08-24 11:18:41 +02003694
3695 VmaAllocation alloc;
3696 std::vector<VmaAllocation> baseAllocations;
3697
3698 if(!empty)
3699 {
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003700 // Make allocations up to 1/3 of pool size.
Adam Sawicki0a607132018-08-24 11:18:41 +02003701 VkDeviceSize totalSize = 0;
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003702 while(totalSize < poolCreateInfo.blockSize / 3)
Adam Sawicki0a607132018-08-24 11:18:41 +02003703 {
Adam Sawicki4d844e22019-01-24 16:21:05 +01003704 // This test intentionally allows sizes that are aligned to 4 or 16 bytes.
3705 // This is theoretically allowed and already uncovered one bug.
Adam Sawicki0a607132018-08-24 11:18:41 +02003706 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3707 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003708 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003709 baseAllocations.push_back(alloc);
3710 totalSize += memReq.size;
3711 }
3712
3713 // Delete half of them, choose randomly.
3714 size_t allocsToDelete = baseAllocations.size() / 2;
3715 for(size_t i = 0; i < allocsToDelete; ++i)
3716 {
3717 const size_t index = (size_t)rand.Generate() % baseAllocations.size();
3718 vmaFreeMemory(g_hAllocator, baseAllocations[index]);
3719 baseAllocations.erase(baseAllocations.begin() + index);
3720 }
3721 }
3722
3723 // BENCHMARK
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003724 const size_t allocCount = maxBufCapacity / 3;
Adam Sawicki0a607132018-08-24 11:18:41 +02003725 std::vector<VmaAllocation> testAllocations;
3726 testAllocations.reserve(allocCount);
3727 duration allocTotalDuration = duration::zero();
3728 duration freeTotalDuration = duration::zero();
3729 for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
3730 {
3731 // Allocations
3732 time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
3733 for(size_t i = 0; i < allocCount; ++i)
3734 {
3735 memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
3736 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003737 TEST(res == VK_SUCCESS);
Adam Sawicki0a607132018-08-24 11:18:41 +02003738 testAllocations.push_back(alloc);
3739 }
3740 allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
3741
3742 // Deallocations
3743 switch(freeOrder)
3744 {
3745 case FREE_ORDER::FORWARD:
3746 // Leave testAllocations unchanged.
3747 break;
3748 case FREE_ORDER::BACKWARD:
3749 std::reverse(testAllocations.begin(), testAllocations.end());
3750 break;
3751 case FREE_ORDER::RANDOM:
3752 std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
3753 break;
3754 default: assert(0);
3755 }
3756
3757 time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
3758 for(size_t i = 0; i < allocCount; ++i)
3759 vmaFreeMemory(g_hAllocator, testAllocations[i]);
3760 freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
3761
3762 testAllocations.clear();
3763 }
3764
3765 // Delete baseAllocations
3766 while(!baseAllocations.empty())
3767 {
3768 vmaFreeMemory(g_hAllocator, baseAllocations.back());
3769 baseAllocations.pop_back();
3770 }
3771
3772 vmaDestroyPool(g_hAllocator, pool);
3773
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003774 const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
3775 const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
3776
Adam Sawicki80927152018-09-07 17:27:23 +02003777 printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
3778 AlgorithmToStr(algorithm),
Adam Sawicki0667e332018-08-24 17:26:44 +02003779 empty ? "Empty" : "Not empty",
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003780 GetAllocationStrategyName(allocStrategy),
Adam Sawicki0a607132018-08-24 11:18:41 +02003781 FREE_ORDER_NAMES[(size_t)freeOrder],
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003782 allocTotalSeconds,
3783 freeTotalSeconds);
3784
3785 if(file)
3786 {
3787 std::string currTime;
3788 CurrentTimeToStr(currTime);
3789
Adam Sawicki80927152018-09-07 17:27:23 +02003790 fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003791 CODE_DESCRIPTION, currTime.c_str(),
Adam Sawicki80927152018-09-07 17:27:23 +02003792 AlgorithmToStr(algorithm),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003793 empty ? 1 : 0,
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003794 GetAllocationStrategyName(allocStrategy),
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003795 FREE_ORDER_NAMES[(uint32_t)freeOrder],
3796 allocTotalSeconds,
3797 freeTotalSeconds);
3798 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003799}
3800
Adam Sawickie73e9882020-03-20 18:05:42 +01003801static void TestBufferDeviceAddress()
3802{
3803 wprintf(L"Test buffer device address\n");
3804
3805 assert(g_BufferDeviceAddressEnabled);
3806
3807 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3808 bufCreateInfo.size = 0x10000;
3809 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
3810 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; // !!!
3811
3812 VmaAllocationCreateInfo allocCreateInfo = {};
3813 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3814
3815 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
3816 {
3817 // 1st is placed, 2nd is dedicated.
3818 if(testIndex == 1)
3819 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3820
3821 BufferInfo bufInfo = {};
3822 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3823 &bufInfo.Buffer, &bufInfo.Allocation, nullptr);
3824 TEST(res == VK_SUCCESS);
3825
3826 VkBufferDeviceAddressInfoEXT bufferDeviceAddressInfo = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT };
3827 bufferDeviceAddressInfo.buffer = bufInfo.Buffer;
3828 //assert(g_vkGetBufferDeviceAddressEXT != nullptr);
3829 if(g_vkGetBufferDeviceAddressEXT != nullptr)
3830 {
3831 VkDeviceAddress addr = g_vkGetBufferDeviceAddressEXT(g_hDevice, &bufferDeviceAddressInfo);
3832 TEST(addr != 0);
3833 }
3834
3835 vmaDestroyBuffer(g_hAllocator, bufInfo.Buffer, bufInfo.Allocation);
3836 }
3837}
3838
Adam Sawickif2012052021-01-11 18:04:42 +01003839static void TestMemoryPriority()
3840{
3841 wprintf(L"Test memory priority\n");
3842
3843 assert(VK_EXT_memory_priority_enabled);
3844
3845 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3846 bufCreateInfo.size = 0x10000;
3847 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3848
3849 VmaAllocationCreateInfo allocCreateInfo = {};
3850 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
3851 allocCreateInfo.priority = 1.f;
3852
3853 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
3854 {
3855 // 1st is placed, 2nd is dedicated.
3856 if(testIndex == 1)
3857 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3858
3859 BufferInfo bufInfo = {};
3860 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
3861 &bufInfo.Buffer, &bufInfo.Allocation, nullptr);
3862 TEST(res == VK_SUCCESS);
3863
3864 // There is nothing we can do to validate the priority.
3865
3866 vmaDestroyBuffer(g_hAllocator, bufInfo.Buffer, bufInfo.Allocation);
3867 }
3868}
3869
Adam Sawicki80927152018-09-07 17:27:23 +02003870static void BenchmarkAlgorithms(FILE* file)
Adam Sawicki0a607132018-08-24 11:18:41 +02003871{
Adam Sawicki80927152018-09-07 17:27:23 +02003872 wprintf(L"Benchmark algorithms\n");
Adam Sawicki0a607132018-08-24 11:18:41 +02003873
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003874 if(file)
3875 {
3876 fprintf(file,
3877 "Code,Time,"
Adam Sawicki80927152018-09-07 17:27:23 +02003878 "Algorithm,Empty,Allocation strategy,Free order,"
Adam Sawicki33d2ce72018-08-27 13:59:13 +02003879 "Allocation time (s),Deallocation time (s)\n");
3880 }
3881
Adam Sawicki0a607132018-08-24 11:18:41 +02003882 uint32_t freeOrderCount = 1;
3883 if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
3884 freeOrderCount = 3;
3885 else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
3886 freeOrderCount = 2;
3887
3888 const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003889 const uint32_t allocStrategyCount = GetAllocationStrategyCount();
Adam Sawicki0a607132018-08-24 11:18:41 +02003890
3891 for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
3892 {
3893 FREE_ORDER freeOrder = FREE_ORDER::COUNT;
3894 switch(freeOrderIndex)
3895 {
3896 case 0: freeOrder = FREE_ORDER::BACKWARD; break;
3897 case 1: freeOrder = FREE_ORDER::FORWARD; break;
3898 case 2: freeOrder = FREE_ORDER::RANDOM; break;
3899 default: assert(0);
3900 }
3901
3902 for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
3903 {
Adam Sawicki80927152018-09-07 17:27:23 +02003904 for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
Adam Sawicki0a607132018-08-24 11:18:41 +02003905 {
Adam Sawicki80927152018-09-07 17:27:23 +02003906 uint32_t algorithm = 0;
3907 switch(algorithmIndex)
3908 {
3909 case 0:
3910 break;
3911 case 1:
3912 algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
3913 break;
3914 case 2:
3915 algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
3916 break;
3917 default:
3918 assert(0);
3919 }
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003920
Adam Sawicki80927152018-09-07 17:27:23 +02003921 uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003922 for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
3923 {
3924 VmaAllocatorCreateFlags strategy = 0;
Adam Sawicki80927152018-09-07 17:27:23 +02003925 if(currAllocStrategyCount > 1)
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003926 {
3927 switch(allocStrategyIndex)
3928 {
3929 case 0: strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; break;
3930 case 1: strategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT; break;
3931 case 2: strategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT; break;
3932 default: assert(0);
3933 }
3934 }
3935
Adam Sawicki80927152018-09-07 17:27:23 +02003936 BenchmarkAlgorithmsCase(
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003937 file,
Adam Sawicki80927152018-09-07 17:27:23 +02003938 algorithm,
Adam Sawicki1f7f8af2018-10-03 17:37:55 +02003939 (emptyIndex == 0), // empty
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02003940 strategy,
3941 freeOrder); // freeOrder
3942 }
Adam Sawicki0a607132018-08-24 11:18:41 +02003943 }
3944 }
3945 }
3946}
3947
Adam Sawickib8333fb2018-03-13 16:15:53 +01003948static void TestPool_SameSize()
3949{
3950 const VkDeviceSize BUF_SIZE = 1024 * 1024;
3951 const size_t BUF_COUNT = 100;
3952 VkResult res;
3953
3954 RandomNumberGenerator rand{123};
3955
3956 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3957 bufferInfo.size = BUF_SIZE;
3958 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3959
3960 uint32_t memoryTypeBits = UINT32_MAX;
3961 {
3962 VkBuffer dummyBuffer;
Adam Sawicki1f84f622019-07-02 13:40:01 +02003963 res = vkCreateBuffer(g_hDevice, &bufferInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003964 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003965
3966 VkMemoryRequirements memReq;
3967 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
3968 memoryTypeBits = memReq.memoryTypeBits;
3969
Adam Sawicki1f84f622019-07-02 13:40:01 +02003970 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003971 }
3972
3973 VmaAllocationCreateInfo poolAllocInfo = {};
3974 poolAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
3975 uint32_t memTypeIndex;
3976 res = vmaFindMemoryTypeIndex(
3977 g_hAllocator,
3978 memoryTypeBits,
3979 &poolAllocInfo,
3980 &memTypeIndex);
3981
3982 VmaPoolCreateInfo poolCreateInfo = {};
3983 poolCreateInfo.memoryTypeIndex = memTypeIndex;
3984 poolCreateInfo.blockSize = BUF_SIZE * BUF_COUNT / 4;
3985 poolCreateInfo.minBlockCount = 1;
3986 poolCreateInfo.maxBlockCount = 4;
3987 poolCreateInfo.frameInUseCount = 0;
3988
3989 VmaPool pool;
3990 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02003991 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01003992
Adam Sawickia020fb82019-11-02 14:43:06 +01003993 // Test pool name
3994 {
3995 static const char* const POOL_NAME = "Pool name";
3996 vmaSetPoolName(g_hAllocator, pool, POOL_NAME);
3997
3998 const char* fetchedPoolName = nullptr;
3999 vmaGetPoolName(g_hAllocator, pool, &fetchedPoolName);
4000 TEST(strcmp(fetchedPoolName, POOL_NAME) == 0);
4001
Adam Sawickia020fb82019-11-02 14:43:06 +01004002 vmaSetPoolName(g_hAllocator, pool, nullptr);
4003 }
4004
Adam Sawickib8333fb2018-03-13 16:15:53 +01004005 vmaSetCurrentFrameIndex(g_hAllocator, 1);
4006
4007 VmaAllocationCreateInfo allocInfo = {};
4008 allocInfo.pool = pool;
4009 allocInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4010 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4011
4012 struct BufItem
4013 {
4014 VkBuffer Buf;
4015 VmaAllocation Alloc;
4016 };
4017 std::vector<BufItem> items;
4018
4019 // Fill entire pool.
4020 for(size_t i = 0; i < BUF_COUNT; ++i)
4021 {
4022 BufItem item;
4023 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004024 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004025 items.push_back(item);
4026 }
4027
4028 // Make sure that another allocation would fail.
4029 {
4030 BufItem item;
4031 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004032 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004033 }
4034
4035 // Validate that no buffer is lost. Also check that they are not mapped.
4036 for(size_t i = 0; i < items.size(); ++i)
4037 {
4038 VmaAllocationInfo allocInfo;
4039 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004040 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
4041 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004042 }
4043
4044 // Free some percent of random items.
4045 {
4046 const size_t PERCENT_TO_FREE = 10;
4047 size_t itemsToFree = items.size() * PERCENT_TO_FREE / 100;
4048 for(size_t i = 0; i < itemsToFree; ++i)
4049 {
4050 size_t index = (size_t)rand.Generate() % items.size();
4051 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4052 items.erase(items.begin() + index);
4053 }
4054 }
4055
4056 // Randomly allocate and free items.
4057 {
4058 const size_t OPERATION_COUNT = BUF_COUNT;
4059 for(size_t i = 0; i < OPERATION_COUNT; ++i)
4060 {
4061 bool allocate = rand.Generate() % 2 != 0;
4062 if(allocate)
4063 {
4064 if(items.size() < BUF_COUNT)
4065 {
4066 BufItem item;
4067 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004068 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004069 items.push_back(item);
4070 }
4071 }
4072 else // Free
4073 {
4074 if(!items.empty())
4075 {
4076 size_t index = (size_t)rand.Generate() % items.size();
4077 vmaDestroyBuffer(g_hAllocator, items[index].Buf, items[index].Alloc);
4078 items.erase(items.begin() + index);
4079 }
4080 }
4081 }
4082 }
4083
4084 // Allocate up to maximum.
4085 while(items.size() < BUF_COUNT)
4086 {
4087 BufItem item;
4088 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004089 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004090 items.push_back(item);
4091 }
4092
4093 // Validate that no buffer is lost.
4094 for(size_t i = 0; i < items.size(); ++i)
4095 {
4096 VmaAllocationInfo allocInfo;
4097 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004098 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004099 }
4100
4101 // Next frame.
4102 vmaSetCurrentFrameIndex(g_hAllocator, 2);
4103
4104 // Allocate another BUF_COUNT buffers.
4105 for(size_t i = 0; i < BUF_COUNT; ++i)
4106 {
4107 BufItem item;
4108 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004109 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004110 items.push_back(item);
4111 }
4112
4113 // Make sure the first BUF_COUNT is lost. Delete them.
4114 for(size_t i = 0; i < BUF_COUNT; ++i)
4115 {
4116 VmaAllocationInfo allocInfo;
4117 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004118 TEST(allocInfo.deviceMemory == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004119 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4120 }
4121 items.erase(items.begin(), items.begin() + BUF_COUNT);
4122
4123 // Validate that no buffer is lost.
4124 for(size_t i = 0; i < items.size(); ++i)
4125 {
4126 VmaAllocationInfo allocInfo;
4127 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004128 TEST(allocInfo.deviceMemory != VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004129 }
4130
4131 // Free one item.
4132 vmaDestroyBuffer(g_hAllocator, items.back().Buf, items.back().Alloc);
4133 items.pop_back();
4134
4135 // Validate statistics.
4136 {
4137 VmaPoolStats poolStats = {};
4138 vmaGetPoolStats(g_hAllocator, pool, &poolStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004139 TEST(poolStats.allocationCount == items.size());
4140 TEST(poolStats.size = BUF_COUNT * BUF_SIZE);
4141 TEST(poolStats.unusedRangeCount == 1);
4142 TEST(poolStats.unusedRangeSizeMax == BUF_SIZE);
4143 TEST(poolStats.unusedSize == BUF_SIZE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004144 }
4145
4146 // Free all remaining items.
4147 for(size_t i = items.size(); i--; )
4148 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4149 items.clear();
4150
4151 // Allocate maximum items again.
4152 for(size_t i = 0; i < BUF_COUNT; ++i)
4153 {
4154 BufItem item;
4155 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004156 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004157 items.push_back(item);
4158 }
4159
4160 // Delete every other item.
4161 for(size_t i = 0; i < BUF_COUNT / 2; ++i)
4162 {
4163 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4164 items.erase(items.begin() + i);
4165 }
4166
4167 // Defragment!
4168 {
4169 std::vector<VmaAllocation> allocationsToDefragment(items.size());
4170 for(size_t i = 0; i < items.size(); ++i)
4171 allocationsToDefragment[i] = items[i].Alloc;
4172
4173 VmaDefragmentationStats defragmentationStats;
4174 res = vmaDefragment(g_hAllocator, allocationsToDefragment.data(), items.size(), nullptr, nullptr, &defragmentationStats);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004175 TEST(res == VK_SUCCESS);
4176 TEST(defragmentationStats.deviceMemoryBlocksFreed == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004177 }
4178
4179 // Free all remaining items.
4180 for(size_t i = items.size(); i--; )
4181 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4182 items.clear();
4183
4184 ////////////////////////////////////////////////////////////////////////////////
4185 // Test for vmaMakePoolAllocationsLost
4186
4187 // Allocate 4 buffers on frame 10.
4188 vmaSetCurrentFrameIndex(g_hAllocator, 10);
4189 for(size_t i = 0; i < 4; ++i)
4190 {
4191 BufItem item;
4192 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocInfo, &item.Buf, &item.Alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004193 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004194 items.push_back(item);
4195 }
4196
4197 // Touch first 2 of them on frame 11.
4198 vmaSetCurrentFrameIndex(g_hAllocator, 11);
4199 for(size_t i = 0; i < 2; ++i)
4200 {
4201 VmaAllocationInfo allocInfo;
4202 vmaGetAllocationInfo(g_hAllocator, items[i].Alloc, &allocInfo);
4203 }
4204
4205 // vmaMakePoolAllocationsLost. Only remaining 2 should be lost.
4206 size_t lostCount = 0xDEADC0DE;
4207 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004208 TEST(lostCount == 2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004209
4210 // Make another call. Now 0 should be lost.
4211 vmaMakePoolAllocationsLost(g_hAllocator, pool, &lostCount);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004212 TEST(lostCount == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004213
4214 // Make another call, with null count. Should not crash.
4215 vmaMakePoolAllocationsLost(g_hAllocator, pool, nullptr);
4216
4217 // END: Free all remaining items.
4218 for(size_t i = items.size(); i--; )
4219 vmaDestroyBuffer(g_hAllocator, items[i].Buf, items[i].Alloc);
4220
4221 items.clear();
4222
Adam Sawickid2924172018-06-11 12:48:46 +02004223 ////////////////////////////////////////////////////////////////////////////////
4224 // Test for allocation too large for pool
4225
4226 {
4227 VmaAllocationCreateInfo allocCreateInfo = {};
4228 allocCreateInfo.pool = pool;
4229
4230 VkMemoryRequirements memReq;
4231 memReq.memoryTypeBits = UINT32_MAX;
4232 memReq.alignment = 1;
4233 memReq.size = poolCreateInfo.blockSize + 4;
4234
4235 VmaAllocation alloc = nullptr;
4236 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004237 TEST(res == VK_ERROR_OUT_OF_DEVICE_MEMORY && alloc == nullptr);
Adam Sawickid2924172018-06-11 12:48:46 +02004238 }
4239
Adam Sawickib8333fb2018-03-13 16:15:53 +01004240 vmaDestroyPool(g_hAllocator, pool);
4241}
4242
Adam Sawickie44c6262018-06-15 14:30:39 +02004243static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)
4244{
4245 const uint8_t* pBytes = (const uint8_t*)pMemory;
4246 for(size_t i = 0; i < size; ++i)
4247 {
4248 if(pBytes[i] != pattern)
4249 {
4250 return false;
4251 }
4252 }
4253 return true;
4254}
4255
4256static void TestAllocationsInitialization()
4257{
4258 VkResult res;
4259
4260 const size_t BUF_SIZE = 1024;
4261
4262 // Create pool.
4263
4264 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4265 bufInfo.size = BUF_SIZE;
4266 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4267
4268 VmaAllocationCreateInfo dummyBufAllocCreateInfo = {};
4269 dummyBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
4270
4271 VmaPoolCreateInfo poolCreateInfo = {};
4272 poolCreateInfo.blockSize = BUF_SIZE * 10;
4273 poolCreateInfo.minBlockCount = 1; // To keep memory alive while pool exists.
4274 poolCreateInfo.maxBlockCount = 1;
4275 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufInfo, &dummyBufAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004276 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004277
4278 VmaAllocationCreateInfo bufAllocCreateInfo = {};
4279 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &bufAllocCreateInfo.pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004280 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004281
4282 // Create one persistently mapped buffer to keep memory of this block mapped,
4283 // so that pointer to mapped data will remain (more or less...) valid even
4284 // after destruction of other allocations.
4285
4286 bufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4287 VkBuffer firstBuf;
4288 VmaAllocation firstAlloc;
4289 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &firstBuf, &firstAlloc, nullptr);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004290 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004291
4292 // Test buffers.
4293
4294 for(uint32_t i = 0; i < 2; ++i)
4295 {
4296 const bool persistentlyMapped = i == 0;
4297 bufAllocCreateInfo.flags = persistentlyMapped ? VMA_ALLOCATION_CREATE_MAPPED_BIT : 0;
4298 VkBuffer buf;
4299 VmaAllocation alloc;
4300 VmaAllocationInfo allocInfo;
4301 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &bufAllocCreateInfo, &buf, &alloc, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004302 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004303
4304 void* pMappedData;
4305 if(!persistentlyMapped)
4306 {
4307 res = vmaMapMemory(g_hAllocator, alloc, &pMappedData);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004308 TEST(res == VK_SUCCESS);
Adam Sawickie44c6262018-06-15 14:30:39 +02004309 }
4310 else
4311 {
4312 pMappedData = allocInfo.pMappedData;
4313 }
4314
4315 // Validate initialized content
4316 bool valid = ValidatePattern(pMappedData, BUF_SIZE, 0xDC);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004317 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004318
4319 if(!persistentlyMapped)
4320 {
4321 vmaUnmapMemory(g_hAllocator, alloc);
4322 }
4323
4324 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4325
4326 // Validate freed content
4327 valid = ValidatePattern(pMappedData, BUF_SIZE, 0xEF);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004328 TEST(valid);
Adam Sawickie44c6262018-06-15 14:30:39 +02004329 }
4330
4331 vmaDestroyBuffer(g_hAllocator, firstBuf, firstAlloc);
4332 vmaDestroyPool(g_hAllocator, bufAllocCreateInfo.pool);
4333}
4334
Adam Sawickib8333fb2018-03-13 16:15:53 +01004335static void TestPool_Benchmark(
4336 PoolTestResult& outResult,
4337 const PoolTestConfig& config)
4338{
Adam Sawickib8d34d52018-10-03 17:41:20 +02004339 TEST(config.ThreadCount > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004340
4341 RandomNumberGenerator mainRand{config.RandSeed};
4342
4343 uint32_t allocationSizeProbabilitySum = std::accumulate(
4344 config.AllocationSizes.begin(),
4345 config.AllocationSizes.end(),
4346 0u,
4347 [](uint32_t sum, const AllocationSize& allocSize) {
4348 return sum + allocSize.Probability;
4349 });
4350
Adam Sawicki41b41112021-03-02 15:11:18 +01004351 VkBufferCreateInfo bufferTemplateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4352 bufferTemplateInfo.size = 256; // Whatever.
4353 bufferTemplateInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004354
Adam Sawicki41b41112021-03-02 15:11:18 +01004355 VkImageCreateInfo imageTemplateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4356 imageTemplateInfo.imageType = VK_IMAGE_TYPE_2D;
4357 imageTemplateInfo.extent.width = 256; // Whatever.
4358 imageTemplateInfo.extent.height = 256; // Whatever.
4359 imageTemplateInfo.extent.depth = 1;
4360 imageTemplateInfo.mipLevels = 1;
4361 imageTemplateInfo.arrayLayers = 1;
4362 imageTemplateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4363 imageTemplateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4364 imageTemplateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4365 imageTemplateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4366 imageTemplateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004367
4368 uint32_t bufferMemoryTypeBits = UINT32_MAX;
4369 {
4370 VkBuffer dummyBuffer;
Adam Sawicki41b41112021-03-02 15:11:18 +01004371 VkResult res = vkCreateBuffer(g_hDevice, &bufferTemplateInfo, g_Allocs, &dummyBuffer);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004372 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004373
4374 VkMemoryRequirements memReq;
4375 vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
4376 bufferMemoryTypeBits = memReq.memoryTypeBits;
4377
Adam Sawicki1f84f622019-07-02 13:40:01 +02004378 vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004379 }
4380
4381 uint32_t imageMemoryTypeBits = UINT32_MAX;
4382 {
4383 VkImage dummyImage;
Adam Sawicki41b41112021-03-02 15:11:18 +01004384 VkResult res = vkCreateImage(g_hDevice, &imageTemplateInfo, g_Allocs, &dummyImage);
Adam Sawickib8d34d52018-10-03 17:41:20 +02004385 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004386
4387 VkMemoryRequirements memReq;
4388 vkGetImageMemoryRequirements(g_hDevice, dummyImage, &memReq);
4389 imageMemoryTypeBits = memReq.memoryTypeBits;
4390
Adam Sawicki1f84f622019-07-02 13:40:01 +02004391 vkDestroyImage(g_hDevice, dummyImage, g_Allocs);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004392 }
4393
4394 uint32_t memoryTypeBits = 0;
4395 if(config.UsesBuffers() && config.UsesImages())
4396 {
4397 memoryTypeBits = bufferMemoryTypeBits & imageMemoryTypeBits;
4398 if(memoryTypeBits == 0)
4399 {
4400 PrintWarning(L"Cannot test buffers + images in the same memory pool on this GPU.");
4401 return;
4402 }
4403 }
4404 else if(config.UsesBuffers())
4405 memoryTypeBits = bufferMemoryTypeBits;
4406 else if(config.UsesImages())
4407 memoryTypeBits = imageMemoryTypeBits;
4408 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02004409 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004410
4411 VmaPoolCreateInfo poolCreateInfo = {};
Adam Sawickib8333fb2018-03-13 16:15:53 +01004412 poolCreateInfo.minBlockCount = 1;
4413 poolCreateInfo.maxBlockCount = 1;
4414 poolCreateInfo.blockSize = config.PoolSize;
4415 poolCreateInfo.frameInUseCount = 1;
4416
Adam Sawicki7e56c482021-02-23 15:27:24 +01004417 const VkPhysicalDeviceMemoryProperties* memProps = nullptr;
4418 vmaGetMemoryProperties(g_hAllocator, &memProps);
4419
Adam Sawicki26eaa3b2021-02-18 15:53:18 +01004420 VmaPool pool = VK_NULL_HANDLE;
4421 VkResult res;
4422 // Loop over memory types because we sometimes allocate a big block here,
4423 // while the most eligible DEVICE_LOCAL heap may be only 256 MB on some GPUs.
4424 while(memoryTypeBits)
4425 {
4426 VmaAllocationCreateInfo dummyAllocCreateInfo = {};
4427 dummyAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4428 vmaFindMemoryTypeIndex(g_hAllocator, memoryTypeBits, &dummyAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004429
Adam Sawicki7e56c482021-02-23 15:27:24 +01004430 const uint32_t heapIndex = memProps->memoryTypes[poolCreateInfo.memoryTypeIndex].heapIndex;
4431 // Protection against validation layer error when trying to allocate a block larger than entire heap size,
4432 // which may be only 256 MB on some platforms.
4433 if(poolCreateInfo.blockSize * poolCreateInfo.minBlockCount < memProps->memoryHeaps[heapIndex].size)
4434 {
4435 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
4436 if(res == VK_SUCCESS)
4437 break;
4438 }
Adam Sawicki26eaa3b2021-02-18 15:53:18 +01004439 memoryTypeBits &= ~(1u << poolCreateInfo.memoryTypeIndex);
4440 }
4441 TEST(pool);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004442
4443 // Start time measurement - after creating pool and initializing data structures.
4444 time_point timeBeg = std::chrono::high_resolution_clock::now();
4445
4446 ////////////////////////////////////////////////////////////////////////////////
4447 // ThreadProc
Adam Sawicki41b41112021-03-02 15:11:18 +01004448 auto ThreadProc = [&config, allocationSizeProbabilitySum, pool](
Adam Sawickib8333fb2018-03-13 16:15:53 +01004449 PoolTestThreadResult* outThreadResult,
4450 uint32_t randSeed,
4451 HANDLE frameStartEvent,
4452 HANDLE frameEndEvent) -> void
4453 {
4454 RandomNumberGenerator threadRand{randSeed};
Adam Sawicki41b41112021-03-02 15:11:18 +01004455 VkResult res = VK_SUCCESS;
4456
4457 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4458 bufferInfo.size = 256; // Whatever.
4459 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4460
4461 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4462 imageInfo.imageType = VK_IMAGE_TYPE_2D;
4463 imageInfo.extent.width = 256; // Whatever.
4464 imageInfo.extent.height = 256; // Whatever.
4465 imageInfo.extent.depth = 1;
4466 imageInfo.mipLevels = 1;
4467 imageInfo.arrayLayers = 1;
4468 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4469 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // LINEAR if CPU memory.
4470 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4471 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // TRANSFER_SRC if CPU memory.
4472 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004473
4474 outThreadResult->AllocationTimeMin = duration::max();
4475 outThreadResult->AllocationTimeSum = duration::zero();
4476 outThreadResult->AllocationTimeMax = duration::min();
4477 outThreadResult->DeallocationTimeMin = duration::max();
4478 outThreadResult->DeallocationTimeSum = duration::zero();
4479 outThreadResult->DeallocationTimeMax = duration::min();
4480 outThreadResult->AllocationCount = 0;
4481 outThreadResult->DeallocationCount = 0;
4482 outThreadResult->LostAllocationCount = 0;
4483 outThreadResult->LostAllocationTotalSize = 0;
4484 outThreadResult->FailedAllocationCount = 0;
4485 outThreadResult->FailedAllocationTotalSize = 0;
4486
4487 struct Item
4488 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004489 VkDeviceSize BufferSize = 0;
4490 VkExtent2D ImageSize = { 0, 0 };
4491 VkBuffer Buf = VK_NULL_HANDLE;
4492 VkImage Image = VK_NULL_HANDLE;
4493 VmaAllocation Alloc = VK_NULL_HANDLE;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004494
Adam Sawicki41b41112021-03-02 15:11:18 +01004495 Item() { }
4496 Item(Item&& src) :
4497 BufferSize(src.BufferSize), ImageSize(src.ImageSize), Buf(src.Buf), Image(src.Image), Alloc(src.Alloc)
4498 {
4499 src.BufferSize = 0;
4500 src.ImageSize = {0, 0};
4501 src.Buf = VK_NULL_HANDLE;
4502 src.Image = VK_NULL_HANDLE;
4503 src.Alloc = VK_NULL_HANDLE;
4504 }
4505 Item(const Item& src) = delete;
4506 ~Item()
4507 {
4508 DestroyResources();
4509 }
4510 Item& operator=(Item&& src)
4511 {
4512 if(&src != this)
4513 {
4514 DestroyResources();
4515 BufferSize = src.BufferSize; ImageSize = src.ImageSize;
4516 Buf = src.Buf; Image = src.Image; Alloc = src.Alloc;
4517 src.BufferSize = 0;
4518 src.ImageSize = {0, 0};
4519 src.Buf = VK_NULL_HANDLE;
4520 src.Image = VK_NULL_HANDLE;
4521 src.Alloc = VK_NULL_HANDLE;
4522 }
4523 return *this;
4524 }
4525 Item& operator=(const Item& src) = delete;
4526 void DestroyResources()
4527 {
4528 if(Buf)
4529 {
4530 assert(Image == VK_NULL_HANDLE);
4531 vmaDestroyBuffer(g_hAllocator, Buf, Alloc);
4532 Buf = VK_NULL_HANDLE;
4533 }
4534 else
4535 {
4536 vmaDestroyImage(g_hAllocator, Image, Alloc);
4537 Image = VK_NULL_HANDLE;
4538 }
4539 Alloc = VK_NULL_HANDLE;
4540 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004541 VkDeviceSize CalcSizeBytes() const
4542 {
4543 return BufferSize +
Adam Sawicki41b41112021-03-02 15:11:18 +01004544 4ull * ImageSize.width * ImageSize.height;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004545 }
4546 };
4547 std::vector<Item> unusedItems, usedItems;
4548
4549 const size_t threadTotalItemCount = config.TotalItemCount / config.ThreadCount;
4550
4551 // Create all items - all unused, not yet allocated.
4552 for(size_t i = 0; i < threadTotalItemCount; ++i)
4553 {
4554 Item item = {};
4555
4556 uint32_t allocSizeIndex = 0;
4557 uint32_t r = threadRand.Generate() % allocationSizeProbabilitySum;
4558 while(r >= config.AllocationSizes[allocSizeIndex].Probability)
4559 r -= config.AllocationSizes[allocSizeIndex++].Probability;
4560
4561 const AllocationSize& allocSize = config.AllocationSizes[allocSizeIndex];
4562 if(allocSize.BufferSizeMax > 0)
4563 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004564 TEST(allocSize.BufferSizeMin > 0);
4565 TEST(allocSize.ImageSizeMin == 0 && allocSize.ImageSizeMax == 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004566 if(allocSize.BufferSizeMax == allocSize.BufferSizeMin)
4567 item.BufferSize = allocSize.BufferSizeMin;
4568 else
4569 {
4570 item.BufferSize = allocSize.BufferSizeMin + threadRand.Generate() % (allocSize.BufferSizeMax - allocSize.BufferSizeMin);
4571 item.BufferSize = item.BufferSize / 16 * 16;
4572 }
4573 }
4574 else
4575 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004576 TEST(allocSize.ImageSizeMin > 0 && allocSize.ImageSizeMax > 0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004577 if(allocSize.ImageSizeMax == allocSize.ImageSizeMin)
4578 item.ImageSize.width = item.ImageSize.height = allocSize.ImageSizeMax;
4579 else
4580 {
4581 item.ImageSize.width = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4582 item.ImageSize.height = allocSize.ImageSizeMin + threadRand.Generate() % (allocSize.ImageSizeMax - allocSize.ImageSizeMin);
4583 }
4584 }
4585
Adam Sawicki41b41112021-03-02 15:11:18 +01004586 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004587 }
4588
4589 auto Allocate = [&](Item& item) -> VkResult
4590 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004591 assert(item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE && item.Alloc == VK_NULL_HANDLE);
4592
Adam Sawickib8333fb2018-03-13 16:15:53 +01004593 VmaAllocationCreateInfo allocCreateInfo = {};
4594 allocCreateInfo.pool = pool;
4595 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
4596 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
4597
4598 if(item.BufferSize)
4599 {
4600 bufferInfo.size = item.BufferSize;
Adam Sawicki41b41112021-03-02 15:11:18 +01004601 VkResult res = VK_SUCCESS;
4602 {
4603 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4604 res = vmaCreateBuffer(g_hAllocator, &bufferInfo, &allocCreateInfo, &item.Buf, &item.Alloc, nullptr);
4605 }
4606 if(res == VK_SUCCESS)
4607 SetDebugUtilsObjectName(VK_OBJECT_TYPE_BUFFER, (uint64_t)item.Buf, "TestPool_Benchmark_Buffer");
4608 return res;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004609 }
4610 else
4611 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02004612 TEST(item.ImageSize.width && item.ImageSize.height);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004613
4614 imageInfo.extent.width = item.ImageSize.width;
4615 imageInfo.extent.height = item.ImageSize.height;
Adam Sawicki41b41112021-03-02 15:11:18 +01004616 VkResult res = VK_SUCCESS;
4617 {
4618 PoolAllocationTimeRegisterObj timeRegisterObj(*outThreadResult);
4619 res = vmaCreateImage(g_hAllocator, &imageInfo, &allocCreateInfo, &item.Image, &item.Alloc, nullptr);
4620 }
4621 if(res == VK_SUCCESS)
4622 SetDebugUtilsObjectName(VK_OBJECT_TYPE_IMAGE, (uint64_t)item.Image, "TestPool_Benchmark_Image");
4623 return res;
Adam Sawickib8333fb2018-03-13 16:15:53 +01004624 }
4625 };
4626
4627 ////////////////////////////////////////////////////////////////////////////////
4628 // Frames
4629 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4630 {
4631 WaitForSingleObject(frameStartEvent, INFINITE);
4632
4633 // Always make some percent of used bufs unused, to choose different used ones.
4634 const size_t bufsToMakeUnused = usedItems.size() * config.ItemsToMakeUnusedPercent / 100;
4635 for(size_t i = 0; i < bufsToMakeUnused; ++i)
4636 {
4637 size_t index = threadRand.Generate() % usedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004638 auto it = usedItems.begin() + index;
4639 Item item = std::move(*it);
4640 usedItems.erase(it);
4641 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004642 }
4643
4644 // Determine which bufs we want to use in this frame.
4645 const size_t usedBufCount = (threadRand.Generate() % (config.UsedItemCountMax - config.UsedItemCountMin) + config.UsedItemCountMin)
4646 / config.ThreadCount;
Adam Sawickib8d34d52018-10-03 17:41:20 +02004647 TEST(usedBufCount < usedItems.size() + unusedItems.size());
Adam Sawickib8333fb2018-03-13 16:15:53 +01004648 // Move some used to unused.
4649 while(usedBufCount < usedItems.size())
4650 {
4651 size_t index = threadRand.Generate() % usedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004652 auto it = usedItems.begin() + index;
4653 Item item = std::move(*it);
4654 usedItems.erase(it);
4655 unusedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004656 }
4657 // Move some unused to used.
4658 while(usedBufCount > usedItems.size())
4659 {
4660 size_t index = threadRand.Generate() % unusedItems.size();
Adam Sawicki41b41112021-03-02 15:11:18 +01004661 auto it = unusedItems.begin() + index;
4662 Item item = std::move(*it);
4663 unusedItems.erase(it);
4664 usedItems.push_back(std::move(item));
Adam Sawickib8333fb2018-03-13 16:15:53 +01004665 }
4666
4667 uint32_t touchExistingCount = 0;
4668 uint32_t touchLostCount = 0;
4669 uint32_t createSucceededCount = 0;
4670 uint32_t createFailedCount = 0;
4671
4672 // Touch all used bufs. If not created or lost, allocate.
4673 for(size_t i = 0; i < usedItems.size(); ++i)
4674 {
4675 Item& item = usedItems[i];
4676 // Not yet created.
4677 if(item.Alloc == VK_NULL_HANDLE)
4678 {
4679 res = Allocate(item);
4680 ++outThreadResult->AllocationCount;
4681 if(res != VK_SUCCESS)
4682 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004683 assert(item.Alloc == VK_NULL_HANDLE && item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004684 ++outThreadResult->FailedAllocationCount;
4685 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4686 ++createFailedCount;
4687 }
4688 else
4689 ++createSucceededCount;
4690 }
4691 else
4692 {
4693 // Touch.
4694 VmaAllocationInfo allocInfo;
4695 vmaGetAllocationInfo(g_hAllocator, item.Alloc, &allocInfo);
4696 // Lost.
4697 if(allocInfo.deviceMemory == VK_NULL_HANDLE)
4698 {
4699 ++touchLostCount;
4700
4701 // Destroy.
4702 {
4703 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004704 item.DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004705 ++outThreadResult->DeallocationCount;
4706 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01004707
4708 ++outThreadResult->LostAllocationCount;
4709 outThreadResult->LostAllocationTotalSize += item.CalcSizeBytes();
4710
4711 // Recreate.
4712 res = Allocate(item);
4713 ++outThreadResult->AllocationCount;
4714 // Creation failed.
4715 if(res != VK_SUCCESS)
4716 {
Adam Sawicki41b41112021-03-02 15:11:18 +01004717 TEST(item.Alloc == VK_NULL_HANDLE && item.Buf == VK_NULL_HANDLE && item.Image == VK_NULL_HANDLE);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004718 ++outThreadResult->FailedAllocationCount;
4719 outThreadResult->FailedAllocationTotalSize += item.CalcSizeBytes();
4720 ++createFailedCount;
4721 }
4722 else
4723 ++createSucceededCount;
4724 }
4725 else
4726 ++touchExistingCount;
4727 }
4728 }
4729
4730 /*
4731 printf("Thread %u frame %u: Touch existing %u lost %u, create succeeded %u failed %u\n",
4732 randSeed, frameIndex,
4733 touchExistingCount, touchLostCount,
4734 createSucceededCount, createFailedCount);
4735 */
4736
4737 SetEvent(frameEndEvent);
4738 }
4739
4740 // Free all remaining items.
4741 for(size_t i = usedItems.size(); i--; )
4742 {
4743 PoolDeallocationTimeRegisterObj timeRegisterObj(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004744 usedItems[i].DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004745 ++outThreadResult->DeallocationCount;
4746 }
4747 for(size_t i = unusedItems.size(); i--; )
4748 {
4749 PoolDeallocationTimeRegisterObj timeRegisterOb(*outThreadResult);
Adam Sawicki41b41112021-03-02 15:11:18 +01004750 unusedItems[i].DestroyResources();
Adam Sawickib8333fb2018-03-13 16:15:53 +01004751 ++outThreadResult->DeallocationCount;
4752 }
4753 };
4754
4755 // Launch threads.
4756 uint32_t threadRandSeed = mainRand.Generate();
4757 std::vector<HANDLE> frameStartEvents{config.ThreadCount};
4758 std::vector<HANDLE> frameEndEvents{config.ThreadCount};
4759 std::vector<std::thread> bkgThreads;
4760 std::vector<PoolTestThreadResult> threadResults{config.ThreadCount};
4761 for(uint32_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4762 {
4763 frameStartEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4764 frameEndEvents[threadIndex] = CreateEvent(NULL, FALSE, FALSE, NULL);
4765 bkgThreads.emplace_back(std::bind(
4766 ThreadProc,
4767 &threadResults[threadIndex],
4768 threadRandSeed + threadIndex,
4769 frameStartEvents[threadIndex],
4770 frameEndEvents[threadIndex]));
4771 }
4772
4773 // Execute frames.
Adam Sawickib8d34d52018-10-03 17:41:20 +02004774 TEST(config.ThreadCount <= MAXIMUM_WAIT_OBJECTS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01004775 for(uint32_t frameIndex = 0; frameIndex < config.FrameCount; ++frameIndex)
4776 {
4777 vmaSetCurrentFrameIndex(g_hAllocator, frameIndex);
4778 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4779 SetEvent(frameStartEvents[threadIndex]);
4780 WaitForMultipleObjects(config.ThreadCount, &frameEndEvents[0], TRUE, INFINITE);
4781 }
4782
4783 // Wait for threads finished
4784 for(size_t i = 0; i < bkgThreads.size(); ++i)
4785 {
4786 bkgThreads[i].join();
4787 CloseHandle(frameEndEvents[i]);
4788 CloseHandle(frameStartEvents[i]);
4789 }
4790 bkgThreads.clear();
4791
4792 // Finish time measurement - before destroying pool.
4793 outResult.TotalTime = std::chrono::high_resolution_clock::now() - timeBeg;
4794
4795 vmaDestroyPool(g_hAllocator, pool);
4796
4797 outResult.AllocationTimeMin = duration::max();
4798 outResult.AllocationTimeAvg = duration::zero();
4799 outResult.AllocationTimeMax = duration::min();
4800 outResult.DeallocationTimeMin = duration::max();
4801 outResult.DeallocationTimeAvg = duration::zero();
4802 outResult.DeallocationTimeMax = duration::min();
4803 outResult.LostAllocationCount = 0;
4804 outResult.LostAllocationTotalSize = 0;
4805 outResult.FailedAllocationCount = 0;
4806 outResult.FailedAllocationTotalSize = 0;
4807 size_t allocationCount = 0;
4808 size_t deallocationCount = 0;
4809 for(size_t threadIndex = 0; threadIndex < config.ThreadCount; ++threadIndex)
4810 {
4811 const PoolTestThreadResult& threadResult = threadResults[threadIndex];
4812 outResult.AllocationTimeMin = std::min(outResult.AllocationTimeMin, threadResult.AllocationTimeMin);
4813 outResult.AllocationTimeMax = std::max(outResult.AllocationTimeMax, threadResult.AllocationTimeMax);
4814 outResult.AllocationTimeAvg += threadResult.AllocationTimeSum;
4815 outResult.DeallocationTimeMin = std::min(outResult.DeallocationTimeMin, threadResult.DeallocationTimeMin);
4816 outResult.DeallocationTimeMax = std::max(outResult.DeallocationTimeMax, threadResult.DeallocationTimeMax);
4817 outResult.DeallocationTimeAvg += threadResult.DeallocationTimeSum;
4818 allocationCount += threadResult.AllocationCount;
4819 deallocationCount += threadResult.DeallocationCount;
4820 outResult.FailedAllocationCount += threadResult.FailedAllocationCount;
4821 outResult.FailedAllocationTotalSize += threadResult.FailedAllocationTotalSize;
4822 outResult.LostAllocationCount += threadResult.LostAllocationCount;
4823 outResult.LostAllocationTotalSize += threadResult.LostAllocationTotalSize;
4824 }
4825 if(allocationCount)
4826 outResult.AllocationTimeAvg /= allocationCount;
4827 if(deallocationCount)
4828 outResult.DeallocationTimeAvg /= deallocationCount;
4829}
4830
4831static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, size_t size2)
4832{
4833 if(ptr1 < ptr2)
4834 return ptr1 + size1 > ptr2;
4835 else if(ptr2 < ptr1)
4836 return ptr2 + size2 > ptr1;
4837 else
4838 return true;
4839}
4840
Adam Sawickiefa88c42019-11-18 16:33:56 +01004841static void TestMemoryUsage()
4842{
4843 wprintf(L"Testing memory usage:\n");
4844
Adam Sawicki69185552019-11-18 17:03:34 +01004845 static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004846 for(uint32_t usage = 0; usage <= lastUsage; ++usage)
4847 {
4848 switch(usage)
4849 {
4850 case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
4851 case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
4852 case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
4853 case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
4854 case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
4855 case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
Adam Sawicki69185552019-11-18 17:03:34 +01004856 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: printf(" VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:\n"); break;
Adam Sawickiefa88c42019-11-18 16:33:56 +01004857 default: assert(0);
4858 }
4859
4860 auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
4861 {
4862 if(res == VK_SUCCESS)
4863 printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
4864 else
4865 printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
4866 };
4867
4868 // 1: Buffer for copy
4869 {
4870 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4871 bufCreateInfo.size = 65536;
4872 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
4873
4874 VkBuffer buf = VK_NULL_HANDLE;
4875 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4876 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4877
4878 VkMemoryRequirements memReq = {};
4879 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4880
4881 VmaAllocationCreateInfo allocCreateInfo = {};
4882 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4883 VmaAllocation alloc = VK_NULL_HANDLE;
4884 VmaAllocationInfo allocInfo = {};
4885 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4886 if(res == VK_SUCCESS)
4887 {
4888 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4889 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4890 TEST(res == VK_SUCCESS);
4891 }
4892 printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4893 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4894 }
4895
4896 // 2: Vertex buffer
4897 {
4898 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
4899 bufCreateInfo.size = 65536;
4900 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4901
4902 VkBuffer buf = VK_NULL_HANDLE;
4903 VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
4904 TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
4905
4906 VkMemoryRequirements memReq = {};
4907 vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
4908
4909 VmaAllocationCreateInfo allocCreateInfo = {};
4910 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4911 VmaAllocation alloc = VK_NULL_HANDLE;
4912 VmaAllocationInfo allocInfo = {};
4913 res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
4914 if(res == VK_SUCCESS)
4915 {
4916 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4917 res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
4918 TEST(res == VK_SUCCESS);
4919 }
4920 printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
4921 vmaDestroyBuffer(g_hAllocator, buf, alloc);
4922 }
4923
4924 // 3: Image for copy, OPTIMAL
4925 {
4926 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4927 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4928 imgCreateInfo.extent.width = 256;
4929 imgCreateInfo.extent.height = 256;
4930 imgCreateInfo.extent.depth = 1;
4931 imgCreateInfo.mipLevels = 1;
4932 imgCreateInfo.arrayLayers = 1;
4933 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4934 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4935 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4936 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4937 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4938
4939 VkImage img = VK_NULL_HANDLE;
4940 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4941 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4942
4943 VkMemoryRequirements memReq = {};
4944 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4945
4946 VmaAllocationCreateInfo allocCreateInfo = {};
4947 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4948 VmaAllocation alloc = VK_NULL_HANDLE;
4949 VmaAllocationInfo allocInfo = {};
4950 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4951 if(res == VK_SUCCESS)
4952 {
4953 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4954 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4955 TEST(res == VK_SUCCESS);
4956 }
4957 printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
4958
4959 vmaDestroyImage(g_hAllocator, img, alloc);
4960 }
4961
4962 // 4: Image SAMPLED, OPTIMAL
4963 {
4964 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4965 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4966 imgCreateInfo.extent.width = 256;
4967 imgCreateInfo.extent.height = 256;
4968 imgCreateInfo.extent.depth = 1;
4969 imgCreateInfo.mipLevels = 1;
4970 imgCreateInfo.arrayLayers = 1;
4971 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4972 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4973 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4974 imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
4975 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4976
4977 VkImage img = VK_NULL_HANDLE;
4978 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4979 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4980
4981 VkMemoryRequirements memReq = {};
4982 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4983
4984 VmaAllocationCreateInfo allocCreateInfo = {};
4985 allocCreateInfo.usage = (VmaMemoryUsage)usage;
4986 VmaAllocation alloc = VK_NULL_HANDLE;
4987 VmaAllocationInfo allocInfo = {};
4988 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4989 if(res == VK_SUCCESS)
4990 {
4991 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4992 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4993 TEST(res == VK_SUCCESS);
4994 }
4995 printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
4996 vmaDestroyImage(g_hAllocator, img, alloc);
4997 }
4998
4999 // 5: Image COLOR_ATTACHMENT, OPTIMAL
5000 {
5001 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
5002 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
5003 imgCreateInfo.extent.width = 256;
5004 imgCreateInfo.extent.height = 256;
5005 imgCreateInfo.extent.depth = 1;
5006 imgCreateInfo.mipLevels = 1;
5007 imgCreateInfo.arrayLayers = 1;
5008 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
5009 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5010 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5011 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5012 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5013
5014 VkImage img = VK_NULL_HANDLE;
5015 VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
5016 TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
5017
5018 VkMemoryRequirements memReq = {};
5019 vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
5020
5021 VmaAllocationCreateInfo allocCreateInfo = {};
5022 allocCreateInfo.usage = (VmaMemoryUsage)usage;
5023 VmaAllocation alloc = VK_NULL_HANDLE;
5024 VmaAllocationInfo allocInfo = {};
5025 res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
5026 if(res == VK_SUCCESS)
5027 {
5028 TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
5029 res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
5030 TEST(res == VK_SUCCESS);
5031 }
5032 printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
5033 vmaDestroyImage(g_hAllocator, img, alloc);
5034 }
5035 }
5036}
5037
Adam Sawicki50882502020-02-07 16:51:31 +01005038static uint32_t FindDeviceCoherentMemoryTypeBits()
5039{
5040 VkPhysicalDeviceMemoryProperties memProps;
5041 vkGetPhysicalDeviceMemoryProperties(g_hPhysicalDevice, &memProps);
5042
5043 uint32_t memTypeBits = 0;
5044 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
5045 {
5046 if(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD)
5047 memTypeBits |= 1u << i;
5048 }
5049 return memTypeBits;
5050}
5051
5052static void TestDeviceCoherentMemory()
5053{
5054 if(!VK_AMD_device_coherent_memory_enabled)
5055 return;
5056
5057 uint32_t deviceCoherentMemoryTypeBits = FindDeviceCoherentMemoryTypeBits();
5058 // Extension is enabled, feature is enabled, and the device still doesn't support any such memory type?
5059 // OK then, so it's just fake!
5060 if(deviceCoherentMemoryTypeBits == 0)
5061 return;
5062
5063 wprintf(L"Testing device coherent memory...\n");
5064
5065 // 1. Try to allocate buffer from a memory type that is DEVICE_COHERENT.
5066
5067 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5068 bufCreateInfo.size = 0x10000;
5069 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5070
5071 VmaAllocationCreateInfo allocCreateInfo = {};
5072 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5073 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD;
5074
5075 AllocInfo alloc = {};
5076 VmaAllocationInfo allocInfo = {};
5077 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
5078
5079 // Make sure it succeeded and was really created in such memory type.
5080 TEST(res == VK_SUCCESS);
5081 TEST((1u << allocInfo.memoryType) & deviceCoherentMemoryTypeBits);
5082
5083 alloc.Destroy();
5084
5085 // 2. Try to create a pool in such memory type.
5086 {
5087 VmaPoolCreateInfo poolCreateInfo = {};
5088
5089 res = vmaFindMemoryTypeIndex(g_hAllocator, UINT32_MAX, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
5090 TEST(res == VK_SUCCESS);
5091 TEST((1u << poolCreateInfo.memoryTypeIndex) & deviceCoherentMemoryTypeBits);
5092
5093 VmaPool pool = VK_NULL_HANDLE;
5094 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5095 TEST(res == VK_SUCCESS);
5096
5097 vmaDestroyPool(g_hAllocator, pool);
5098 }
5099
5100 // 3. Try the same with a local allocator created without VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT.
5101
5102 VmaAllocatorCreateInfo allocatorCreateInfo = {};
5103 SetAllocatorCreateInfo(allocatorCreateInfo);
5104 allocatorCreateInfo.flags &= ~VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT;
5105
5106 VmaAllocator localAllocator = VK_NULL_HANDLE;
5107 res = vmaCreateAllocator(&allocatorCreateInfo, &localAllocator);
5108 TEST(res == VK_SUCCESS && localAllocator);
5109
5110 res = vmaCreateBuffer(localAllocator, &bufCreateInfo, &allocCreateInfo, &alloc.m_Buffer, &alloc.m_Allocation, &allocInfo);
5111
5112 // Make sure it failed.
5113 TEST(res != VK_SUCCESS && !alloc.m_Buffer && !alloc.m_Allocation);
5114
5115 // 4. Try to find memory type.
5116 {
5117 uint32_t memTypeIndex = UINT_MAX;
5118 res = vmaFindMemoryTypeIndex(localAllocator, UINT32_MAX, &allocCreateInfo, &memTypeIndex);
5119 TEST(res != VK_SUCCESS);
5120 }
5121
5122 vmaDestroyAllocator(localAllocator);
5123}
5124
Adam Sawicki40ffe982019-10-11 15:56:02 +02005125static void TestBudget()
5126{
5127 wprintf(L"Testing budget...\n");
5128
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005129 static const VkDeviceSize BUF_SIZE = 10ull * 1024 * 1024;
Adam Sawicki353e3672019-11-02 14:12:05 +01005130 static const uint32_t BUF_COUNT = 4;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005131
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005132 const VkPhysicalDeviceMemoryProperties* memProps = {};
5133 vmaGetMemoryProperties(g_hAllocator, &memProps);
5134
Adam Sawicki40ffe982019-10-11 15:56:02 +02005135 for(uint32_t testIndex = 0; testIndex < 2; ++testIndex)
5136 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005137 vmaSetCurrentFrameIndex(g_hAllocator, ++g_FrameIndex);
5138
5139 VmaBudget budgetBeg[VK_MAX_MEMORY_HEAPS] = {};
5140 vmaGetBudget(g_hAllocator, budgetBeg);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005141
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005142 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005143 {
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005144 TEST(budgetBeg[i].budget > 0);
5145 TEST(budgetBeg[i].budget <= memProps->memoryHeaps[i].size);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01005146 TEST(budgetBeg[i].allocationBytes <= budgetBeg[i].blockBytes);
5147 }
5148
Adam Sawicki40ffe982019-10-11 15:56:02 +02005149 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5150 bufInfo.size = BUF_SIZE;
5151 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5152
5153 VmaAllocationCreateInfo allocCreateInfo = {};
5154 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5155 if(testIndex == 0)
5156 {
5157 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5158 }
5159
5160 // CREATE BUFFERS
5161 uint32_t heapIndex = 0;
5162 BufferInfo bufInfos[BUF_COUNT] = {};
5163 for(uint32_t bufIndex = 0; bufIndex < BUF_COUNT; ++bufIndex)
5164 {
5165 VmaAllocationInfo allocInfo;
5166 VkResult res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5167 &bufInfos[bufIndex].Buffer, &bufInfos[bufIndex].Allocation, &allocInfo);
5168 TEST(res == VK_SUCCESS);
5169 if(bufIndex == 0)
5170 {
5171 heapIndex = MemoryTypeToHeap(allocInfo.memoryType);
5172 }
5173 else
5174 {
5175 // All buffers need to fall into the same heap.
5176 TEST(MemoryTypeToHeap(allocInfo.memoryType) == heapIndex);
5177 }
5178 }
5179
Adam Sawicki353e3672019-11-02 14:12:05 +01005180 VmaBudget budgetWithBufs[VK_MAX_MEMORY_HEAPS] = {};
5181 vmaGetBudget(g_hAllocator, budgetWithBufs);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005182
5183 // DESTROY BUFFERS
5184 for(size_t bufIndex = BUF_COUNT; bufIndex--; )
5185 {
5186 vmaDestroyBuffer(g_hAllocator, bufInfos[bufIndex].Buffer, bufInfos[bufIndex].Allocation);
5187 }
5188
Adam Sawicki353e3672019-11-02 14:12:05 +01005189 VmaBudget budgetEnd[VK_MAX_MEMORY_HEAPS] = {};
5190 vmaGetBudget(g_hAllocator, budgetEnd);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005191
5192 // CHECK
Adam Sawicki6a93b8a2020-03-09 16:58:18 +01005193 for(uint32_t i = 0; i < memProps->memoryHeapCount; ++i)
Adam Sawicki40ffe982019-10-11 15:56:02 +02005194 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005195 TEST(budgetEnd[i].allocationBytes <= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005196 if(i == heapIndex)
5197 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005198 TEST(budgetEnd[i].allocationBytes == budgetBeg[i].allocationBytes);
5199 TEST(budgetWithBufs[i].allocationBytes == budgetBeg[i].allocationBytes + BUF_SIZE * BUF_COUNT);
5200 TEST(budgetWithBufs[i].blockBytes >= budgetEnd[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005201 }
5202 else
5203 {
Adam Sawicki353e3672019-11-02 14:12:05 +01005204 TEST(budgetEnd[i].allocationBytes == budgetEnd[i].allocationBytes &&
5205 budgetEnd[i].allocationBytes == budgetWithBufs[i].allocationBytes);
5206 TEST(budgetEnd[i].blockBytes == budgetEnd[i].blockBytes &&
5207 budgetEnd[i].blockBytes == budgetWithBufs[i].blockBytes);
Adam Sawicki40ffe982019-10-11 15:56:02 +02005208 }
5209 }
5210 }
5211}
5212
Adam Sawicki0620c8e2020-08-18 16:43:44 +02005213static void TestAliasing()
5214{
5215 wprintf(L"Testing aliasing...\n");
5216
5217 /*
5218 This is just a simple test, more like a code sample to demonstrate it's possible.
5219 */
5220
5221 // A 512x512 texture to be sampled.
5222 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
5223 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
5224 img1CreateInfo.extent.width = 512;
5225 img1CreateInfo.extent.height = 512;
5226 img1CreateInfo.extent.depth = 1;
5227 img1CreateInfo.mipLevels = 10;
5228 img1CreateInfo.arrayLayers = 1;
5229 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
5230 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5231 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5232 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
5233 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5234
5235 // A full screen texture to be used as color attachment.
5236 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
5237 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
5238 img2CreateInfo.extent.width = 1920;
5239 img2CreateInfo.extent.height = 1080;
5240 img2CreateInfo.extent.depth = 1;
5241 img2CreateInfo.mipLevels = 1;
5242 img2CreateInfo.arrayLayers = 1;
5243 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
5244 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5245 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5246 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5247 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
5248
5249 VkImage img1 = VK_NULL_HANDLE;
5250 ERR_GUARD_VULKAN(vkCreateImage(g_hDevice, &img1CreateInfo, g_Allocs, &img1));
5251 VkImage img2 = VK_NULL_HANDLE;
5252 ERR_GUARD_VULKAN(vkCreateImage(g_hDevice, &img2CreateInfo, g_Allocs, &img2));
5253
5254 VkMemoryRequirements img1MemReq = {};
5255 vkGetImageMemoryRequirements(g_hDevice, img1, &img1MemReq);
5256 VkMemoryRequirements img2MemReq = {};
5257 vkGetImageMemoryRequirements(g_hDevice, img2, &img2MemReq);
5258
5259 VkMemoryRequirements finalMemReq = {};
5260 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
5261 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
5262 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
5263 if(finalMemReq.memoryTypeBits != 0)
5264 {
5265 wprintf(L" size: max(%llu, %llu) = %llu\n",
5266 img1MemReq.size, img2MemReq.size, finalMemReq.size);
5267 wprintf(L" alignment: max(%llu, %llu) = %llu\n",
5268 img1MemReq.alignment, img2MemReq.alignment, finalMemReq.alignment);
5269 wprintf(L" memoryTypeBits: %u & %u = %u\n",
5270 img1MemReq.memoryTypeBits, img2MemReq.memoryTypeBits, finalMemReq.memoryTypeBits);
5271
5272 VmaAllocationCreateInfo allocCreateInfo = {};
5273 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
5274
5275 VmaAllocation alloc = VK_NULL_HANDLE;
5276 ERR_GUARD_VULKAN(vmaAllocateMemory(g_hAllocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr));
5277
5278 ERR_GUARD_VULKAN(vmaBindImageMemory(g_hAllocator, alloc, img1));
5279 ERR_GUARD_VULKAN(vmaBindImageMemory(g_hAllocator, alloc, img2));
5280
5281 // You can use img1, img2 here, but not at the same time!
5282
5283 vmaFreeMemory(g_hAllocator, alloc);
5284 }
5285 else
5286 {
5287 wprintf(L" Textures cannot alias!\n");
5288 }
5289
5290 vkDestroyImage(g_hDevice, img2, g_Allocs);
5291 vkDestroyImage(g_hDevice, img1, g_Allocs);
5292}
5293
Adam Sawickib8333fb2018-03-13 16:15:53 +01005294static void TestMapping()
5295{
5296 wprintf(L"Testing mapping...\n");
5297
5298 VkResult res;
5299 uint32_t memTypeIndex = UINT32_MAX;
5300
5301 enum TEST
5302 {
5303 TEST_NORMAL,
5304 TEST_POOL,
5305 TEST_DEDICATED,
5306 TEST_COUNT
5307 };
5308 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5309 {
5310 VmaPool pool = nullptr;
5311 if(testIndex == TEST_POOL)
5312 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005313 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005314 VmaPoolCreateInfo poolInfo = {};
5315 poolInfo.memoryTypeIndex = memTypeIndex;
5316 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005317 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005318 }
5319
5320 VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5321 bufInfo.size = 0x10000;
5322 bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005323
Adam Sawickib8333fb2018-03-13 16:15:53 +01005324 VmaAllocationCreateInfo allocCreateInfo = {};
5325 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5326 allocCreateInfo.pool = pool;
5327 if(testIndex == TEST_DEDICATED)
5328 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005329
Adam Sawickib8333fb2018-03-13 16:15:53 +01005330 VmaAllocationInfo allocInfo;
Adam Sawicki40ffe982019-10-11 15:56:02 +02005331
Adam Sawickib8333fb2018-03-13 16:15:53 +01005332 // Mapped manually
5333
5334 // Create 2 buffers.
5335 BufferInfo bufferInfos[3];
5336 for(size_t i = 0; i < 2; ++i)
5337 {
5338 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5339 &bufferInfos[i].Buffer, &bufferInfos[i].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005340 TEST(res == VK_SUCCESS);
5341 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005342 memTypeIndex = allocInfo.memoryType;
5343 }
Adam Sawicki40ffe982019-10-11 15:56:02 +02005344
Adam Sawickib8333fb2018-03-13 16:15:53 +01005345 // Map buffer 0.
5346 char* data00 = nullptr;
5347 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data00);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005348 TEST(res == VK_SUCCESS && data00 != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005349 data00[0xFFFF] = data00[0];
5350
5351 // Map buffer 0 second time.
5352 char* data01 = nullptr;
5353 res = vmaMapMemory(g_hAllocator, bufferInfos[0].Allocation, (void**)&data01);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005354 TEST(res == VK_SUCCESS && data01 == data00);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005355
5356 // Map buffer 1.
5357 char* data1 = nullptr;
5358 res = vmaMapMemory(g_hAllocator, bufferInfos[1].Allocation, (void**)&data1);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005359 TEST(res == VK_SUCCESS && data1 != nullptr);
5360 TEST(!MemoryRegionsOverlap(data00, (size_t)bufInfo.size, data1, (size_t)bufInfo.size));
Adam Sawickib8333fb2018-03-13 16:15:53 +01005361 data1[0xFFFF] = data1[0];
5362
5363 // Unmap buffer 0 two times.
5364 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5365 vmaUnmapMemory(g_hAllocator, bufferInfos[0].Allocation);
5366 vmaGetAllocationInfo(g_hAllocator, bufferInfos[0].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005367 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005368
5369 // Unmap buffer 1.
5370 vmaUnmapMemory(g_hAllocator, bufferInfos[1].Allocation);
5371 vmaGetAllocationInfo(g_hAllocator, bufferInfos[1].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005372 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005373
5374 // Create 3rd buffer - persistently mapped.
5375 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5376 res = vmaCreateBuffer(g_hAllocator, &bufInfo, &allocCreateInfo,
5377 &bufferInfos[2].Buffer, &bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005378 TEST(res == VK_SUCCESS && allocInfo.pMappedData != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005379
5380 // Map buffer 2.
5381 char* data2 = nullptr;
5382 res = vmaMapMemory(g_hAllocator, bufferInfos[2].Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005383 TEST(res == VK_SUCCESS && data2 == allocInfo.pMappedData);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005384 data2[0xFFFF] = data2[0];
5385
5386 // Unmap buffer 2.
5387 vmaUnmapMemory(g_hAllocator, bufferInfos[2].Allocation);
5388 vmaGetAllocationInfo(g_hAllocator, bufferInfos[2].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005389 TEST(allocInfo.pMappedData == data2);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005390
5391 // Destroy all buffers.
5392 for(size_t i = 3; i--; )
5393 vmaDestroyBuffer(g_hAllocator, bufferInfos[i].Buffer, bufferInfos[i].Allocation);
5394
5395 vmaDestroyPool(g_hAllocator, pool);
5396 }
5397}
5398
Adam Sawickidaa6a552019-06-25 15:26:37 +02005399// Test CREATE_MAPPED with required DEVICE_LOCAL. There was a bug with it.
5400static void TestDeviceLocalMapped()
5401{
5402 VkResult res;
5403
5404 for(uint32_t testIndex = 0; testIndex < 3; ++testIndex)
5405 {
5406 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5407 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
5408 bufCreateInfo.size = 4096;
5409
5410 VmaPool pool = VK_NULL_HANDLE;
5411 VmaAllocationCreateInfo allocCreateInfo = {};
5412 allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
5413 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
5414 if(testIndex == 2)
5415 {
5416 VmaPoolCreateInfo poolCreateInfo = {};
5417 res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
5418 TEST(res == VK_SUCCESS);
5419 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
5420 TEST(res == VK_SUCCESS);
5421 allocCreateInfo.pool = pool;
5422 }
5423 else if(testIndex == 1)
5424 {
5425 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
5426 }
5427
5428 VkBuffer buf = VK_NULL_HANDLE;
5429 VmaAllocation alloc = VK_NULL_HANDLE;
5430 VmaAllocationInfo allocInfo = {};
5431 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
5432 TEST(res == VK_SUCCESS && alloc);
5433
5434 VkMemoryPropertyFlags memTypeFlags = 0;
5435 vmaGetMemoryTypeProperties(g_hAllocator, allocInfo.memoryType, &memTypeFlags);
5436 const bool shouldBeMapped = (memTypeFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
5437 TEST((allocInfo.pMappedData != nullptr) == shouldBeMapped);
5438
5439 vmaDestroyBuffer(g_hAllocator, buf, alloc);
5440 vmaDestroyPool(g_hAllocator, pool);
5441 }
5442}
5443
Adam Sawickib8333fb2018-03-13 16:15:53 +01005444static void TestMappingMultithreaded()
5445{
5446 wprintf(L"Testing mapping multithreaded...\n");
5447
5448 static const uint32_t threadCount = 16;
5449 static const uint32_t bufferCount = 1024;
5450 static const uint32_t threadBufferCount = bufferCount / threadCount;
5451
5452 VkResult res;
5453 volatile uint32_t memTypeIndex = UINT32_MAX;
5454
5455 enum TEST
5456 {
5457 TEST_NORMAL,
5458 TEST_POOL,
5459 TEST_DEDICATED,
5460 TEST_COUNT
5461 };
5462 for(uint32_t testIndex = 0; testIndex < TEST_COUNT; ++testIndex)
5463 {
5464 VmaPool pool = nullptr;
5465 if(testIndex == TEST_POOL)
5466 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005467 TEST(memTypeIndex != UINT32_MAX);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005468 VmaPoolCreateInfo poolInfo = {};
5469 poolInfo.memoryTypeIndex = memTypeIndex;
5470 res = vmaCreatePool(g_hAllocator, &poolInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005471 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005472 }
5473
5474 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
5475 bufCreateInfo.size = 0x10000;
5476 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
5477
5478 VmaAllocationCreateInfo allocCreateInfo = {};
5479 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
5480 allocCreateInfo.pool = pool;
5481 if(testIndex == TEST_DEDICATED)
5482 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
5483
5484 std::thread threads[threadCount];
5485 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5486 {
5487 threads[threadIndex] = std::thread([=, &memTypeIndex](){
5488 // ======== THREAD FUNCTION ========
5489
5490 RandomNumberGenerator rand{threadIndex};
5491
5492 enum class MODE
5493 {
5494 // Don't map this buffer at all.
5495 DONT_MAP,
5496 // Map and quickly unmap.
5497 MAP_FOR_MOMENT,
5498 // Map and unmap before destruction.
5499 MAP_FOR_LONGER,
5500 // Map two times. Quickly unmap, second unmap before destruction.
5501 MAP_TWO_TIMES,
5502 // Create this buffer as persistently mapped.
5503 PERSISTENTLY_MAPPED,
5504 COUNT
5505 };
5506 std::vector<BufferInfo> bufInfos{threadBufferCount};
5507 std::vector<MODE> bufModes{threadBufferCount};
5508
5509 for(uint32_t bufferIndex = 0; bufferIndex < threadBufferCount; ++bufferIndex)
5510 {
5511 BufferInfo& bufInfo = bufInfos[bufferIndex];
5512 const MODE mode = (MODE)(rand.Generate() % (uint32_t)MODE::COUNT);
5513 bufModes[bufferIndex] = mode;
5514
5515 VmaAllocationCreateInfo localAllocCreateInfo = allocCreateInfo;
5516 if(mode == MODE::PERSISTENTLY_MAPPED)
5517 localAllocCreateInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
5518
5519 VmaAllocationInfo allocInfo;
5520 VkResult res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &localAllocCreateInfo,
5521 &bufInfo.Buffer, &bufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005522 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005523
5524 if(memTypeIndex == UINT32_MAX)
5525 memTypeIndex = allocInfo.memoryType;
5526
5527 char* data = nullptr;
5528
5529 if(mode == MODE::PERSISTENTLY_MAPPED)
5530 {
5531 data = (char*)allocInfo.pMappedData;
Adam Sawickib8d34d52018-10-03 17:41:20 +02005532 TEST(data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005533 }
5534 else if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_FOR_LONGER ||
5535 mode == MODE::MAP_TWO_TIMES)
5536 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005537 TEST(data == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005538 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005539 TEST(res == VK_SUCCESS && data != nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005540
5541 if(mode == MODE::MAP_TWO_TIMES)
5542 {
5543 char* data2 = nullptr;
5544 res = vmaMapMemory(g_hAllocator, bufInfo.Allocation, (void**)&data2);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005545 TEST(res == VK_SUCCESS && data2 == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005546 }
5547 }
5548 else if(mode == MODE::DONT_MAP)
5549 {
Adam Sawickib8d34d52018-10-03 17:41:20 +02005550 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005551 }
5552 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005553 TEST(0);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005554
5555 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5556 if(data)
5557 data[0xFFFF] = data[0];
5558
5559 if(mode == MODE::MAP_FOR_MOMENT || mode == MODE::MAP_TWO_TIMES)
5560 {
5561 vmaUnmapMemory(g_hAllocator, bufInfo.Allocation);
5562
5563 VmaAllocationInfo allocInfo;
5564 vmaGetAllocationInfo(g_hAllocator, bufInfo.Allocation, &allocInfo);
5565 if(mode == MODE::MAP_FOR_MOMENT)
Adam Sawickib8d34d52018-10-03 17:41:20 +02005566 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005567 else
Adam Sawickib8d34d52018-10-03 17:41:20 +02005568 TEST(allocInfo.pMappedData == data);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005569 }
5570
5571 switch(rand.Generate() % 3)
5572 {
5573 case 0: Sleep(0); break; // Yield.
5574 case 1: Sleep(10); break; // 10 ms
5575 // default: No sleep.
5576 }
5577
5578 // Test if reading and writing from the beginning and end of mapped memory doesn't crash.
5579 if(data)
5580 data[0xFFFF] = data[0];
5581 }
5582
5583 for(size_t bufferIndex = threadBufferCount; bufferIndex--; )
5584 {
5585 if(bufModes[bufferIndex] == MODE::MAP_FOR_LONGER ||
5586 bufModes[bufferIndex] == MODE::MAP_TWO_TIMES)
5587 {
5588 vmaUnmapMemory(g_hAllocator, bufInfos[bufferIndex].Allocation);
5589
5590 VmaAllocationInfo allocInfo;
5591 vmaGetAllocationInfo(g_hAllocator, bufInfos[bufferIndex].Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005592 TEST(allocInfo.pMappedData == nullptr);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005593 }
5594
5595 vmaDestroyBuffer(g_hAllocator, bufInfos[bufferIndex].Buffer, bufInfos[bufferIndex].Allocation);
5596 }
5597 });
5598 }
5599
5600 for(uint32_t threadIndex = 0; threadIndex < threadCount; ++threadIndex)
5601 threads[threadIndex].join();
5602
5603 vmaDestroyPool(g_hAllocator, pool);
5604 }
5605}
5606
5607static void WriteMainTestResultHeader(FILE* file)
5608{
5609 fprintf(file,
Adam Sawicki740b08f2018-08-27 13:42:07 +02005610 "Code,Time,"
5611 "Threads,Buffers and images,Sizes,Operations,Allocation strategy,Free order,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005612 "Total Time (us),"
5613 "Allocation Time Min (us),"
5614 "Allocation Time Avg (us),"
5615 "Allocation Time Max (us),"
5616 "Deallocation Time Min (us),"
5617 "Deallocation Time Avg (us),"
5618 "Deallocation Time Max (us),"
5619 "Total Memory Allocated (B),"
5620 "Free Range Size Avg (B),"
5621 "Free Range Size Max (B)\n");
5622}
5623
5624static void WriteMainTestResult(
5625 FILE* file,
5626 const char* codeDescription,
5627 const char* testDescription,
5628 const Config& config, const Result& result)
5629{
5630 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5631 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5632 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5633 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5634 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5635 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5636 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5637
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005638 std::string currTime;
5639 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005640
5641 fprintf(file,
5642 "%s,%s,%s,"
Adam Sawickib8333fb2018-03-13 16:15:53 +01005643 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u\n",
5644 codeDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005645 currTime.c_str(),
Adam Sawicki740b08f2018-08-27 13:42:07 +02005646 testDescription,
Adam Sawickib8333fb2018-03-13 16:15:53 +01005647 totalTimeSeconds * 1e6f,
5648 allocationTimeMinSeconds * 1e6f,
5649 allocationTimeAvgSeconds * 1e6f,
5650 allocationTimeMaxSeconds * 1e6f,
5651 deallocationTimeMinSeconds * 1e6f,
5652 deallocationTimeAvgSeconds * 1e6f,
5653 deallocationTimeMaxSeconds * 1e6f,
5654 result.TotalMemoryAllocated,
5655 result.FreeRangeSizeAvg,
5656 result.FreeRangeSizeMax);
5657}
5658
5659static void WritePoolTestResultHeader(FILE* file)
5660{
5661 fprintf(file,
5662 "Code,Test,Time,"
5663 "Config,"
5664 "Total Time (us),"
5665 "Allocation Time Min (us),"
5666 "Allocation Time Avg (us),"
5667 "Allocation Time Max (us),"
5668 "Deallocation Time Min (us),"
5669 "Deallocation Time Avg (us),"
5670 "Deallocation Time Max (us),"
5671 "Lost Allocation Count,"
5672 "Lost Allocation Total Size (B),"
5673 "Failed Allocation Count,"
5674 "Failed Allocation Total Size (B)\n");
5675}
5676
5677static void WritePoolTestResult(
5678 FILE* file,
5679 const char* codeDescription,
5680 const char* testDescription,
5681 const PoolTestConfig& config,
5682 const PoolTestResult& result)
5683{
5684 float totalTimeSeconds = ToFloatSeconds(result.TotalTime);
5685 float allocationTimeMinSeconds = ToFloatSeconds(result.AllocationTimeMin);
5686 float allocationTimeAvgSeconds = ToFloatSeconds(result.AllocationTimeAvg);
5687 float allocationTimeMaxSeconds = ToFloatSeconds(result.AllocationTimeMax);
5688 float deallocationTimeMinSeconds = ToFloatSeconds(result.DeallocationTimeMin);
5689 float deallocationTimeAvgSeconds = ToFloatSeconds(result.DeallocationTimeAvg);
5690 float deallocationTimeMaxSeconds = ToFloatSeconds(result.DeallocationTimeMax);
5691
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005692 std::string currTime;
5693 CurrentTimeToStr(currTime);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005694
5695 fprintf(file,
5696 "%s,%s,%s,"
5697 "ThreadCount=%u PoolSize=%llu FrameCount=%u TotalItemCount=%u UsedItemCount=%u...%u ItemsToMakeUnusedPercent=%u,"
5698 "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%I64u,%I64u,%I64u,%I64u\n",
5699 // General
5700 codeDescription,
5701 testDescription,
Adam Sawicki33d2ce72018-08-27 13:59:13 +02005702 currTime.c_str(),
Adam Sawickib8333fb2018-03-13 16:15:53 +01005703 // Config
5704 config.ThreadCount,
5705 (unsigned long long)config.PoolSize,
5706 config.FrameCount,
5707 config.TotalItemCount,
5708 config.UsedItemCountMin,
5709 config.UsedItemCountMax,
5710 config.ItemsToMakeUnusedPercent,
5711 // Results
5712 totalTimeSeconds * 1e6f,
5713 allocationTimeMinSeconds * 1e6f,
5714 allocationTimeAvgSeconds * 1e6f,
5715 allocationTimeMaxSeconds * 1e6f,
5716 deallocationTimeMinSeconds * 1e6f,
5717 deallocationTimeAvgSeconds * 1e6f,
5718 deallocationTimeMaxSeconds * 1e6f,
5719 result.LostAllocationCount,
5720 result.LostAllocationTotalSize,
5721 result.FailedAllocationCount,
5722 result.FailedAllocationTotalSize);
5723}
5724
5725static void PerformCustomMainTest(FILE* file)
5726{
5727 Config config{};
5728 config.RandSeed = 65735476;
5729 //config.MaxBytesToAllocate = 4ull * 1024 * 1024; // 4 MB
5730 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5731 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5732 config.FreeOrder = FREE_ORDER::FORWARD;
5733 config.ThreadCount = 16;
5734 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
Adam Sawicki0667e332018-08-24 17:26:44 +02005735 config.AllocationStrategy = 0;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005736
5737 // Buffers
5738 //config.AllocationSizes.push_back({4, 16, 1024});
5739 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5740
5741 // Images
5742 //config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5743 //config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5744
5745 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5746 config.AdditionalOperationCount = 1024;
5747
5748 Result result{};
5749 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02005750 TEST(res == VK_SUCCESS);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005751 WriteMainTestResult(file, "Foo", "CustomTest", config, result);
5752}
5753
5754static void PerformCustomPoolTest(FILE* file)
5755{
5756 PoolTestConfig config;
5757 config.PoolSize = 100 * 1024 * 1024;
5758 config.RandSeed = 2345764;
5759 config.ThreadCount = 1;
5760 config.FrameCount = 200;
5761 config.ItemsToMakeUnusedPercent = 2;
5762
5763 AllocationSize allocSize = {};
5764 allocSize.BufferSizeMin = 1024;
5765 allocSize.BufferSizeMax = 1024 * 1024;
5766 allocSize.Probability = 1;
5767 config.AllocationSizes.push_back(allocSize);
5768
5769 allocSize.BufferSizeMin = 0;
5770 allocSize.BufferSizeMax = 0;
5771 allocSize.ImageSizeMin = 128;
5772 allocSize.ImageSizeMax = 1024;
5773 allocSize.Probability = 1;
5774 config.AllocationSizes.push_back(allocSize);
5775
5776 config.PoolSize = config.CalcAvgResourceSize() * 200;
5777 config.UsedItemCountMax = 160;
5778 config.TotalItemCount = config.UsedItemCountMax * 10;
5779 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
5780
Adam Sawickib8333fb2018-03-13 16:15:53 +01005781 PoolTestResult result = {};
5782 TestPool_Benchmark(result, config);
Adam Sawickib8333fb2018-03-13 16:15:53 +01005783
5784 WritePoolTestResult(file, "Code desc", "Test desc", config, result);
5785}
5786
Adam Sawickib8333fb2018-03-13 16:15:53 +01005787static void PerformMainTests(FILE* file)
5788{
Adam Sawicki7e56c482021-02-23 15:27:24 +01005789 wprintf(L"MAIN TESTS:\n");
5790
Adam Sawickib8333fb2018-03-13 16:15:53 +01005791 uint32_t repeatCount = 1;
5792 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
5793
5794 Config config{};
5795 config.RandSeed = 65735476;
5796 config.MemUsageProbability[0] = 1; // VMA_MEMORY_USAGE_GPU_ONLY
5797 config.FreeOrder = FREE_ORDER::FORWARD;
5798
5799 size_t threadCountCount = 1;
5800 switch(ConfigType)
5801 {
5802 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
5803 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
5804 case CONFIG_TYPE_AVERAGE: threadCountCount = 3; break;
5805 case CONFIG_TYPE_LARGE: threadCountCount = 5; break;
5806 case CONFIG_TYPE_MAXIMUM: threadCountCount = 7; break;
5807 default: assert(0);
5808 }
Adam Sawicki0667e332018-08-24 17:26:44 +02005809
Adam Sawicki0a3fb6c2018-08-27 14:40:27 +02005810 const size_t strategyCount = GetAllocationStrategyCount();
Adam Sawicki0667e332018-08-24 17:26:44 +02005811
Adam Sawickib8333fb2018-03-13 16:15:53 +01005812 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
5813 {
5814 std::string desc1;
5815
5816 switch(threadCountIndex)
5817 {
5818 case 0:
5819 desc1 += "1_thread";
5820 config.ThreadCount = 1;
5821 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5822 break;
5823 case 1:
5824 desc1 += "16_threads+0%_common";
5825 config.ThreadCount = 16;
5826 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5827 break;
5828 case 2:
5829 desc1 += "16_threads+50%_common";
5830 config.ThreadCount = 16;
5831 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5832 break;
5833 case 3:
5834 desc1 += "16_threads+100%_common";
5835 config.ThreadCount = 16;
5836 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5837 break;
5838 case 4:
5839 desc1 += "2_threads+0%_common";
5840 config.ThreadCount = 2;
5841 config.ThreadsUsingCommonAllocationsProbabilityPercent = 0;
5842 break;
5843 case 5:
5844 desc1 += "2_threads+50%_common";
5845 config.ThreadCount = 2;
5846 config.ThreadsUsingCommonAllocationsProbabilityPercent = 50;
5847 break;
5848 case 6:
5849 desc1 += "2_threads+100%_common";
5850 config.ThreadCount = 2;
5851 config.ThreadsUsingCommonAllocationsProbabilityPercent = 100;
5852 break;
5853 default:
5854 assert(0);
5855 }
5856
5857 // 0 = buffers, 1 = images, 2 = buffers and images
5858 size_t buffersVsImagesCount = 2;
5859 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
5860 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
5861 {
5862 std::string desc2 = desc1;
5863 switch(buffersVsImagesIndex)
5864 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005865 case 0: desc2 += ",Buffers"; break;
5866 case 1: desc2 += ",Images"; break;
5867 case 2: desc2 += ",Buffers+Images"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005868 default: assert(0);
5869 }
5870
5871 // 0 = small, 1 = large, 2 = small and large
5872 size_t smallVsLargeCount = 2;
5873 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
5874 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
5875 {
5876 std::string desc3 = desc2;
5877 switch(smallVsLargeIndex)
5878 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02005879 case 0: desc3 += ",Small"; break;
5880 case 1: desc3 += ",Large"; break;
5881 case 2: desc3 += ",Small+Large"; break;
Adam Sawickib8333fb2018-03-13 16:15:53 +01005882 default: assert(0);
5883 }
5884
5885 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5886 config.MaxBytesToAllocate = 4ull * 1024 * 1024 * 1024; // 4 GB
5887 else
5888 config.MaxBytesToAllocate = 4ull * 1024 * 1024;
5889
5890 // 0 = varying sizes min...max, 1 = set of constant sizes
5891 size_t constantSizesCount = 1;
5892 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
5893 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
5894 {
5895 std::string desc4 = desc3;
5896 switch(constantSizesIndex)
5897 {
5898 case 0: desc4 += " Varying_sizes"; break;
5899 case 1: desc4 += " Constant_sizes"; break;
5900 default: assert(0);
5901 }
5902
5903 config.AllocationSizes.clear();
5904 // Buffers present
5905 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
5906 {
5907 // Small
5908 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5909 {
5910 // Varying size
5911 if(constantSizesIndex == 0)
5912 config.AllocationSizes.push_back({4, 16, 1024});
5913 // Constant sizes
5914 else
5915 {
5916 config.AllocationSizes.push_back({1, 16, 16});
5917 config.AllocationSizes.push_back({1, 64, 64});
5918 config.AllocationSizes.push_back({1, 256, 256});
5919 config.AllocationSizes.push_back({1, 1024, 1024});
5920 }
5921 }
5922 // Large
5923 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5924 {
5925 // Varying size
5926 if(constantSizesIndex == 0)
5927 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
5928 // Constant sizes
5929 else
5930 {
5931 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
5932 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
5933 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
5934 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
5935 }
5936 }
5937 }
5938 // Images present
5939 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
5940 {
5941 // Small
5942 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
5943 {
5944 // Varying size
5945 if(constantSizesIndex == 0)
5946 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
5947 // Constant sizes
5948 else
5949 {
5950 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
5951 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
5952 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
5953 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
5954 }
5955 }
5956 // Large
5957 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
5958 {
5959 // Varying size
5960 if(constantSizesIndex == 0)
5961 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
5962 // Constant sizes
5963 else
5964 {
5965 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
5966 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
5967 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
5968 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
5969 }
5970 }
5971 }
5972
5973 // 0 = 100%, additional_operations = 0, 1 = 50%, 2 = 5%, 3 = 95% additional_operations = a lot
5974 size_t beginBytesToAllocateCount = 1;
5975 if(ConfigType >= CONFIG_TYPE_SMALL) ++beginBytesToAllocateCount;
5976 if(ConfigType >= CONFIG_TYPE_AVERAGE) ++beginBytesToAllocateCount;
5977 if(ConfigType >= CONFIG_TYPE_LARGE) ++beginBytesToAllocateCount;
5978 for(size_t beginBytesToAllocateIndex = 0; beginBytesToAllocateIndex < beginBytesToAllocateCount; ++beginBytesToAllocateIndex)
5979 {
5980 std::string desc5 = desc4;
5981
5982 switch(beginBytesToAllocateIndex)
5983 {
5984 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005985 desc5 += ",Allocate_100%";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005986 config.BeginBytesToAllocate = config.MaxBytesToAllocate;
5987 config.AdditionalOperationCount = 0;
5988 break;
5989 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005990 desc5 += ",Allocate_50%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005991 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 50 / 100;
5992 config.AdditionalOperationCount = 1024;
5993 break;
5994 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02005995 desc5 += ",Allocate_5%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01005996 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 5 / 100;
5997 config.AdditionalOperationCount = 1024;
5998 break;
5999 case 3:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006000 desc5 += ",Allocate_95%+Operations";
Adam Sawickib8333fb2018-03-13 16:15:53 +01006001 config.BeginBytesToAllocate = config.MaxBytesToAllocate * 95 / 100;
6002 config.AdditionalOperationCount = 1024;
6003 break;
6004 default:
6005 assert(0);
6006 }
6007
Adam Sawicki0667e332018-08-24 17:26:44 +02006008 for(size_t strategyIndex = 0; strategyIndex < strategyCount; ++strategyIndex)
Adam Sawickib8333fb2018-03-13 16:15:53 +01006009 {
Adam Sawicki0667e332018-08-24 17:26:44 +02006010 std::string desc6 = desc5;
6011 switch(strategyIndex)
6012 {
6013 case 0:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006014 desc6 += ",BestFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006015 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
6016 break;
6017 case 1:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006018 desc6 += ",WorstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006019 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
6020 break;
6021 case 2:
Adam Sawicki740b08f2018-08-27 13:42:07 +02006022 desc6 += ",FirstFit";
Adam Sawicki0667e332018-08-24 17:26:44 +02006023 config.AllocationStrategy = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT;
6024 break;
6025 default:
6026 assert(0);
6027 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01006028
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006029 desc6 += ',';
6030 desc6 += FREE_ORDER_NAMES[(uint32_t)config.FreeOrder];
Adam Sawicki740b08f2018-08-27 13:42:07 +02006031
6032 const char* testDescription = desc6.c_str();
Adam Sawicki0667e332018-08-24 17:26:44 +02006033
6034 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
6035 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02006036 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawicki0667e332018-08-24 17:26:44 +02006037
6038 Result result{};
6039 VkResult res = MainTest(result, config);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006040 TEST(res == VK_SUCCESS);
Adam Sawicki740b08f2018-08-27 13:42:07 +02006041 if(file)
6042 {
6043 WriteMainTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
6044 }
Adam Sawicki0667e332018-08-24 17:26:44 +02006045 }
Adam Sawickib8333fb2018-03-13 16:15:53 +01006046 }
6047 }
6048 }
6049 }
6050 }
6051 }
6052}
6053
6054static void PerformPoolTests(FILE* file)
6055{
Adam Sawicki7e56c482021-02-23 15:27:24 +01006056 wprintf(L"POOL TESTS:\n");
6057
Adam Sawickib8333fb2018-03-13 16:15:53 +01006058 const size_t AVG_RESOURCES_PER_POOL = 300;
6059
6060 uint32_t repeatCount = 1;
6061 if(ConfigType >= CONFIG_TYPE_MAXIMUM) repeatCount = 3;
6062
6063 PoolTestConfig config{};
6064 config.RandSeed = 2346343;
6065 config.FrameCount = 200;
6066 config.ItemsToMakeUnusedPercent = 2;
6067
6068 size_t threadCountCount = 1;
6069 switch(ConfigType)
6070 {
6071 case CONFIG_TYPE_MINIMUM: threadCountCount = 1; break;
6072 case CONFIG_TYPE_SMALL: threadCountCount = 2; break;
6073 case CONFIG_TYPE_AVERAGE: threadCountCount = 2; break;
6074 case CONFIG_TYPE_LARGE: threadCountCount = 3; break;
6075 case CONFIG_TYPE_MAXIMUM: threadCountCount = 3; break;
6076 default: assert(0);
6077 }
6078 for(size_t threadCountIndex = 0; threadCountIndex < threadCountCount; ++threadCountIndex)
6079 {
6080 std::string desc1;
6081
6082 switch(threadCountIndex)
6083 {
6084 case 0:
6085 desc1 += "1_thread";
6086 config.ThreadCount = 1;
6087 break;
6088 case 1:
6089 desc1 += "16_threads";
6090 config.ThreadCount = 16;
6091 break;
6092 case 2:
6093 desc1 += "2_threads";
6094 config.ThreadCount = 2;
6095 break;
6096 default:
6097 assert(0);
6098 }
6099
6100 // 0 = buffers, 1 = images, 2 = buffers and images
6101 size_t buffersVsImagesCount = 2;
6102 if(ConfigType >= CONFIG_TYPE_LARGE) ++buffersVsImagesCount;
6103 for(size_t buffersVsImagesIndex = 0; buffersVsImagesIndex < buffersVsImagesCount; ++buffersVsImagesIndex)
6104 {
6105 std::string desc2 = desc1;
6106 switch(buffersVsImagesIndex)
6107 {
6108 case 0: desc2 += " Buffers"; break;
6109 case 1: desc2 += " Images"; break;
6110 case 2: desc2 += " Buffers+Images"; break;
6111 default: assert(0);
6112 }
6113
6114 // 0 = small, 1 = large, 2 = small and large
6115 size_t smallVsLargeCount = 2;
6116 if(ConfigType >= CONFIG_TYPE_LARGE) ++smallVsLargeCount;
6117 for(size_t smallVsLargeIndex = 0; smallVsLargeIndex < smallVsLargeCount; ++smallVsLargeIndex)
6118 {
6119 std::string desc3 = desc2;
6120 switch(smallVsLargeIndex)
6121 {
6122 case 0: desc3 += " Small"; break;
6123 case 1: desc3 += " Large"; break;
6124 case 2: desc3 += " Small+Large"; break;
6125 default: assert(0);
6126 }
6127
6128 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6129 config.PoolSize = 6ull * 1024 * 1024 * 1024; // 6 GB
6130 else
6131 config.PoolSize = 4ull * 1024 * 1024;
6132
6133 // 0 = varying sizes min...max, 1 = set of constant sizes
6134 size_t constantSizesCount = 1;
6135 if(ConfigType >= CONFIG_TYPE_SMALL) ++constantSizesCount;
6136 for(size_t constantSizesIndex = 0; constantSizesIndex < constantSizesCount; ++constantSizesIndex)
6137 {
6138 std::string desc4 = desc3;
6139 switch(constantSizesIndex)
6140 {
6141 case 0: desc4 += " Varying_sizes"; break;
6142 case 1: desc4 += " Constant_sizes"; break;
6143 default: assert(0);
6144 }
6145
6146 config.AllocationSizes.clear();
6147 // Buffers present
6148 if(buffersVsImagesIndex == 0 || buffersVsImagesIndex == 2)
6149 {
6150 // Small
6151 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
6152 {
6153 // Varying size
6154 if(constantSizesIndex == 0)
6155 config.AllocationSizes.push_back({4, 16, 1024});
6156 // Constant sizes
6157 else
6158 {
6159 config.AllocationSizes.push_back({1, 16, 16});
6160 config.AllocationSizes.push_back({1, 64, 64});
6161 config.AllocationSizes.push_back({1, 256, 256});
6162 config.AllocationSizes.push_back({1, 1024, 1024});
6163 }
6164 }
6165 // Large
6166 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6167 {
6168 // Varying size
6169 if(constantSizesIndex == 0)
6170 config.AllocationSizes.push_back({4, 0x10000, 0xA00000}); // 64 KB ... 10 MB
6171 // Constant sizes
6172 else
6173 {
6174 config.AllocationSizes.push_back({1, 0x10000, 0x10000});
6175 config.AllocationSizes.push_back({1, 0x80000, 0x80000});
6176 config.AllocationSizes.push_back({1, 0x200000, 0x200000});
6177 config.AllocationSizes.push_back({1, 0xA00000, 0xA00000});
6178 }
6179 }
6180 }
6181 // Images present
6182 if(buffersVsImagesIndex == 1 || buffersVsImagesIndex == 2)
6183 {
6184 // Small
6185 if(smallVsLargeIndex == 0 || smallVsLargeIndex == 2)
6186 {
6187 // Varying size
6188 if(constantSizesIndex == 0)
6189 config.AllocationSizes.push_back({4, 0, 0, 4, 32});
6190 // Constant sizes
6191 else
6192 {
6193 config.AllocationSizes.push_back({1, 0, 0, 4, 4});
6194 config.AllocationSizes.push_back({1, 0, 0, 8, 8});
6195 config.AllocationSizes.push_back({1, 0, 0, 16, 16});
6196 config.AllocationSizes.push_back({1, 0, 0, 32, 32});
6197 }
6198 }
6199 // Large
6200 if(smallVsLargeIndex == 1 || smallVsLargeIndex == 2)
6201 {
6202 // Varying size
6203 if(constantSizesIndex == 0)
6204 config.AllocationSizes.push_back({4, 0, 0, 256, 2048});
6205 // Constant sizes
6206 else
6207 {
6208 config.AllocationSizes.push_back({1, 0, 0, 256, 256});
6209 config.AllocationSizes.push_back({1, 0, 0, 512, 512});
6210 config.AllocationSizes.push_back({1, 0, 0, 1024, 1024});
6211 config.AllocationSizes.push_back({1, 0, 0, 2048, 2048});
6212 }
6213 }
6214 }
6215
6216 const VkDeviceSize avgResourceSize = config.CalcAvgResourceSize();
6217 config.PoolSize = avgResourceSize * AVG_RESOURCES_PER_POOL;
6218
6219 // 0 = 66%, 1 = 133%, 2 = 100%, 3 = 33%, 4 = 166%
6220 size_t subscriptionModeCount;
6221 switch(ConfigType)
6222 {
6223 case CONFIG_TYPE_MINIMUM: subscriptionModeCount = 2; break;
6224 case CONFIG_TYPE_SMALL: subscriptionModeCount = 2; break;
6225 case CONFIG_TYPE_AVERAGE: subscriptionModeCount = 3; break;
6226 case CONFIG_TYPE_LARGE: subscriptionModeCount = 5; break;
6227 case CONFIG_TYPE_MAXIMUM: subscriptionModeCount = 5; break;
6228 default: assert(0);
6229 }
6230 for(size_t subscriptionModeIndex = 0; subscriptionModeIndex < subscriptionModeCount; ++subscriptionModeIndex)
6231 {
6232 std::string desc5 = desc4;
6233
6234 switch(subscriptionModeIndex)
6235 {
6236 case 0:
6237 desc5 += " Subscription_66%";
6238 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 66 / 100;
6239 break;
6240 case 1:
6241 desc5 += " Subscription_133%";
6242 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 133 / 100;
6243 break;
6244 case 2:
6245 desc5 += " Subscription_100%";
6246 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL;
6247 break;
6248 case 3:
6249 desc5 += " Subscription_33%";
6250 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 33 / 100;
6251 break;
6252 case 4:
6253 desc5 += " Subscription_166%";
6254 config.UsedItemCountMax = AVG_RESOURCES_PER_POOL * 166 / 100;
6255 break;
6256 default:
6257 assert(0);
6258 }
6259
6260 config.TotalItemCount = config.UsedItemCountMax * 5;
6261 config.UsedItemCountMin = config.UsedItemCountMax * 80 / 100;
6262
6263 const char* testDescription = desc5.c_str();
6264
6265 for(size_t repeat = 0; repeat < repeatCount; ++repeat)
6266 {
Adam Sawicki740b08f2018-08-27 13:42:07 +02006267 printf("%s #%u\n", testDescription, (uint32_t)repeat);
Adam Sawickib8333fb2018-03-13 16:15:53 +01006268
6269 PoolTestResult result{};
Adam Sawickib8333fb2018-03-13 16:15:53 +01006270 TestPool_Benchmark(result, config);
Adam Sawickib8333fb2018-03-13 16:15:53 +01006271 WritePoolTestResult(file, CODE_DESCRIPTION, testDescription, config, result);
6272 }
6273 }
6274 }
6275 }
6276 }
6277 }
6278}
6279
Adam Sawickia83793a2018-09-03 13:40:42 +02006280static void BasicTestBuddyAllocator()
6281{
6282 wprintf(L"Basic test buddy allocator\n");
6283
6284 RandomNumberGenerator rand{76543};
6285
6286 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6287 sampleBufCreateInfo.size = 1024; // Whatever.
6288 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6289
6290 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6291 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6292
6293 VmaPoolCreateInfo poolCreateInfo = {};
6294 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006295 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006296
Adam Sawickid6e6d6b2018-09-21 14:07:02 +02006297 // Deliberately adding 1023 to test usable size smaller than memory block size.
6298 poolCreateInfo.blockSize = 1024 * 1024 + 1023;
Adam Sawickia83793a2018-09-03 13:40:42 +02006299 poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
Adam Sawicki80927152018-09-07 17:27:23 +02006300 //poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
Adam Sawickia83793a2018-09-03 13:40:42 +02006301
6302 VmaPool pool = nullptr;
6303 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006304 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006305
6306 VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
6307
6308 VmaAllocationCreateInfo allocCreateInfo = {};
6309 allocCreateInfo.pool = pool;
6310
6311 std::vector<BufferInfo> bufInfo;
6312 BufferInfo newBufInfo;
6313 VmaAllocationInfo allocInfo;
6314
6315 bufCreateInfo.size = 1024 * 256;
6316 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6317 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006318 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006319 bufInfo.push_back(newBufInfo);
6320
6321 bufCreateInfo.size = 1024 * 512;
6322 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6323 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006324 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006325 bufInfo.push_back(newBufInfo);
6326
6327 bufCreateInfo.size = 1024 * 128;
6328 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6329 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006330 TEST(res == VK_SUCCESS);
Adam Sawickia83793a2018-09-03 13:40:42 +02006331 bufInfo.push_back(newBufInfo);
Adam Sawickia01d4582018-09-21 14:22:35 +02006332
6333 // Test very small allocation, smaller than minimum node size.
6334 bufCreateInfo.size = 1;
6335 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6336 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006337 TEST(res == VK_SUCCESS);
Adam Sawickia01d4582018-09-21 14:22:35 +02006338 bufInfo.push_back(newBufInfo);
Adam Sawickia83793a2018-09-03 13:40:42 +02006339
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006340 // Test some small allocation with alignment requirement.
6341 {
6342 VkMemoryRequirements memReq;
6343 memReq.alignment = 256;
6344 memReq.memoryTypeBits = UINT32_MAX;
6345 memReq.size = 32;
6346
6347 newBufInfo.Buffer = VK_NULL_HANDLE;
6348 res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo,
6349 &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006350 TEST(res == VK_SUCCESS);
6351 TEST(allocInfo.offset % memReq.alignment == 0);
Adam Sawicki9933c5c2018-09-21 14:57:24 +02006352 bufInfo.push_back(newBufInfo);
6353 }
6354
6355 //SaveAllocatorStatsToFile(L"TEST.json");
6356
Adam Sawicki21017c62018-09-07 15:26:59 +02006357 VmaPoolStats stats = {};
6358 vmaGetPoolStats(g_hAllocator, pool, &stats);
6359 int DBG = 0; // Set breakpoint here to inspect `stats`.
6360
Adam Sawicki80927152018-09-07 17:27:23 +02006361 // Allocate enough new buffers to surely fall into second block.
6362 for(uint32_t i = 0; i < 32; ++i)
6363 {
6364 bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
6365 res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
6366 &newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
Adam Sawickib8d34d52018-10-03 17:41:20 +02006367 TEST(res == VK_SUCCESS);
Adam Sawicki80927152018-09-07 17:27:23 +02006368 bufInfo.push_back(newBufInfo);
6369 }
6370
6371 SaveAllocatorStatsToFile(L"BuddyTest01.json");
6372
Adam Sawickia83793a2018-09-03 13:40:42 +02006373 // Destroy the buffers in random order.
6374 while(!bufInfo.empty())
6375 {
6376 const size_t indexToDestroy = rand.Generate() % bufInfo.size();
6377 const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
6378 vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
6379 bufInfo.erase(bufInfo.begin() + indexToDestroy);
6380 }
6381
6382 vmaDestroyPool(g_hAllocator, pool);
6383}
6384
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006385static void BasicTestAllocatePages()
6386{
6387 wprintf(L"Basic test allocate pages\n");
6388
6389 RandomNumberGenerator rand{765461};
6390
6391 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
6392 sampleBufCreateInfo.size = 1024; // Whatever.
6393 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
6394
6395 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
6396 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6397
6398 VmaPoolCreateInfo poolCreateInfo = {};
6399 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
Adam Sawickia7d77692018-10-03 16:15:27 +02006400 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006401
6402 // 1 block of 1 MB.
6403 poolCreateInfo.blockSize = 1024 * 1024;
6404 poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
6405
6406 // Create pool.
6407 VmaPool pool = nullptr;
6408 res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
Adam Sawickia7d77692018-10-03 16:15:27 +02006409 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006410
6411 // Make 100 allocations of 4 KB - they should fit into the pool.
6412 VkMemoryRequirements memReq;
6413 memReq.memoryTypeBits = UINT32_MAX;
6414 memReq.alignment = 4 * 1024;
6415 memReq.size = 4 * 1024;
6416
6417 VmaAllocationCreateInfo allocCreateInfo = {};
6418 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
6419 allocCreateInfo.pool = pool;
6420
6421 constexpr uint32_t allocCount = 100;
6422
6423 std::vector<VmaAllocation> alloc{allocCount};
6424 std::vector<VmaAllocationInfo> allocInfo{allocCount};
6425 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006426 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006427 for(uint32_t i = 0; i < allocCount; ++i)
6428 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006429 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006430 allocInfo[i].pMappedData != nullptr &&
6431 allocInfo[i].deviceMemory == allocInfo[0].deviceMemory &&
6432 allocInfo[i].memoryType == allocInfo[0].memoryType);
6433 }
6434
6435 // Free the allocations.
6436 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6437 std::fill(alloc.begin(), alloc.end(), nullptr);
6438 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6439
6440 // Try to make 100 allocations of 100 KB. This call should fail due to not enough memory.
6441 // Also test optional allocationInfo = null.
6442 memReq.size = 100 * 1024;
6443 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), nullptr);
Adam Sawickia7d77692018-10-03 16:15:27 +02006444 TEST(res != VK_SUCCESS);
6445 TEST(std::find_if(alloc.begin(), alloc.end(), [](VmaAllocation alloc){ return alloc != VK_NULL_HANDLE; }) == alloc.end());
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006446
6447 // Make 100 allocations of 4 KB, but with required alignment of 128 KB. This should also fail.
6448 memReq.size = 4 * 1024;
6449 memReq.alignment = 128 * 1024;
6450 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &allocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006451 TEST(res != VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006452
6453 // Make 100 dedicated allocations of 4 KB.
6454 memReq.alignment = 4 * 1024;
6455 memReq.size = 4 * 1024;
6456
6457 VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};
6458 dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
6459 dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
6460 res = vmaAllocateMemoryPages(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, allocCount, alloc.data(), allocInfo.data());
Adam Sawickia7d77692018-10-03 16:15:27 +02006461 TEST(res == VK_SUCCESS);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006462 for(uint32_t i = 0; i < allocCount; ++i)
6463 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006464 TEST(alloc[i] != VK_NULL_HANDLE &&
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006465 allocInfo[i].pMappedData != nullptr &&
6466 allocInfo[i].memoryType == allocInfo[0].memoryType &&
6467 allocInfo[i].offset == 0);
6468 if(i > 0)
6469 {
Adam Sawickia7d77692018-10-03 16:15:27 +02006470 TEST(allocInfo[i].deviceMemory != allocInfo[0].deviceMemory);
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006471 }
6472 }
6473
6474 // Free the allocations.
6475 vmaFreeMemoryPages(g_hAllocator, allocCount, alloc.data());
6476 std::fill(alloc.begin(), alloc.end(), nullptr);
6477 std::fill(allocInfo.begin(), allocInfo.end(), VmaAllocationInfo{});
6478
6479 vmaDestroyPool(g_hAllocator, pool);
6480}
6481
Adam Sawickif2975342018-10-16 13:49:02 +02006482// Test the testing environment.
6483static void TestGpuData()
6484{
6485 RandomNumberGenerator rand = { 53434 };
6486
6487 std::vector<AllocInfo> allocInfo;
6488
6489 for(size_t i = 0; i < 100; ++i)
6490 {
6491 AllocInfo info = {};
6492
6493 info.m_BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
6494 info.m_BufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT |
6495 VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
6496 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
6497 info.m_BufferInfo.size = 1024 * 1024 * (rand.Generate() % 9 + 1);
6498
6499 VmaAllocationCreateInfo allocCreateInfo = {};
6500 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
6501
6502 VkResult res = vmaCreateBuffer(g_hAllocator, &info.m_BufferInfo, &allocCreateInfo, &info.m_Buffer, &info.m_Allocation, nullptr);
6503 TEST(res == VK_SUCCESS);
6504
6505 info.m_StartValue = rand.Generate();
6506
6507 allocInfo.push_back(std::move(info));
6508 }
6509
6510 UploadGpuData(allocInfo.data(), allocInfo.size());
6511
6512 ValidateGpuData(allocInfo.data(), allocInfo.size());
6513
6514 DestroyAllAllocations(allocInfo);
6515}
6516
Adam Sawickib8333fb2018-03-13 16:15:53 +01006517void Test()
6518{
6519 wprintf(L"TESTING:\n");
6520
Adam Sawicki48b8a332019-11-02 15:24:33 +01006521 if(false)
Adam Sawicki70a683e2018-08-24 15:36:32 +02006522 {
Adam Sawicki1a8424f2018-12-13 11:01:16 +01006523 ////////////////////////////////////////////////////////////////////////////////
6524 // Temporarily insert custom tests here:
Adam Sawicki70a683e2018-08-24 15:36:32 +02006525 return;
6526 }
6527
Adam Sawickib8333fb2018-03-13 16:15:53 +01006528 // # Simple tests
6529
6530 TestBasics();
Adam Sawickiaaa1a562020-06-24 17:41:09 +02006531 TestAllocationVersusResourceSize();
Adam Sawickif2975342018-10-16 13:49:02 +02006532 //TestGpuData(); // Not calling this because it's just testing the testing environment.
Adam Sawicki212a4a62018-06-14 15:44:45 +02006533#if VMA_DEBUG_MARGIN
6534 TestDebugMargin();
6535#else
6536 TestPool_SameSize();
Adam Sawickiddcbf8c2019-11-22 15:22:42 +01006537 TestPool_MinBlockCount();
Adam Sawicki212a4a62018-06-14 15:44:45 +02006538 TestHeapSizeLimit();
6539#endif
Adam Sawickie44c6262018-06-15 14:30:39 +02006540#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
6541 TestAllocationsInitialization();
6542#endif
Adam Sawickiefa88c42019-11-18 16:33:56 +01006543 TestMemoryUsage();
Adam Sawicki50882502020-02-07 16:51:31 +01006544 TestDeviceCoherentMemory();
Adam Sawicki40ffe982019-10-11 15:56:02 +02006545 TestBudget();
Adam Sawicki0620c8e2020-08-18 16:43:44 +02006546 TestAliasing();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006547 TestMapping();
Adam Sawickidaa6a552019-06-25 15:26:37 +02006548 TestDeviceLocalMapped();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006549 TestMappingMultithreaded();
Adam Sawicki0876c0d2018-06-20 15:18:11 +02006550 TestLinearAllocator();
Adam Sawicki8cfe05f2018-08-22 16:48:17 +02006551 ManuallyTestLinearAllocator();
Adam Sawicki70a683e2018-08-24 15:36:32 +02006552 TestLinearAllocatorMultiBlock();
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006553
Adam Sawicki4338f662018-09-07 14:12:37 +02006554 BasicTestBuddyAllocator();
Adam Sawicki2e4d3ef2018-10-03 15:48:17 +02006555 BasicTestAllocatePages();
Adam Sawicki4338f662018-09-07 14:12:37 +02006556
Adam Sawickie73e9882020-03-20 18:05:42 +01006557 if(g_BufferDeviceAddressEnabled)
6558 TestBufferDeviceAddress();
Adam Sawickif2012052021-01-11 18:04:42 +01006559 if(VK_EXT_memory_priority_enabled)
6560 TestMemoryPriority();
Adam Sawickie73e9882020-03-20 18:05:42 +01006561
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006562 {
6563 FILE* file;
Adam Sawickic6432d12018-09-21 16:44:16 +02006564 fopen_s(&file, "Algorithms.csv", "w");
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006565 assert(file != NULL);
Adam Sawicki80927152018-09-07 17:27:23 +02006566 BenchmarkAlgorithms(file);
Adam Sawicki33d2ce72018-08-27 13:59:13 +02006567 fclose(file);
6568 }
6569
Adam Sawickib8333fb2018-03-13 16:15:53 +01006570 TestDefragmentationSimple();
6571 TestDefragmentationFull();
Adam Sawicki52076eb2018-11-22 16:14:50 +01006572 TestDefragmentationWholePool();
Adam Sawicki9a4f5082018-11-23 17:26:05 +01006573 TestDefragmentationGpu();
Adam Sawickia52012d2019-12-23 15:28:51 +01006574 TestDefragmentationIncrementalBasic();
6575 TestDefragmentationIncrementalComplex();
Adam Sawickib8333fb2018-03-13 16:15:53 +01006576
6577 // # Detailed tests
6578 FILE* file;
6579 fopen_s(&file, "Results.csv", "w");
6580 assert(file != NULL);
6581
6582 WriteMainTestResultHeader(file);
6583 PerformMainTests(file);
6584 //PerformCustomMainTest(file);
6585
6586 WritePoolTestResultHeader(file);
6587 PerformPoolTests(file);
6588 //PerformCustomPoolTest(file);
6589
6590 fclose(file);
Adam Sawicki4ac8ff82019-11-18 14:47:33 +01006591
Adam Sawicki7e56c482021-02-23 15:27:24 +01006592 wprintf(L"Done, all PASSED.\n");
Adam Sawickib8333fb2018-03-13 16:15:53 +01006593}
6594
Adam Sawickif1a793c2018-03-13 15:42:22 +01006595#endif // #ifdef _WIN32