• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2018-2021 Advanced Micro Devices, Inc. All rights reserved.
3 //
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 
23 #include "VmaUsage.h"
24 #include "Common.h"
25 #include "Constants.h"
26 #include <unordered_map>
27 #include <map>
28 #include <algorithm>
29 
30 static VERBOSITY g_Verbosity = VERBOSITY::DEFAULT;
31 
32 static const uint32_t VULKAN_API_VERSION = VK_API_VERSION_1_1;
33 
34 namespace DetailedStats
35 {
36 
37 struct Flag
38 {
39     uint32_t setCount = 0;
40 
PostValueDetailedStats::Flag41     void PostValue(bool v)
42     {
43         if(v)
44         {
45             ++setCount;
46         }
47     }
48 
PrintDetailedStats::Flag49     void Print(uint32_t totalCount) const
50     {
51         if(setCount)
52         {
53             printf(" %u (%.2f%%)\n", setCount, (double)setCount * 100.0 / (double)totalCount);
54         }
55         else
56         {
57             printf(" 0\n");
58         }
59     }
60 };
61 
62 struct Enum
63 {
EnumDetailedStats::Enum64     Enum(size_t itemCount, const char* const* itemNames, const uint32_t* itemValues = nullptr) :
65         m_ItemCount(itemCount),
66         m_ItemNames(itemNames),
67         m_ItemValues(itemValues)
68     {
69     }
70 
PostValueDetailedStats::Enum71     void PostValue(uint32_t v)
72     {
73         if(v < _countof(m_BaseCount))
74         {
75             ++m_BaseCount[v];
76         }
77         else
78         {
79             auto it = m_ExtendedCount.find(v);
80             if(it != m_ExtendedCount.end())
81             {
82                 ++it->second;
83             }
84             else
85             {
86                 m_ExtendedCount.insert(std::make_pair(v, 1u));
87             }
88         }
89     }
90 
PrintDetailedStats::Enum91     void Print(uint32_t totalCount) const
92     {
93         if(totalCount &&
94             (!m_ExtendedCount.empty() || std::count_if(m_BaseCount, m_BaseCount + _countof(m_BaseCount), [](uint32_t v) { return v > 0; })))
95         {
96             printf("\n");
97 
98             for(size_t i = 0; i < _countof(m_BaseCount); ++i)
99             {
100                 const uint32_t currCount = m_BaseCount[i];
101                 if(currCount)
102                 {
103                     PrintItem((uint32_t)i, currCount, totalCount);
104                 }
105             }
106 
107             for(const auto& it : m_ExtendedCount)
108             {
109                 PrintItem(it.first, it.second, totalCount);
110             }
111         }
112         else
113         {
114             printf(" 0\n");
115         }
116     }
117 
118 private:
119     const size_t m_ItemCount;
120     const char* const* const m_ItemNames;
121     const uint32_t* const m_ItemValues;
122 
123     uint32_t m_BaseCount[32] = {};
124     std::map<uint32_t, uint32_t> m_ExtendedCount;
125 
PrintItemDetailedStats::Enum126     void PrintItem(uint32_t value, uint32_t count, uint32_t totalCount) const
127     {
128         size_t itemIndex = m_ItemCount;
129         if(m_ItemValues)
130         {
131             for(itemIndex = 0; itemIndex < m_ItemCount; ++itemIndex)
132             {
133                 if(m_ItemValues[itemIndex] == value)
134                 {
135                     break;
136                 }
137             }
138         }
139         else
140         {
141             if(value < m_ItemCount)
142             {
143                 itemIndex = value;
144             }
145         }
146 
147         if(itemIndex < m_ItemCount)
148         {
149             printf("        %s: ", m_ItemNames[itemIndex]);
150         }
151         else
152         {
153             printf("        0x%X: ", value);
154         }
155 
156         printf("%u (%.2f%%)\n", count, (double)count * 100.0 / (double)totalCount);
157     }
158 };
159 
160 struct FlagSet
161 {
162     uint32_t count[32] = {};
163 
FlagSetDetailedStats::FlagSet164     FlagSet(size_t count, const char* const* names, const uint32_t* values = nullptr) :
165         m_Count(count),
166         m_Names(names),
167         m_Values(values)
168     {
169     }
170 
PostValueDetailedStats::FlagSet171     void PostValue(uint32_t v)
172     {
173         for(size_t i = 0; i < 32; ++i)
174         {
175             if((v & (1u << i)) != 0)
176             {
177                 ++count[i];
178             }
179         }
180     }
181 
PrintDetailedStats::FlagSet182     void Print(uint32_t totalCount) const
183     {
184         if(totalCount &&
185             std::count_if(count, count + _countof(count), [](uint32_t v) { return v > 0; }))
186         {
187             printf("\n");
188             for(uint32_t bitIndex = 0; bitIndex < 32; ++bitIndex)
189             {
190                 const uint32_t currCount = count[bitIndex];
191                 if(currCount)
192                 {
193                     size_t itemIndex = m_Count;
194                     if(m_Values)
195                     {
196                         for(itemIndex = 0; itemIndex < m_Count; ++itemIndex)
197                         {
198                             if(m_Values[itemIndex] == (1u << bitIndex))
199                             {
200                                 break;
201                             }
202                         }
203                     }
204                     else
205                     {
206                         if(bitIndex < m_Count)
207                         {
208                             itemIndex = bitIndex;
209                         }
210                     }
211 
212                     if(itemIndex < m_Count)
213                     {
214                         printf("        %s: ", m_Names[itemIndex]);
215                     }
216                     else
217                     {
218                         printf("        0x%X: ", 1u << bitIndex);
219                     }
220 
221                     printf("%u (%.2f%%)\n", currCount, (double)currCount * 100.0 / (double)totalCount);
222                 }
223             }
224         }
225         else
226         {
227             printf(" 0\n");
228         }
229     }
230 
231 private:
232     const size_t m_Count;
233     const char* const* const m_Names;
234     const uint32_t* const m_Values;
235 };
236 
237 // T should be unsigned int
238 template<typename T>
239 struct MinMaxAvg
240 {
241     T min = std::numeric_limits<T>::max();
242     T max = 0;
243     T sum = T();
244 
PostValueDetailedStats::MinMaxAvg245     void PostValue(T v)
246     {
247         this->min = std::min(this->min, v);
248         this->max = std::max(this->max, v);
249         sum += v;
250     }
251 
PrintDetailedStats::MinMaxAvg252     void Print(uint32_t totalCount) const
253     {
254         if(totalCount && sum > T())
255         {
256             if(this->min == this->max)
257             {
258                 printf(" %llu\n", (uint64_t)this->max);
259             }
260             else
261             {
262                 printf("\n        Min: %llu\n        Max: %llu\n        Avg: %llu\n",
263                     (uint64_t)this->min,
264                     (uint64_t)this->max,
265                     round_div<uint64_t>(this->sum, totalCount));
266             }
267         }
268         else
269         {
270             printf(" 0\n");
271         }
272     }
273 };
274 
275 template<typename T>
276 struct BitMask
277 {
278     uint32_t zeroCount = 0;
279     uint32_t maxCount = 0;
280 
PostValueDetailedStats::BitMask281     void PostValue(T v)
282     {
283         if(v)
284         {
285             if(v == std::numeric_limits<T>::max())
286             {
287                 ++maxCount;
288             }
289         }
290         else
291         {
292             ++zeroCount;
293         }
294     }
295 
PrintDetailedStats::BitMask296     void Print(uint32_t totalCount) const
297     {
298         if(totalCount > 0 && zeroCount < totalCount)
299         {
300             const uint32_t otherCount = totalCount - (zeroCount + maxCount);
301 
302             printf("\n        0: %u (%.2f%%)\n        Max: %u (%.2f%%)\n        Other: %u (%.2f%%)\n",
303                 zeroCount,  (double)zeroCount  * 100.0 / (double)totalCount,
304                 maxCount,   (double)maxCount   * 100.0 / (double)totalCount,
305                 otherCount, (double)otherCount * 100.0 / (double)totalCount);
306         }
307         else
308         {
309             printf(" 0\n");
310         }
311     }
312 };
313 
314 struct CountPerMemType
315 {
316     uint32_t count[VK_MAX_MEMORY_TYPES] = {};
317 
PostValueDetailedStats::CountPerMemType318     void PostValue(uint32_t v)
319     {
320         for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
321         {
322             if((v & (1u << i)) != 0)
323             {
324                 ++count[i];
325             }
326         }
327     }
328 
PrintDetailedStats::CountPerMemType329     void Print(uint32_t totalCount) const
330     {
331         if(totalCount)
332         {
333             printf("\n");
334             for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
335             {
336                 if(count[i])
337                 {
338                     printf("        %u: %u (%.2f%%)\n", i, count[i],
339                         (double)count[i] * 100.0 / (double)totalCount);
340                 }
341             }
342         }
343         else
344         {
345             printf(" 0\n");
346         }
347     }
348 };
349 
350 struct StructureStats
351 {
352     uint32_t totalCount = 0;
353 };
354 
355 #define PRINT_FIELD(name) \
356     printf("    " #name ":"); \
357     (name).Print(totalCount);
358 #define PRINT_FIELD_NAMED(name, nameStr) \
359     printf("    " nameStr ":"); \
360     (name).Print(totalCount);
361 
362 struct VmaPoolCreateInfoStats : public StructureStats
363 {
364     CountPerMemType memoryTypeIndex;
365     FlagSet flags;
366     MinMaxAvg<VkDeviceSize> blockSize;
367     MinMaxAvg<size_t> minBlockCount;
368     MinMaxAvg<size_t> maxBlockCount;
369     Flag minMaxBlockCountEqual;
370     MinMaxAvg<uint32_t> frameInUseCount;
371 
VmaPoolCreateInfoStatsDetailedStats::VmaPoolCreateInfoStats372     VmaPoolCreateInfoStats() :
373         flags(VMA_POOL_CREATE_FLAG_COUNT, VMA_POOL_CREATE_FLAG_NAMES, VMA_POOL_CREATE_FLAG_VALUES)
374     {
375     }
376 
PostValueDetailedStats::VmaPoolCreateInfoStats377     void PostValue(const VmaPoolCreateInfo& v)
378     {
379         ++totalCount;
380 
381         memoryTypeIndex.PostValue(v.memoryTypeIndex);
382         flags.PostValue(v.flags);
383         blockSize.PostValue(v.blockSize);
384         minBlockCount.PostValue(v.minBlockCount);
385         maxBlockCount.PostValue(v.maxBlockCount);
386         minMaxBlockCountEqual.PostValue(v.minBlockCount == v.maxBlockCount);
387         frameInUseCount.PostValue(v.frameInUseCount);
388     }
389 
PrintDetailedStats::VmaPoolCreateInfoStats390     void Print() const
391     {
392         if(totalCount == 0)
393         {
394             return;
395         }
396 
397         printf("VmaPoolCreateInfo (%u):\n", totalCount);
398 
399         PRINT_FIELD(memoryTypeIndex);
400         PRINT_FIELD(flags);
401         PRINT_FIELD(blockSize);
402         PRINT_FIELD(minBlockCount);
403         PRINT_FIELD(maxBlockCount);
404         PRINT_FIELD_NAMED(minMaxBlockCountEqual, "minBlockCount == maxBlockCount");
405         PRINT_FIELD(frameInUseCount);
406     }
407 };
408 
409 struct VkBufferCreateInfoStats : public StructureStats
410 {
411     FlagSet flags;
412     MinMaxAvg<VkDeviceSize> size;
413     FlagSet usage;
414     Enum sharingMode;
415 
VkBufferCreateInfoStatsDetailedStats::VkBufferCreateInfoStats416     VkBufferCreateInfoStats() :
417         flags(VK_BUFFER_CREATE_FLAG_COUNT, VK_BUFFER_CREATE_FLAG_NAMES, VK_BUFFER_CREATE_FLAG_VALUES),
418         usage(VK_BUFFER_USAGE_FLAG_COUNT, VK_BUFFER_USAGE_FLAG_NAMES, VK_BUFFER_USAGE_FLAG_VALUES),
419         sharingMode(VK_SHARING_MODE_COUNT, VK_SHARING_MODE_NAMES)
420     {
421     }
422 
PostValueDetailedStats::VkBufferCreateInfoStats423     void PostValue(const VkBufferCreateInfo& v)
424     {
425         ++totalCount;
426 
427         flags.PostValue(v.flags);
428         size.PostValue(v.size);
429         usage.PostValue(v.usage);
430         sharingMode.PostValue(v.sharingMode);
431     }
432 
PrintDetailedStats::VkBufferCreateInfoStats433     void Print() const
434     {
435         if(totalCount == 0)
436         {
437             return;
438         }
439 
440         printf("VkBufferCreateInfo (%u):\n", totalCount);
441 
442         PRINT_FIELD(flags);
443         PRINT_FIELD(size);
444         PRINT_FIELD(usage);
445         PRINT_FIELD(sharingMode);
446     }
447 };
448 
449 struct VkImageCreateInfoStats : public StructureStats
450 {
451     FlagSet flags;
452     Enum imageType;
453     Enum format;
454     MinMaxAvg<uint32_t> width, height, depth, mipLevels, arrayLayers;
455     Flag depthGreaterThanOne, mipLevelsGreaterThanOne, arrayLayersGreaterThanOne;
456     Enum samples;
457     Enum tiling;
458     FlagSet usage;
459     Enum sharingMode;
460     Enum initialLayout;
461 
VkImageCreateInfoStatsDetailedStats::VkImageCreateInfoStats462     VkImageCreateInfoStats() :
463         flags(VK_IMAGE_CREATE_FLAG_COUNT, VK_IMAGE_CREATE_FLAG_NAMES, VK_IMAGE_CREATE_FLAG_VALUES),
464         imageType(VK_IMAGE_TYPE_COUNT, VK_IMAGE_TYPE_NAMES),
465         format(VK_FORMAT_COUNT, VK_FORMAT_NAMES, VK_FORMAT_VALUES),
466         samples(VK_SAMPLE_COUNT_COUNT, VK_SAMPLE_COUNT_NAMES, VK_SAMPLE_COUNT_VALUES),
467         tiling(VK_IMAGE_TILING_COUNT, VK_IMAGE_TILING_NAMES),
468         usage(VK_IMAGE_USAGE_FLAG_COUNT, VK_IMAGE_USAGE_FLAG_NAMES, VK_IMAGE_USAGE_FLAG_VALUES),
469         sharingMode(VK_SHARING_MODE_COUNT, VK_SHARING_MODE_NAMES),
470         initialLayout(VK_IMAGE_LAYOUT_COUNT, VK_IMAGE_LAYOUT_NAMES, VK_IMAGE_LAYOUT_VALUES)
471     {
472     }
473 
PostValueDetailedStats::VkImageCreateInfoStats474     void PostValue(const VkImageCreateInfo& v)
475     {
476         ++totalCount;
477 
478         flags.PostValue(v.flags);
479         imageType.PostValue(v.imageType);
480         format.PostValue(v.format);
481         width.PostValue(v.extent.width);
482         height.PostValue(v.extent.height);
483         depth.PostValue(v.extent.depth);
484         mipLevels.PostValue(v.mipLevels);
485         arrayLayers.PostValue(v.arrayLayers);
486         depthGreaterThanOne.PostValue(v.extent.depth > 1);
487         mipLevelsGreaterThanOne.PostValue(v.mipLevels > 1);
488         arrayLayersGreaterThanOne.PostValue(v.arrayLayers > 1);
489         samples.PostValue(v.samples);
490         tiling.PostValue(v.tiling);
491         usage.PostValue(v.usage);
492         sharingMode.PostValue(v.sharingMode);
493         initialLayout.PostValue(v.initialLayout);
494     }
495 
PrintDetailedStats::VkImageCreateInfoStats496     void Print() const
497     {
498         if(totalCount == 0)
499         {
500             return;
501         }
502 
503         printf("VkImageCreateInfo (%u):\n", totalCount);
504 
505         PRINT_FIELD(flags);
506         PRINT_FIELD(imageType);
507         PRINT_FIELD(format);
508         PRINT_FIELD(width);
509         PRINT_FIELD(height);
510         PRINT_FIELD(depth);
511         PRINT_FIELD(mipLevels);
512         PRINT_FIELD(arrayLayers);
513         PRINT_FIELD_NAMED(depthGreaterThanOne, "depth > 1");
514         PRINT_FIELD_NAMED(mipLevelsGreaterThanOne, "mipLevels > 1");
515         PRINT_FIELD_NAMED(arrayLayersGreaterThanOne, "arrayLayers > 1");
516         PRINT_FIELD(samples);
517         PRINT_FIELD(tiling);
518         PRINT_FIELD(usage);
519         PRINT_FIELD(sharingMode);
520         PRINT_FIELD(initialLayout);
521     }
522 };
523 
524 struct VmaAllocationCreateInfoStats : public StructureStats
525 {
526     FlagSet flags;
527     Enum usage;
528     FlagSet requiredFlags, preferredFlags;
529     Flag requiredFlagsNotZero, preferredFlagsNotZero;
530     BitMask<uint32_t> memoryTypeBits;
531     Flag poolNotNull;
532     Flag userDataNotNull;
533 
VmaAllocationCreateInfoStatsDetailedStats::VmaAllocationCreateInfoStats534     VmaAllocationCreateInfoStats() :
535         flags(VMA_ALLOCATION_CREATE_FLAG_COUNT, VMA_ALLOCATION_CREATE_FLAG_NAMES, VMA_ALLOCATION_CREATE_FLAG_VALUES),
536         usage(VMA_MEMORY_USAGE_COUNT, VMA_MEMORY_USAGE_NAMES),
537         requiredFlags(VK_MEMORY_PROPERTY_FLAG_COUNT, VK_MEMORY_PROPERTY_FLAG_NAMES, VK_MEMORY_PROPERTY_FLAG_VALUES),
538         preferredFlags(VK_MEMORY_PROPERTY_FLAG_COUNT, VK_MEMORY_PROPERTY_FLAG_NAMES, VK_MEMORY_PROPERTY_FLAG_VALUES)
539     {
540     }
541 
PostValueDetailedStats::VmaAllocationCreateInfoStats542     void PostValue(const VmaAllocationCreateInfo& v, size_t count = 1)
543     {
544         totalCount += (uint32_t)count;
545 
546         for(size_t i = 0; i < count; ++i)
547         {
548             flags.PostValue(v.flags);
549             usage.PostValue(v.usage);
550             requiredFlags.PostValue(v.requiredFlags);
551             preferredFlags.PostValue(v.preferredFlags);
552             requiredFlagsNotZero.PostValue(v.requiredFlags != 0);
553             preferredFlagsNotZero.PostValue(v.preferredFlags != 0);
554             memoryTypeBits.PostValue(v.memoryTypeBits);
555             poolNotNull.PostValue(v.pool != VK_NULL_HANDLE);
556             userDataNotNull.PostValue(v.pUserData != nullptr);
557         }
558     }
559 
PrintDetailedStats::VmaAllocationCreateInfoStats560     void Print() const
561     {
562         if(totalCount == 0)
563         {
564             return;
565         }
566 
567         printf("VmaAllocationCreateInfo (%u):\n", totalCount);
568 
569         PRINT_FIELD(flags);
570         PRINT_FIELD(usage);
571         PRINT_FIELD(requiredFlags);
572         PRINT_FIELD(preferredFlags);
573         PRINT_FIELD_NAMED(requiredFlagsNotZero, "requiredFlags != 0");
574         PRINT_FIELD_NAMED(preferredFlagsNotZero, "preferredFlags != 0");
575         PRINT_FIELD(memoryTypeBits);
576         PRINT_FIELD_NAMED(poolNotNull, "pool != VK_NULL_HANDLE");
577         PRINT_FIELD_NAMED(userDataNotNull, "pUserData != nullptr");
578     }
579 };
580 
581 struct VmaAllocateMemoryPagesStats : public StructureStats
582 {
583     MinMaxAvg<size_t> allocationCount;
584 
PostValueDetailedStats::VmaAllocateMemoryPagesStats585     void PostValue(size_t allocationCount)
586     {
587         this->allocationCount.PostValue(allocationCount);
588     }
589 
PrintDetailedStats::VmaAllocateMemoryPagesStats590     void Print() const
591     {
592         if(totalCount == 0)
593         {
594             return;
595         }
596 
597         printf("vmaAllocateMemoryPages (%u):\n", totalCount);
598 
599         PRINT_FIELD(allocationCount);
600     }
601 };
602 
603 struct VmaDefragmentationInfo2Stats : public StructureStats
604 {
605     BitMask<VkDeviceSize> maxCpuBytesToMove;
606     BitMask<uint32_t> maxCpuAllocationsToMove;
607     BitMask<VkDeviceSize> maxGpuBytesToMove;
608     BitMask<uint32_t> maxGpuAllocationsToMove;
609     Flag commandBufferNotNull;
610     MinMaxAvg<uint32_t> allocationCount;
611     Flag allocationCountNotZero;
612     MinMaxAvg<uint32_t> poolCount;
613     Flag poolCountNotZero;
614 
PostValueDetailedStats::VmaDefragmentationInfo2Stats615     void PostValue(const VmaDefragmentationInfo2& info)
616     {
617         ++totalCount;
618 
619         maxCpuBytesToMove.PostValue(info.maxCpuBytesToMove);
620         maxCpuAllocationsToMove.PostValue(info.maxCpuAllocationsToMove);
621         maxGpuBytesToMove.PostValue(info.maxGpuBytesToMove);
622         maxGpuAllocationsToMove.PostValue(info.maxGpuAllocationsToMove);
623         commandBufferNotNull.PostValue(info.commandBuffer != VK_NULL_HANDLE);
624         allocationCount.PostValue(info.allocationCount);
625         allocationCountNotZero.PostValue(info.allocationCount != 0);
626         poolCount.PostValue(info.poolCount);
627         poolCountNotZero.PostValue(info.poolCount != 0);
628     }
629 
PrintDetailedStats::VmaDefragmentationInfo2Stats630     void Print() const
631     {
632         if(totalCount == 0)
633         {
634             return;
635         }
636 
637         printf("VmaDefragmentationInfo2 (%u):\n", totalCount);
638 
639         PRINT_FIELD(maxCpuBytesToMove);
640         PRINT_FIELD(maxCpuAllocationsToMove);
641         PRINT_FIELD(maxGpuBytesToMove);
642         PRINT_FIELD(maxGpuAllocationsToMove);
643         PRINT_FIELD_NAMED(commandBufferNotNull, "commandBuffer != VK_NULL_HANDLE");
644         PRINT_FIELD(allocationCount);
645         PRINT_FIELD_NAMED(allocationCountNotZero, "allocationCount > 0");
646         PRINT_FIELD(poolCount);
647         PRINT_FIELD_NAMED(poolCountNotZero, "poolCount > 0");
648     }
649 };
650 
651 #undef PRINT_FIELD_NAMED
652 #undef PRINT_FIELD
653 
654 } // namespace DetailedStats
655 
656 // Set this to false to disable deleting leaked VmaAllocation, VmaPool objects
657 // and let VMA report asserts about them.
658 static const bool CLEANUP_LEAKED_OBJECTS = true;
659 
660 static std::string g_FilePath;
661 // Most significant 16 bits are major version, least significant 16 bits are minor version.
662 static uint32_t g_FileVersion;
663 
MakeVersion(uint32_t major,uint32_t minor)664 inline uint32_t MakeVersion(uint32_t major, uint32_t minor) { return (major << 16) | minor; }
GetVersionMajor(uint32_t version)665 inline uint32_t GetVersionMajor(uint32_t version) { return version >> 16; }
GetVersionMinor(uint32_t version)666 inline uint32_t GetVersionMinor(uint32_t version) { return version & 0xFFFF; }
667 
668 static size_t g_IterationCount = 1;
669 static uint32_t g_PhysicalDeviceIndex = 0;
670 static RangeSequence<size_t> g_LineRanges;
671 static bool g_UserDataEnabled = true;
672 static bool g_MemStatsEnabled = false;
673 VULKAN_EXTENSION_REQUEST g_VK_LAYER_KHRONOS_validation           = VULKAN_EXTENSION_REQUEST::DEFAULT;
674 VULKAN_EXTENSION_REQUEST g_VK_EXT_memory_budget_request          = VULKAN_EXTENSION_REQUEST::DEFAULT;
675 VULKAN_EXTENSION_REQUEST g_VK_AMD_device_coherent_memory_request = VULKAN_EXTENSION_REQUEST::DEFAULT;
676 
677 struct StatsAfterLineEntry
678 {
679     size_t line;
680     bool detailed;
681 
operator <StatsAfterLineEntry682     bool operator<(const StatsAfterLineEntry& rhs) const { return line < rhs.line; }
operator ==StatsAfterLineEntry683     bool operator==(const StatsAfterLineEntry& rhs) const { return line == rhs.line; }
684 };
685 static std::vector<StatsAfterLineEntry> g_DumpStatsAfterLine;
686 static std::vector<size_t> g_DefragmentAfterLine;
687 static uint32_t g_DefragmentationFlags = 0;
688 static size_t g_DumpStatsAfterLineNextIndex = 0;
689 static size_t g_DefragmentAfterLineNextIndex = 0;
690 
ValidateFileVersion()691 static bool ValidateFileVersion()
692 {
693     if(GetVersionMajor(g_FileVersion) == 1 &&
694         GetVersionMinor(g_FileVersion) <= 8)
695     {
696         return true;
697     }
698 
699     return false;
700 }
701 
ParseFileVersion(const StrRange & s)702 static bool ParseFileVersion(const StrRange& s)
703 {
704     CsvSplit csvSplit;
705     csvSplit.Set(s, 2);
706     uint32_t major, minor;
707     if(csvSplit.GetCount() == 2 &&
708         StrRangeToUint(csvSplit.GetRange(0), major) &&
709         StrRangeToUint(csvSplit.GetRange(1), minor))
710     {
711         g_FileVersion = (major << 16) | minor;
712         return true;
713     }
714     else
715     {
716         return false;
717     }
718 }
719 
720 ////////////////////////////////////////////////////////////////////////////////
721 // class Statistics
722 
723 class Statistics
724 {
725 public:
726     static uint32_t BufferUsageToClass(uint32_t usage);
727     static uint32_t ImageUsageToClass(uint32_t usage);
728 
729     Statistics();
730     ~Statistics();
731     void Init(uint32_t memHeapCount, uint32_t memTypeCount);
732     void PrintDeviceMemStats() const;
733     void PrintMemStats() const;
734     void PrintDetailedStats() const;
735 
GetFunctionCallCount() const736     const size_t* GetFunctionCallCount() const { return m_FunctionCallCount; }
GetImageCreationCount(uint32_t imgClass) const737     size_t GetImageCreationCount(uint32_t imgClass) const { return m_ImageCreationCount[imgClass]; }
GetLinearImageCreationCount() const738     size_t GetLinearImageCreationCount() const { return m_LinearImageCreationCount; }
GetBufferCreationCount(uint32_t bufClass) const739     size_t GetBufferCreationCount(uint32_t bufClass) const { return m_BufferCreationCount[bufClass]; }
GetAllocationCreationCount() const740     size_t GetAllocationCreationCount() const { return (size_t)m_VmaAllocationCreateInfo.totalCount + m_CreateLostAllocationCount; }
GetPoolCreationCount() const741     size_t GetPoolCreationCount() const { return m_VmaPoolCreateInfo.totalCount; }
GetBufferCreationCount() const742     size_t GetBufferCreationCount() const { return (size_t)m_VkBufferCreateInfo.totalCount; }
743 
744     void RegisterFunctionCall(VMA_FUNCTION func);
745     void RegisterCreateImage(const VkImageCreateInfo& info);
746     void RegisterCreateBuffer(const VkBufferCreateInfo& info);
747     void RegisterCreatePool(const VmaPoolCreateInfo& info);
748     void RegisterCreateAllocation(const VmaAllocationCreateInfo& info, size_t allocCount = 1);
RegisterCreateLostAllocation()749     void RegisterCreateLostAllocation() { ++m_CreateLostAllocationCount; }
RegisterAllocateMemoryPages(size_t allocCount)750     void RegisterAllocateMemoryPages(size_t allocCount) { m_VmaAllocateMemoryPages.PostValue(allocCount); }
751     void RegisterDefragmentation(const VmaDefragmentationInfo2& info);
752 
753     void RegisterDeviceMemoryAllocation(uint32_t memoryType, VkDeviceSize size);
754     void UpdateMemStats(const VmaStats& currStats);
755 
756 private:
757     uint32_t m_MemHeapCount = 0;
758     uint32_t m_MemTypeCount = 0;
759 
760     size_t m_FunctionCallCount[(size_t)VMA_FUNCTION::Count] = {};
761     size_t m_ImageCreationCount[4] = { };
762     size_t m_LinearImageCreationCount = 0;
763     size_t m_BufferCreationCount[4] = { };
764 
765     struct DeviceMemStatInfo
766     {
767         size_t allocationCount;
768         VkDeviceSize allocationTotalSize;
769     };
770     struct DeviceMemStats
771     {
772         DeviceMemStatInfo memoryType[VK_MAX_MEMORY_TYPES];
773         DeviceMemStatInfo total;
774     } m_DeviceMemStats;
775 
776     // Structure similar to VmaStatInfo, but not the same.
777     struct MemStatInfo
778     {
779         uint32_t blockCount;
780         uint32_t allocationCount;
781         uint32_t unusedRangeCount;
782         VkDeviceSize usedBytes;
783         VkDeviceSize unusedBytes;
784         VkDeviceSize totalBytes;
785     };
786     struct MemStats
787     {
788         MemStatInfo memoryType[VK_MAX_MEMORY_TYPES];
789         MemStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
790         MemStatInfo total;
791     } m_PeakMemStats;
792 
793     DetailedStats::VmaPoolCreateInfoStats m_VmaPoolCreateInfo;
794     DetailedStats::VkBufferCreateInfoStats m_VkBufferCreateInfo;
795     DetailedStats::VkImageCreateInfoStats m_VkImageCreateInfo;
796     DetailedStats::VmaAllocationCreateInfoStats m_VmaAllocationCreateInfo;
797     size_t m_CreateLostAllocationCount = 0;
798     DetailedStats::VmaAllocateMemoryPagesStats m_VmaAllocateMemoryPages;
799     DetailedStats::VmaDefragmentationInfo2Stats m_VmaDefragmentationInfo2;
800 
801     void UpdateMemStatInfo(MemStatInfo& inoutPeakInfo, const VmaStatInfo& currInfo);
802     static void PrintMemStatInfo(const MemStatInfo& info);
803 };
804 
805 // Hack for global AllocateDeviceMemoryCallback.
806 static Statistics* g_Statistics;
807 
AllocateDeviceMemoryCallback(VmaAllocator allocator,uint32_t memoryType,VkDeviceMemory memory,VkDeviceSize size,void * pUserData)808 static void VKAPI_CALL AllocateDeviceMemoryCallback(
809     VmaAllocator      allocator,
810     uint32_t          memoryType,
811     VkDeviceMemory    memory,
812     VkDeviceSize      size,
813     void*             pUserData)
814 {
815     g_Statistics->RegisterDeviceMemoryAllocation(memoryType, size);
816 }
817 
818 /// Callback function called before vkFreeMemory.
FreeDeviceMemoryCallback(VmaAllocator allocator,uint32_t memoryType,VkDeviceMemory memory,VkDeviceSize size,void * pUserData)819 static void VKAPI_CALL FreeDeviceMemoryCallback(
820     VmaAllocator      allocator,
821     uint32_t          memoryType,
822     VkDeviceMemory    memory,
823     VkDeviceSize      size,
824     void*             pUserData)
825 {
826     // Nothing.
827 }
828 
BufferUsageToClass(uint32_t usage)829 uint32_t Statistics::BufferUsageToClass(uint32_t usage)
830 {
831     // Buffer is used as source of data for fixed-function stage of graphics pipeline.
832     // It's indirect, vertex, or index buffer.
833     if ((usage & (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT |
834         VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
835         VK_BUFFER_USAGE_INDEX_BUFFER_BIT)) != 0)
836     {
837         return 0;
838     }
839     // Buffer is accessed by shaders for load/store/atomic.
840     // Aka "UAV"
841     else if ((usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
842         VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) != 0)
843     {
844         return 1;
845     }
846     // Buffer is accessed by shaders for reading uniform data.
847     // Aka "constant buffer"
848     else if ((usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
849     VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)) != 0)
850     {
851         return 2;
852     }
853     // Any other type of buffer.
854     // Notice that VK_BUFFER_USAGE_TRANSFER_SRC_BIT and VK_BUFFER_USAGE_TRANSFER_DST_BIT
855     // flags are intentionally ignored.
856     else
857     {
858         return 3;
859     }
860 }
861 
ImageUsageToClass(uint32_t usage)862 uint32_t Statistics::ImageUsageToClass(uint32_t usage)
863 {
864     // Image is used as depth/stencil "texture/surface".
865     if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0)
866     {
867         return 0;
868     }
869     // Image is used as other type of attachment.
870     // Aka "render target"
871     else if ((usage & (VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
872         VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
873         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) != 0)
874     {
875         return 1;
876     }
877     // Image is accessed by shaders for sampling.
878     // Aka "texture"
879     else if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)
880     {
881         return 2;
882     }
883     // Any other type of image.
884     // Notice that VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_TRANSFER_DST_BIT
885     // flags are intentionally ignored.
886     else
887     {
888         return 3;
889     }
890 }
891 
Statistics()892 Statistics::Statistics()
893 {
894     ZeroMemory(&m_DeviceMemStats, sizeof(m_DeviceMemStats));
895     ZeroMemory(&m_PeakMemStats, sizeof(m_PeakMemStats));
896 
897     assert(g_Statistics == nullptr);
898     g_Statistics = this;
899 }
900 
~Statistics()901 Statistics::~Statistics()
902 {
903     assert(g_Statistics == this);
904     g_Statistics = nullptr;
905 }
906 
Init(uint32_t memHeapCount,uint32_t memTypeCount)907 void Statistics::Init(uint32_t memHeapCount, uint32_t memTypeCount)
908 {
909     m_MemHeapCount = memHeapCount;
910     m_MemTypeCount = memTypeCount;
911 }
912 
PrintDeviceMemStats() const913 void Statistics::PrintDeviceMemStats() const
914 {
915     printf("Successful device memory allocations:\n");
916     printf("    Total: count = %zu, total size = %llu\n",
917         m_DeviceMemStats.total.allocationCount, m_DeviceMemStats.total.allocationTotalSize);
918     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
919     {
920         printf("    Memory type %u: count = %zu, total size = %llu\n",
921             i, m_DeviceMemStats.memoryType[i].allocationCount, m_DeviceMemStats.memoryType[i].allocationTotalSize);
922     }
923 }
924 
PrintMemStats() const925 void Statistics::PrintMemStats() const
926 {
927     printf("Memory statistics:\n");
928 
929     printf("    Total:\n");
930     PrintMemStatInfo(m_PeakMemStats.total);
931 
932     for(uint32_t i = 0; i < m_MemHeapCount; ++i)
933     {
934         const MemStatInfo& info = m_PeakMemStats.memoryHeap[i];
935         if(info.blockCount > 0 || info.totalBytes > 0)
936         {
937             printf("    Heap %u:\n", i);
938             PrintMemStatInfo(info);
939         }
940     }
941 
942     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
943     {
944         const MemStatInfo& info = m_PeakMemStats.memoryType[i];
945         if(info.blockCount > 0 || info.totalBytes > 0)
946         {
947             printf("    Type %u:\n", i);
948             PrintMemStatInfo(info);
949         }
950     }
951 }
952 
PrintDetailedStats() const953 void Statistics::PrintDetailedStats() const
954 {
955     m_VmaPoolCreateInfo.Print();
956     m_VmaAllocationCreateInfo.Print();
957     m_VmaAllocateMemoryPages.Print();
958     m_VkBufferCreateInfo.Print();
959     m_VkImageCreateInfo.Print();
960     m_VmaDefragmentationInfo2.Print();
961 }
962 
RegisterFunctionCall(VMA_FUNCTION func)963 void Statistics::RegisterFunctionCall(VMA_FUNCTION func)
964 {
965     ++m_FunctionCallCount[(size_t)func];
966 }
967 
RegisterCreateImage(const VkImageCreateInfo & info)968 void Statistics::RegisterCreateImage(const VkImageCreateInfo& info)
969 {
970     if(info.tiling == VK_IMAGE_TILING_LINEAR)
971         ++m_LinearImageCreationCount;
972     else
973     {
974         const uint32_t imgClass = ImageUsageToClass(info.usage);
975         ++m_ImageCreationCount[imgClass];
976     }
977 
978     m_VkImageCreateInfo.PostValue(info);
979 }
980 
RegisterCreateBuffer(const VkBufferCreateInfo & info)981 void Statistics::RegisterCreateBuffer(const VkBufferCreateInfo& info)
982 {
983     const uint32_t bufClass = BufferUsageToClass(info.usage);
984     ++m_BufferCreationCount[bufClass];
985 
986     m_VkBufferCreateInfo.PostValue(info);
987 }
988 
RegisterCreatePool(const VmaPoolCreateInfo & info)989 void Statistics::RegisterCreatePool(const VmaPoolCreateInfo& info)
990 {
991     m_VmaPoolCreateInfo.PostValue(info);
992 }
993 
RegisterCreateAllocation(const VmaAllocationCreateInfo & info,size_t allocCount)994 void Statistics::RegisterCreateAllocation(const VmaAllocationCreateInfo& info, size_t allocCount)
995 {
996     m_VmaAllocationCreateInfo.PostValue(info, allocCount);
997 }
998 
RegisterDefragmentation(const VmaDefragmentationInfo2 & info)999 void Statistics::RegisterDefragmentation(const VmaDefragmentationInfo2& info)
1000 {
1001     m_VmaDefragmentationInfo2.PostValue(info);
1002 }
1003 
UpdateMemStats(const VmaStats & currStats)1004 void Statistics::UpdateMemStats(const VmaStats& currStats)
1005 {
1006     UpdateMemStatInfo(m_PeakMemStats.total, currStats.total);
1007 
1008     for(uint32_t i = 0; i < m_MemHeapCount; ++i)
1009     {
1010         UpdateMemStatInfo(m_PeakMemStats.memoryHeap[i], currStats.memoryHeap[i]);
1011     }
1012 
1013     for(uint32_t i = 0; i < m_MemTypeCount; ++i)
1014     {
1015         UpdateMemStatInfo(m_PeakMemStats.memoryType[i], currStats.memoryType[i]);
1016     }
1017 }
1018 
RegisterDeviceMemoryAllocation(uint32_t memoryType,VkDeviceSize size)1019 void Statistics::RegisterDeviceMemoryAllocation(uint32_t memoryType, VkDeviceSize size)
1020 {
1021     ++m_DeviceMemStats.total.allocationCount;
1022     m_DeviceMemStats.total.allocationTotalSize += size;
1023 
1024     ++m_DeviceMemStats.memoryType[memoryType].allocationCount;
1025     m_DeviceMemStats.memoryType[memoryType].allocationTotalSize += size;
1026 }
1027 
UpdateMemStatInfo(MemStatInfo & inoutPeakInfo,const VmaStatInfo & currInfo)1028 void Statistics::UpdateMemStatInfo(MemStatInfo& inoutPeakInfo, const VmaStatInfo& currInfo)
1029 {
1030 #define SET_PEAK(inoutDst, src) \
1031     if((src) > (inoutDst)) \
1032     { \
1033         (inoutDst) = (src); \
1034     }
1035 
1036     SET_PEAK(inoutPeakInfo.blockCount, currInfo.blockCount);
1037     SET_PEAK(inoutPeakInfo.allocationCount, currInfo.allocationCount);
1038     SET_PEAK(inoutPeakInfo.unusedRangeCount, currInfo.unusedRangeCount);
1039     SET_PEAK(inoutPeakInfo.usedBytes, currInfo.usedBytes);
1040     SET_PEAK(inoutPeakInfo.unusedBytes, currInfo.unusedBytes);
1041     SET_PEAK(inoutPeakInfo.totalBytes, currInfo.usedBytes + currInfo.unusedBytes);
1042 
1043 #undef SET_PEAK
1044 }
1045 
PrintMemStatInfo(const MemStatInfo & info)1046 void Statistics::PrintMemStatInfo(const MemStatInfo& info)
1047 {
1048     printf("        Peak blocks %u, allocations %u, unused ranges %u\n",
1049         info.blockCount,
1050         info.allocationCount,
1051         info.unusedRangeCount);
1052     printf("        Peak total bytes %llu, used bytes %llu, unused bytes %llu\n",
1053         info.totalBytes,
1054         info.usedBytes,
1055         info.unusedBytes);
1056 }
1057 
1058 ////////////////////////////////////////////////////////////////////////////////
1059 // class ConfigurationParser
1060 
1061 class ConfigurationParser
1062 {
1063 public:
1064     ConfigurationParser();
1065 
1066     bool Parse(LineSplit& lineSplit);
1067 
1068     void Compare(
1069         const VkPhysicalDeviceProperties& currDevProps,
1070         const VkPhysicalDeviceMemoryProperties& currMemProps,
1071         uint32_t vulkanApiVersion,
1072         bool currMemoryBudgetEnabled);
1073 
1074 private:
1075     enum class OPTION
1076     {
1077         VulkanApiVersion,
1078         PhysicalDevice_apiVersion,
1079         PhysicalDevice_driverVersion,
1080         PhysicalDevice_vendorID,
1081         PhysicalDevice_deviceID,
1082         PhysicalDevice_deviceType,
1083         PhysicalDevice_deviceName,
1084         PhysicalDeviceLimits_maxMemoryAllocationCount,
1085         PhysicalDeviceLimits_bufferImageGranularity,
1086         PhysicalDeviceLimits_nonCoherentAtomSize,
1087         Extension_VK_KHR_dedicated_allocation,
1088         Extension_VK_KHR_bind_memory2,
1089         Extension_VK_EXT_memory_budget,
1090         Extension_VK_AMD_device_coherent_memory,
1091         Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,
1092         Macro_VMA_MIN_ALIGNMENT,
1093         Macro_VMA_DEBUG_MARGIN,
1094         Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS,
1095         Macro_VMA_DEBUG_DETECT_CORRUPTION,
1096         Macro_VMA_DEBUG_GLOBAL_MUTEX,
1097         Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,
1098         Macro_VMA_SMALL_HEAP_MAX_SIZE,
1099         Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,
1100         Count
1101     };
1102 
1103     std::vector<bool> m_OptionSet;
1104     std::vector<std::string> m_OptionValue;
1105     VkPhysicalDeviceMemoryProperties m_MemProps;
1106 
1107     bool m_WarningHeaderPrinted = false;
1108 
1109     void SetOption(
1110         size_t lineNumber,
1111         OPTION option,
1112         const StrRange& str);
1113     void EnsureWarningHeader();
1114     void CompareOption(VERBOSITY minVerbosity, const char* name,
1115         OPTION option, uint32_t currValue);
1116     void CompareOption(VERBOSITY minVerbosity, const char* name,
1117         OPTION option, uint64_t currValue);
1118     void CompareOption(VERBOSITY minVerbosity, const char* name,
1119         OPTION option, bool currValue);
1120     void CompareOption(VERBOSITY minVerbosity, const char* name,
1121         OPTION option, const char* currValue);
1122     void CompareMemProps(
1123         const VkPhysicalDeviceMemoryProperties& currMemProps);
1124 };
1125 
ConfigurationParser()1126 ConfigurationParser::ConfigurationParser() :
1127     m_OptionSet((size_t)OPTION::Count),
1128     m_OptionValue((size_t)OPTION::Count)
1129 {
1130     ZeroMemory(&m_MemProps, sizeof(m_MemProps));
1131 }
1132 
Parse(LineSplit & lineSplit)1133 bool ConfigurationParser::Parse(LineSplit& lineSplit)
1134 {
1135     for(auto& it : m_OptionSet)
1136     {
1137         it = false;
1138     }
1139     for(auto& it : m_OptionValue)
1140     {
1141         it.clear();
1142     }
1143 
1144     StrRange line;
1145 
1146     if(!lineSplit.GetNextLine(line) && !StrRangeEq(line, "Config,Begin"))
1147     {
1148         return false;
1149     }
1150 
1151     CsvSplit csvSplit;
1152     while(lineSplit.GetNextLine(line))
1153     {
1154         if(StrRangeEq(line, "Config,End"))
1155         {
1156             break;
1157         }
1158 
1159         const size_t currLineNumber = lineSplit.GetNextLineIndex();
1160 
1161         csvSplit.Set(line);
1162         if(csvSplit.GetCount() == 0)
1163         {
1164             return false;
1165         }
1166 
1167         const StrRange optionName = csvSplit.GetRange(0);
1168         if(StrRangeEq(optionName, "VulkanApiVersion"))
1169         {
1170             SetOption(currLineNumber, OPTION::VulkanApiVersion, StrRange{csvSplit.GetRange(1).beg, csvSplit.GetRange(2).end});
1171         }
1172         else if(StrRangeEq(optionName, "PhysicalDevice"))
1173         {
1174             if(csvSplit.GetCount() >= 3)
1175             {
1176                 const StrRange subOptionName = csvSplit.GetRange(1);
1177                 if(StrRangeEq(subOptionName, "apiVersion"))
1178                     SetOption(currLineNumber, OPTION::PhysicalDevice_apiVersion, csvSplit.GetRange(2));
1179                 else if(StrRangeEq(subOptionName, "driverVersion"))
1180                     SetOption(currLineNumber, OPTION::PhysicalDevice_driverVersion, csvSplit.GetRange(2));
1181                 else if(StrRangeEq(subOptionName, "vendorID"))
1182                     SetOption(currLineNumber, OPTION::PhysicalDevice_vendorID, csvSplit.GetRange(2));
1183                 else if(StrRangeEq(subOptionName, "deviceID"))
1184                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceID, csvSplit.GetRange(2));
1185                 else if(StrRangeEq(subOptionName, "deviceType"))
1186                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceType, csvSplit.GetRange(2));
1187                 else if(StrRangeEq(subOptionName, "deviceName"))
1188                     SetOption(currLineNumber, OPTION::PhysicalDevice_deviceName, StrRange(csvSplit.GetRange(2).beg, line.end));
1189                 else
1190                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1191             }
1192             else
1193                 printf("Line %zu: Too few columns.\n", currLineNumber);
1194         }
1195         else if(StrRangeEq(optionName, "PhysicalDeviceLimits"))
1196         {
1197             if(csvSplit.GetCount() >= 3)
1198             {
1199                 const StrRange subOptionName = csvSplit.GetRange(1);
1200                 if(StrRangeEq(subOptionName, "maxMemoryAllocationCount"))
1201                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, csvSplit.GetRange(2));
1202                 else if(StrRangeEq(subOptionName, "bufferImageGranularity"))
1203                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_bufferImageGranularity, csvSplit.GetRange(2));
1204                 else if(StrRangeEq(subOptionName, "nonCoherentAtomSize"))
1205                     SetOption(currLineNumber, OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, csvSplit.GetRange(2));
1206                 else
1207                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1208             }
1209             else
1210                 printf("Line %zu: Too few columns.\n", currLineNumber);
1211         }
1212         else if(StrRangeEq(optionName, "Extension"))
1213         {
1214             if(csvSplit.GetCount() >= 3)
1215             {
1216                 const StrRange subOptionName = csvSplit.GetRange(1);
1217                 if(StrRangeEq(subOptionName, "VK_KHR_dedicated_allocation"))
1218                 {
1219                     // Ignore because this extension is promoted to Vulkan 1.1.
1220                 }
1221                 else if(StrRangeEq(subOptionName, "VK_KHR_bind_memory2"))
1222                     SetOption(currLineNumber, OPTION::Extension_VK_KHR_bind_memory2, csvSplit.GetRange(2));
1223                 else if(StrRangeEq(subOptionName, "VK_EXT_memory_budget"))
1224                     SetOption(currLineNumber, OPTION::Extension_VK_EXT_memory_budget, csvSplit.GetRange(2));
1225                 else if(StrRangeEq(subOptionName, "VK_AMD_device_coherent_memory"))
1226                     SetOption(currLineNumber, OPTION::Extension_VK_AMD_device_coherent_memory, csvSplit.GetRange(2));
1227                 else
1228                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1229             }
1230             else
1231                 printf("Line %zu: Too few columns.\n", currLineNumber);
1232         }
1233         else if(StrRangeEq(optionName, "Macro"))
1234         {
1235             if(csvSplit.GetCount() >= 3)
1236             {
1237                 const StrRange subOptionName = csvSplit.GetRange(1);
1238                 if(StrRangeEq(subOptionName, "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY"))
1239                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_ALWAYS_DEDICATED_MEMORY, csvSplit.GetRange(2));
1240                 else if(StrRangeEq(subOptionName, "VMA_MIN_ALIGNMENT") || StrRangeEq(subOptionName, "VMA_DEBUG_ALIGNMENT"))
1241                     SetOption(currLineNumber, OPTION::Macro_VMA_MIN_ALIGNMENT, csvSplit.GetRange(2));
1242                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_MARGIN"))
1243                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MARGIN, csvSplit.GetRange(2));
1244                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_INITIALIZE_ALLOCATIONS"))
1245                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_INITIALIZE_ALLOCATIONS, csvSplit.GetRange(2));
1246                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_DETECT_CORRUPTION"))
1247                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_DETECT_CORRUPTION, csvSplit.GetRange(2));
1248                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_GLOBAL_MUTEX"))
1249                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_GLOBAL_MUTEX, csvSplit.GetRange(2));
1250                 else if(StrRangeEq(subOptionName, "VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY"))
1251                     SetOption(currLineNumber, OPTION::Macro_VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY, csvSplit.GetRange(2));
1252                 else if(StrRangeEq(subOptionName, "VMA_SMALL_HEAP_MAX_SIZE"))
1253                     SetOption(currLineNumber, OPTION::Macro_VMA_SMALL_HEAP_MAX_SIZE, csvSplit.GetRange(2));
1254                 else if(StrRangeEq(subOptionName, "VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE"))
1255                     SetOption(currLineNumber, OPTION::Macro_VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE, csvSplit.GetRange(2));
1256                 else
1257                     printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1258             }
1259             else
1260                 printf("Line %zu: Too few columns.\n", currLineNumber);
1261         }
1262         else if(StrRangeEq(optionName, "PhysicalDeviceMemory"))
1263         {
1264             uint32_t value = 0;
1265             if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "HeapCount") &&
1266                 StrRangeToUint(csvSplit.GetRange(2), value))
1267             {
1268                 m_MemProps.memoryHeapCount = value;
1269             }
1270             else if(csvSplit.GetCount() == 3 && StrRangeEq(csvSplit.GetRange(1), "TypeCount") &&
1271                 StrRangeToUint(csvSplit.GetRange(2), value))
1272             {
1273                 m_MemProps.memoryTypeCount = value;
1274             }
1275             else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Heap") &&
1276                 StrRangeToUint(csvSplit.GetRange(2), value) &&
1277                 value < m_MemProps.memoryHeapCount)
1278             {
1279                 if(StrRangeEq(csvSplit.GetRange(3), "size") &&
1280                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].size))
1281                 {
1282                      // Parsed.
1283                 }
1284                 else if(StrRangeEq(csvSplit.GetRange(3), "flags") &&
1285                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryHeaps[value].flags))
1286                 {
1287                      // Parsed.
1288                 }
1289                 else
1290                     printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1291             }
1292             else if(csvSplit.GetCount() == 5 && StrRangeEq(csvSplit.GetRange(1), "Type") &&
1293                 StrRangeToUint(csvSplit.GetRange(2), value) &&
1294                 value < m_MemProps.memoryTypeCount)
1295             {
1296                 if(StrRangeEq(csvSplit.GetRange(3), "heapIndex") &&
1297                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].heapIndex))
1298                 {
1299                      // Parsed.
1300                 }
1301                 else if(StrRangeEq(csvSplit.GetRange(3), "propertyFlags") &&
1302                     StrRangeToUint(csvSplit.GetRange(4), m_MemProps.memoryTypes[value].propertyFlags))
1303                 {
1304                      // Parsed.
1305                 }
1306                 else
1307                     printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1308             }
1309             else
1310                 printf("Line %zu: Invalid configuration option.\n", currLineNumber);
1311         }
1312         else
1313             printf("Line %zu: Unrecognized configuration option.\n", currLineNumber);
1314     }
1315 
1316     return true;
1317 }
1318 
Compare(const VkPhysicalDeviceProperties & currDevProps,const VkPhysicalDeviceMemoryProperties & currMemProps,uint32_t vulkanApiVersion,bool currMemoryBudgetEnabled)1319 void ConfigurationParser::Compare(
1320     const VkPhysicalDeviceProperties& currDevProps,
1321     const VkPhysicalDeviceMemoryProperties& currMemProps,
1322     uint32_t vulkanApiVersion,
1323     bool currMemoryBudgetEnabled)
1324 {
1325     char vulkanApiVersionStr[32];
1326     sprintf_s(vulkanApiVersionStr, "%u,%u", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
1327     CompareOption(VERBOSITY::DEFAULT, "VulkanApiVersion",
1328         OPTION::VulkanApiVersion, vulkanApiVersionStr);
1329 
1330     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice apiVersion",
1331         OPTION::PhysicalDevice_apiVersion, currDevProps.apiVersion);
1332     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice driverVersion",
1333         OPTION::PhysicalDevice_driverVersion, currDevProps.driverVersion);
1334     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice vendorID",
1335         OPTION::PhysicalDevice_vendorID, currDevProps.vendorID);
1336     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceID",
1337         OPTION::PhysicalDevice_deviceID, currDevProps.deviceID);
1338     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceType",
1339         OPTION::PhysicalDevice_deviceType, (uint32_t)currDevProps.deviceType);
1340     CompareOption(VERBOSITY::MAXIMUM, "PhysicalDevice deviceName",
1341         OPTION::PhysicalDevice_deviceName, currDevProps.deviceName);
1342 
1343     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits maxMemoryAllocationCount",
1344         OPTION::PhysicalDeviceLimits_maxMemoryAllocationCount, currDevProps.limits.maxMemoryAllocationCount);
1345     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits bufferImageGranularity",
1346         OPTION::PhysicalDeviceLimits_bufferImageGranularity, currDevProps.limits.bufferImageGranularity);
1347     CompareOption(VERBOSITY::DEFAULT, "PhysicalDeviceLimits nonCoherentAtomSize",
1348         OPTION::PhysicalDeviceLimits_nonCoherentAtomSize, currDevProps.limits.nonCoherentAtomSize);
1349 
1350     CompareMemProps(currMemProps);
1351 }
1352 
SetOption(size_t lineNumber,OPTION option,const StrRange & str)1353 void ConfigurationParser::SetOption(
1354     size_t lineNumber,
1355     OPTION option,
1356     const StrRange& str)
1357 {
1358     if(m_OptionSet[(size_t)option])
1359     {
1360         printf("Line %zu: Option already specified.\n" ,lineNumber);
1361     }
1362 
1363     m_OptionSet[(size_t)option] = true;
1364 
1365     std::string val;
1366     str.to_str(val);
1367     m_OptionValue[(size_t)option] = std::move(val);
1368 }
1369 
EnsureWarningHeader()1370 void ConfigurationParser::EnsureWarningHeader()
1371 {
1372     if(!m_WarningHeaderPrinted)
1373     {
1374         printf("WARNING: Following configuration parameters don't match:\n");
1375         m_WarningHeaderPrinted = true;
1376     }
1377 }
1378 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,uint32_t currValue)1379 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1380     OPTION option, uint32_t currValue)
1381 {
1382     if(m_OptionSet[(size_t)option] &&
1383         g_Verbosity >= minVerbosity)
1384     {
1385         uint32_t origValue;
1386         if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))
1387         {
1388             if(origValue != currValue)
1389             {
1390                 EnsureWarningHeader();
1391                 printf("    %s: original %u, current %u\n", name, origValue, currValue);
1392             }
1393         }
1394     }
1395 }
1396 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,uint64_t currValue)1397 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1398     OPTION option, uint64_t currValue)
1399 {
1400     if(m_OptionSet[(size_t)option] &&
1401         g_Verbosity >= minVerbosity)
1402     {
1403         uint64_t origValue;
1404         if(StrRangeToUint(StrRange(m_OptionValue[(size_t)option]), origValue))
1405         {
1406             if(origValue != currValue)
1407             {
1408                 EnsureWarningHeader();
1409                 printf("    %s: original %llu, current %llu\n", name, origValue, currValue);
1410             }
1411         }
1412     }
1413 }
1414 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,bool currValue)1415 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1416     OPTION option, bool currValue)
1417 {
1418     if(m_OptionSet[(size_t)option] &&
1419         g_Verbosity >= minVerbosity)
1420     {
1421         bool origValue;
1422         if(StrRangeToBool(StrRange(m_OptionValue[(size_t)option]), origValue))
1423         {
1424             if(origValue != currValue)
1425             {
1426                 EnsureWarningHeader();
1427                 printf("    %s: original %u, current %u\n", name,
1428                     origValue ? 1 : 0,
1429                     currValue ? 1 : 0);
1430             }
1431         }
1432     }
1433 }
1434 
CompareOption(VERBOSITY minVerbosity,const char * name,OPTION option,const char * currValue)1435 void ConfigurationParser::CompareOption(VERBOSITY minVerbosity, const char* name,
1436     OPTION option, const char* currValue)
1437 {
1438     if(m_OptionSet[(size_t)option] &&
1439         g_Verbosity >= minVerbosity)
1440     {
1441         const std::string& origValue = m_OptionValue[(size_t)option];
1442         if(origValue != currValue)
1443         {
1444             EnsureWarningHeader();
1445             printf("    %s: original \"%s\", current \"%s\"\n", name, origValue.c_str(), currValue);
1446         }
1447     }
1448 }
1449 
CompareMemProps(const VkPhysicalDeviceMemoryProperties & currMemProps)1450 void ConfigurationParser::CompareMemProps(
1451     const VkPhysicalDeviceMemoryProperties& currMemProps)
1452 {
1453     if(g_Verbosity < VERBOSITY::DEFAULT)
1454     {
1455         return;
1456     }
1457 
1458     bool memoryMatch =
1459         currMemProps.memoryHeapCount == m_MemProps.memoryHeapCount &&
1460         currMemProps.memoryTypeCount == m_MemProps.memoryTypeCount;
1461 
1462     for(uint32_t i = 0; memoryMatch && i < currMemProps.memoryHeapCount; ++i)
1463     {
1464         memoryMatch =
1465             currMemProps.memoryHeaps[i].flags == m_MemProps.memoryHeaps[i].flags;
1466     }
1467     for(uint32_t i = 0; memoryMatch && i < currMemProps.memoryTypeCount; ++i)
1468     {
1469         memoryMatch =
1470             currMemProps.memoryTypes[i].heapIndex == m_MemProps.memoryTypes[i].heapIndex &&
1471             currMemProps.memoryTypes[i].propertyFlags == m_MemProps.memoryTypes[i].propertyFlags;
1472     }
1473 
1474     if(memoryMatch && g_Verbosity == VERBOSITY::MAXIMUM)
1475     {
1476         bool memorySizeMatch = true;
1477         for(uint32_t i = 0; memorySizeMatch && i < currMemProps.memoryHeapCount; ++i)
1478         {
1479             memorySizeMatch =
1480                 currMemProps.memoryHeaps[i].size == m_MemProps.memoryHeaps[i].size;
1481         }
1482 
1483         if(!memorySizeMatch)
1484         {
1485             printf("WARNING: Sizes of original memory heaps are different from current ones.\n");
1486         }
1487     }
1488     else
1489     {
1490         printf("WARNING: Layout of original memory heaps and types is different from current one.\n");
1491     }
1492 }
1493 
1494 ////////////////////////////////////////////////////////////////////////////////
1495 // class Player
1496 
1497 static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation";
1498 
MyDebugReportCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,VkDebugUtilsMessageTypeFlagsEXT messageTypes,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void * pUserData)1499 static VkBool32 VKAPI_PTR MyDebugReportCallback(
1500     VkDebugUtilsMessageSeverityFlagBitsEXT           messageSeverity,
1501     VkDebugUtilsMessageTypeFlagsEXT                  messageTypes,
1502     const VkDebugUtilsMessengerCallbackDataEXT*      pCallbackData,
1503     void*                                            pUserData)
1504 {
1505     assert(pCallbackData && pCallbackData->pMessageIdName && pCallbackData->pMessage);
1506     printf("%s \xBA %s\n", pCallbackData->pMessageIdName, pCallbackData->pMessage);
1507     return VK_FALSE;
1508 }
1509 
IsLayerSupported(const VkLayerProperties * pProps,size_t propCount,const char * pLayerName)1510 static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)
1511 {
1512     const VkLayerProperties* propsEnd = pProps + propCount;
1513     return std::find_if(
1514         pProps,
1515         propsEnd,
1516         [pLayerName](const VkLayerProperties& prop) -> bool {
1517             return strcmp(pLayerName, prop.layerName) == 0;
1518         }) != propsEnd;
1519 }
1520 
1521 static const size_t FIRST_PARAM_INDEX = 4;
1522 
InitVulkanFeatures(VkPhysicalDeviceFeatures & outFeatures,const VkPhysicalDeviceFeatures & supportedFeatures)1523 static void InitVulkanFeatures(
1524     VkPhysicalDeviceFeatures& outFeatures,
1525     const VkPhysicalDeviceFeatures& supportedFeatures)
1526 {
1527     ZeroMemory(&outFeatures, sizeof(outFeatures));
1528 
1529     // Enable something what may interact with memory/buffer/image support.
1530 
1531     outFeatures.fullDrawIndexUint32 = supportedFeatures.fullDrawIndexUint32;
1532     outFeatures.imageCubeArray = supportedFeatures.imageCubeArray;
1533     outFeatures.geometryShader = supportedFeatures.geometryShader;
1534     outFeatures.tessellationShader = supportedFeatures.tessellationShader;
1535     outFeatures.multiDrawIndirect = supportedFeatures.multiDrawIndirect;
1536     outFeatures.textureCompressionETC2 = supportedFeatures.textureCompressionETC2;
1537     outFeatures.textureCompressionASTC_LDR = supportedFeatures.textureCompressionASTC_LDR;
1538     outFeatures.textureCompressionBC = supportedFeatures.textureCompressionBC;
1539 }
1540 
1541 class Player
1542 {
1543 public:
1544     Player();
1545     int Init();
1546     ~Player();
1547 
1548     void ApplyConfig(ConfigurationParser& configParser);
1549     void ExecuteLine(size_t lineNumber, const StrRange& line);
1550     void DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed);
1551     void Defragment();
1552 
1553     void PrintStats();
1554 
1555 private:
1556     static const size_t MAX_WARNINGS_TO_SHOW = 64;
1557 
1558     size_t m_WarningCount = 0;
1559     bool m_AllocateForBufferImageWarningIssued = false;
1560 
1561     VkInstance m_VulkanInstance = VK_NULL_HANDLE;
1562     VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;
1563     uint32_t m_GraphicsQueueFamilyIndex = UINT32_MAX;
1564     uint32_t m_TransferQueueFamilyIndex = UINT32_MAX;
1565     VkDevice m_Device = VK_NULL_HANDLE;
1566     VkQueue m_GraphicsQueue = VK_NULL_HANDLE;
1567     VkQueue m_TransferQueue = VK_NULL_HANDLE;
1568     VmaAllocator m_Allocator = VK_NULL_HANDLE;
1569     VkCommandPool m_CommandPool = VK_NULL_HANDLE;
1570     VkCommandBuffer m_CommandBuffer = VK_NULL_HANDLE;
1571     bool m_MemoryBudgetEnabled = false;
1572     const VkPhysicalDeviceProperties* m_DevProps = nullptr;
1573     const VkPhysicalDeviceMemoryProperties* m_MemProps = nullptr;
1574 
1575     PFN_vkCreateDebugUtilsMessengerEXT m_vkCreateDebugUtilsMessengerEXT = nullptr;
1576     PFN_vkDestroyDebugUtilsMessengerEXT m_vkDestroyDebugUtilsMessengerEXT = nullptr;
1577     VkDebugUtilsMessengerEXT m_DebugUtilsMessenger = VK_NULL_HANDLE;
1578 
1579     uint32_t m_VmaFrameIndex = 0;
1580 
1581     // Any of these handles null can mean it was created in original but couldn't be created now.
1582     struct Pool
1583     {
1584         VmaPool pool;
1585     };
1586     struct Allocation
1587     {
1588         uint32_t allocationFlags = 0;
1589         VmaAllocation allocation = VK_NULL_HANDLE;
1590         VkBuffer buffer = VK_NULL_HANDLE;
1591         VkImage image = VK_NULL_HANDLE;
1592     };
1593     std::unordered_map<uint64_t, Pool> m_Pools;
1594     std::unordered_map<uint64_t, Allocation> m_Allocations;
1595     std::unordered_map<uint64_t, VmaDefragmentationContext> m_DefragmentationContexts;
1596 
1597     struct Thread
1598     {
1599         uint32_t callCount;
1600     };
1601     std::unordered_map<uint32_t, Thread> m_Threads;
1602 
1603     // Copy of column [1] from previously parsed line.
1604     std::string m_LastLineTimeStr;
1605     Statistics m_Stats;
1606 
1607     std::vector<char> m_UserDataTmpStr;
1608 
1609     void Destroy(const Allocation& alloc);
1610 
1611     // Finds VmaPool bu original pointer.
1612     // If origPool = null, returns true and outPool = null.
1613     // If failed, prints warning, returns false and outPool = null.
1614     bool FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool);
1615     // If allocation with that origPtr already exists, prints warning and replaces it.
1616     void AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc);
1617 
1618     // Increments warning counter. Returns true if warning message should be printed.
1619     bool IssueWarning();
1620 
1621     int InitVulkan();
1622     void FinalizeVulkan();
1623     void RegisterDebugCallbacks();
1624     void UnregisterDebugCallbacks();
1625 
1626     // If parmeter count doesn't match, issues warning and returns false.
1627     bool ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound);
1628 
1629     // If failed, prints warning, returns false, and sets allocCreateInfo.pUserData to null.
1630     bool PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData);
1631 
1632     void UpdateMemStats();
1633 
1634     void ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit);
1635     void ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit);
1636     void ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit);
1637     void ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit);
ExecuteDestroyBuffer(size_t lineNumber,const CsvSplit & csvSplit)1638     void ExecuteDestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyBuffer); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyBuffer"); }
1639     void ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit);
ExecuteDestroyImage(size_t lineNumber,const CsvSplit & csvSplit)1640     void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyImage); DestroyAllocation(lineNumber, csvSplit, "vmaDestroyImage"); }
ExecuteFreeMemory(size_t lineNumber,const CsvSplit & csvSplit)1641     void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemory); DestroyAllocation(lineNumber, csvSplit, "vmaFreeMemory"); }
1642     void ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);
1643     void ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1644     void ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit);
1645     void ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit);
1646     void ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType);
1647     void ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit);
1648     void ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit);
1649     void ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1650     void ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1651     void ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1652     void ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit);
1653     void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit);
1654     void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit);
1655     void ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit);
1656     void ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit);
1657     void ExecuteSetPoolName(size_t lineNumber, const CsvSplit& csvSplit);
1658 
1659     void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName);
1660 
1661     void PrintStats(const VmaStats& stats, const char* suffix);
1662     void PrintStatInfo(const VmaStatInfo& info);
1663 };
1664 
Player()1665 Player::Player()
1666 {
1667 }
1668 
Init()1669 int Player::Init()
1670 {
1671     int result = InitVulkan();
1672 
1673     if(result == 0)
1674     {
1675         m_Stats.Init(m_MemProps->memoryHeapCount, m_MemProps->memoryTypeCount);
1676         UpdateMemStats();
1677     }
1678 
1679     return result;
1680 }
1681 
~Player()1682 Player::~Player()
1683 {
1684     FinalizeVulkan();
1685 
1686     if(g_Verbosity < VERBOSITY::MAXIMUM && m_WarningCount > MAX_WARNINGS_TO_SHOW)
1687         printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW);
1688 }
1689 
ApplyConfig(ConfigurationParser & configParser)1690 void Player::ApplyConfig(ConfigurationParser& configParser)
1691 {
1692     configParser.Compare(*m_DevProps, *m_MemProps,
1693         VULKAN_API_VERSION,
1694         m_MemoryBudgetEnabled);
1695 }
1696 
ExecuteLine(size_t lineNumber,const StrRange & line)1697 void Player::ExecuteLine(size_t lineNumber, const StrRange& line)
1698 {
1699     CsvSplit csvSplit;
1700     csvSplit.Set(line);
1701 
1702     if(csvSplit.GetCount() >= FIRST_PARAM_INDEX)
1703     {
1704         // Check thread ID.
1705         uint32_t threadId;
1706         if(StrRangeToUint(csvSplit.GetRange(0), threadId))
1707         {
1708             const auto it = m_Threads.find(threadId);
1709             if(it != m_Threads.end())
1710             {
1711                 ++it->second.callCount;
1712             }
1713             else
1714             {
1715                 Thread threadInfo{};
1716                 threadInfo.callCount = 1;
1717                 m_Threads[threadId] = threadInfo;
1718             }
1719         }
1720         else
1721         {
1722             if(IssueWarning())
1723             {
1724                 printf("Line %zu: Incorrect thread ID.\n", lineNumber);
1725             }
1726         }
1727 
1728         // Save time.
1729         csvSplit.GetRange(1).to_str(m_LastLineTimeStr);
1730 
1731         // Update VMA current frame index.
1732         StrRange frameIndexStr = csvSplit.GetRange(2);
1733         uint32_t frameIndex;
1734         if(StrRangeToUint(frameIndexStr, frameIndex))
1735         {
1736             if(frameIndex != m_VmaFrameIndex)
1737             {
1738                 vmaSetCurrentFrameIndex(m_Allocator, frameIndex);
1739                 m_VmaFrameIndex = frameIndex;
1740             }
1741         }
1742         else
1743         {
1744             if(IssueWarning())
1745             {
1746                 printf("Line %zu: Incorrect frame index.\n", lineNumber);
1747             }
1748         }
1749 
1750         StrRange functionName = csvSplit.GetRange(3);
1751 
1752         if(StrRangeEq(functionName, "vmaCreateAllocator"))
1753         {
1754             if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
1755             {
1756                 // Nothing.
1757             }
1758         }
1759         else if(StrRangeEq(functionName, "vmaDestroyAllocator"))
1760         {
1761             if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
1762             {
1763                 // Nothing.
1764             }
1765         }
1766         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreatePool]))
1767             ExecuteCreatePool(lineNumber, csvSplit);
1768         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyPool]))
1769             ExecuteDestroyPool(lineNumber, csvSplit);
1770         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::SetAllocationUserData]))
1771             ExecuteSetAllocationUserData(lineNumber, csvSplit);
1772         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateBuffer]))
1773             ExecuteCreateBuffer(lineNumber, csvSplit);
1774         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyBuffer]))
1775             ExecuteDestroyBuffer(lineNumber, csvSplit);
1776         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateImage]))
1777             ExecuteCreateImage(lineNumber, csvSplit);
1778         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DestroyImage]))
1779             ExecuteDestroyImage(lineNumber, csvSplit);
1780         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemory]))
1781             ExecuteFreeMemory(lineNumber, csvSplit);
1782         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FreeMemoryPages]))
1783             ExecuteFreeMemoryPages(lineNumber, csvSplit);
1784         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::CreateLostAllocation]))
1785             ExecuteCreateLostAllocation(lineNumber, csvSplit);
1786         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemory]))
1787             ExecuteAllocateMemory(lineNumber, csvSplit);
1788         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryPages]))
1789             ExecuteAllocateMemoryPages(lineNumber, csvSplit);
1790         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForBuffer]))
1791             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::BUFFER);
1792         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::AllocateMemoryForImage]))
1793             ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::IMAGE);
1794         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MapMemory]))
1795             ExecuteMapMemory(lineNumber, csvSplit);
1796         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::UnmapMemory]))
1797             ExecuteUnmapMemory(lineNumber, csvSplit);
1798         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::FlushAllocation]))
1799             ExecuteFlushAllocation(lineNumber, csvSplit);
1800         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::InvalidateAllocation]))
1801             ExecuteInvalidateAllocation(lineNumber, csvSplit);
1802         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::TouchAllocation]))
1803             ExecuteTouchAllocation(lineNumber, csvSplit);
1804         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::GetAllocationInfo]))
1805             ExecuteGetAllocationInfo(lineNumber, csvSplit);
1806         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::MakePoolAllocationsLost]))
1807             ExecuteMakePoolAllocationsLost(lineNumber, csvSplit);
1808         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::ResizeAllocation]))
1809             ExecuteResizeAllocation(lineNumber, csvSplit);
1810         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationBegin]))
1811             ExecuteDefragmentationBegin(lineNumber, csvSplit);
1812         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::DefragmentationEnd]))
1813             ExecuteDefragmentationEnd(lineNumber, csvSplit);
1814         else if(StrRangeEq(functionName, VMA_FUNCTION_NAMES[(uint32_t)VMA_FUNCTION::SetPoolName]))
1815             ExecuteSetPoolName(lineNumber, csvSplit);
1816         else
1817         {
1818             if(IssueWarning())
1819             {
1820                 printf("Line %zu: Unknown function.\n", lineNumber);
1821             }
1822         }
1823     }
1824     else
1825     {
1826         if(IssueWarning())
1827         {
1828             printf("Line %zu: Too few columns.\n", lineNumber);
1829         }
1830     }
1831 }
1832 
DumpStats(const char * fileNameFormat,size_t lineNumber,bool detailed)1833 void Player::DumpStats(const char* fileNameFormat, size_t lineNumber, bool detailed)
1834 {
1835     char* pStatsString = nullptr;
1836     vmaBuildStatsString(m_Allocator, &pStatsString, detailed ? VK_TRUE : VK_FALSE);
1837 
1838     char fileName[MAX_PATH];
1839     sprintf_s(fileName, fileNameFormat, lineNumber);
1840 
1841     FILE* file = nullptr;
1842     errno_t err = fopen_s(&file, fileName, "wb");
1843     if(err == 0)
1844     {
1845         fwrite(pStatsString, 1, strlen(pStatsString), file);
1846         fclose(file);
1847     }
1848     else
1849     {
1850         printf("ERROR: Failed to write file: %s\n", fileName);
1851     }
1852 
1853     vmaFreeStatsString(m_Allocator, pStatsString);
1854 }
1855 
Destroy(const Allocation & alloc)1856 void Player::Destroy(const Allocation& alloc)
1857 {
1858     if(alloc.buffer)
1859     {
1860         assert(alloc.image == VK_NULL_HANDLE);
1861         vmaDestroyBuffer(m_Allocator, alloc.buffer, alloc.allocation);
1862     }
1863     else if(alloc.image)
1864     {
1865         vmaDestroyImage(m_Allocator, alloc.image, alloc.allocation);
1866     }
1867     else
1868         vmaFreeMemory(m_Allocator, alloc.allocation);
1869 }
1870 
FindPool(size_t lineNumber,uint64_t origPool,VmaPool & outPool)1871 bool Player::FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool)
1872 {
1873     outPool = VK_NULL_HANDLE;
1874 
1875     if(origPool != 0)
1876     {
1877         const auto poolIt = m_Pools.find(origPool);
1878         if(poolIt != m_Pools.end())
1879         {
1880             outPool = poolIt->second.pool;
1881             return true;
1882         }
1883         else
1884         {
1885             if(IssueWarning())
1886             {
1887                 printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool);
1888             }
1889         }
1890     }
1891 
1892     return true;
1893 }
1894 
AddAllocation(size_t lineNumber,uint64_t origPtr,VkResult res,const char * functionName,Allocation && allocDesc)1895 void Player::AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc)
1896 {
1897     if(origPtr)
1898     {
1899         if(res == VK_SUCCESS)
1900         {
1901             // Originally succeeded, currently succeeded.
1902             // Just save pointer (done below).
1903         }
1904         else
1905         {
1906             // Originally succeeded, currently failed.
1907             // Print warning. Save null pointer.
1908             if(IssueWarning())
1909             {
1910                 printf("Line %zu: %s failed (%d), while originally succeeded.\n", lineNumber, functionName, res);
1911             }
1912         }
1913 
1914         const auto existingIt = m_Allocations.find(origPtr);
1915         if(existingIt != m_Allocations.end())
1916         {
1917             if(IssueWarning())
1918             {
1919                 printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr);
1920             }
1921         }
1922         m_Allocations[origPtr] = std::move(allocDesc);
1923     }
1924     else
1925     {
1926         if(res == VK_SUCCESS)
1927         {
1928             // Originally failed, currently succeeded.
1929             // Print warning, destroy the object.
1930             if(IssueWarning())
1931             {
1932                 printf("Line %zu: %s succeeded, originally failed.\n", lineNumber, functionName);
1933             }
1934 
1935             Destroy(allocDesc);
1936         }
1937         else
1938         {
1939             // Originally failed, currently failed.
1940             // Print warning.
1941             if(IssueWarning())
1942             {
1943                 printf("Line %zu: %s failed (%d), originally also failed.\n", lineNumber, functionName, res);
1944             }
1945         }
1946     }
1947 }
1948 
IssueWarning()1949 bool Player::IssueWarning()
1950 {
1951     if(g_Verbosity < VERBOSITY::MAXIMUM)
1952     {
1953         return m_WarningCount++ < MAX_WARNINGS_TO_SHOW;
1954     }
1955     else
1956     {
1957         ++m_WarningCount;
1958         return true;
1959     }
1960 }
1961 
InitVulkan()1962 int Player::InitVulkan()
1963 {
1964     if(g_Verbosity == VERBOSITY::MAXIMUM)
1965     {
1966         printf("Initializing Vulkan...\n");
1967     }
1968 
1969     uint32_t instanceLayerPropCount = 0;
1970     VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr);
1971     assert(res == VK_SUCCESS);
1972 
1973     std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
1974     if(instanceLayerPropCount > 0)
1975     {
1976         res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data());
1977         assert(res == VK_SUCCESS);
1978     }
1979 
1980     const bool validationLayersAvailable =
1981         IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME);
1982 
1983     bool validationLayersEnabled = false;
1984     switch(g_VK_LAYER_KHRONOS_validation)
1985     {
1986     case VULKAN_EXTENSION_REQUEST::DISABLED:
1987         break;
1988     case VULKAN_EXTENSION_REQUEST::DEFAULT:
1989         validationLayersEnabled = validationLayersAvailable;
1990         break;
1991     case VULKAN_EXTENSION_REQUEST::ENABLED:
1992         validationLayersEnabled = validationLayersAvailable;
1993         if(!validationLayersAvailable)
1994         {
1995             printf("WARNING: %s layer cannot be enabled.\n", VALIDATION_LAYER_NAME);
1996         }
1997         break;
1998     default: assert(0);
1999     }
2000 
2001     uint32_t availableInstanceExtensionCount = 0;
2002     res = vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, nullptr);
2003     assert(res == VK_SUCCESS);
2004     std::vector<VkExtensionProperties> availableInstanceExtensions(availableInstanceExtensionCount);
2005     if(availableInstanceExtensionCount > 0)
2006     {
2007         res = vkEnumerateInstanceExtensionProperties(nullptr, &availableInstanceExtensionCount, availableInstanceExtensions.data());
2008         assert(res == VK_SUCCESS);
2009     }
2010 
2011     std::vector<const char*> enabledInstanceExtensions;
2012     //enabledInstanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
2013     //enabledInstanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
2014 
2015     std::vector<const char*> instanceLayers;
2016     if(validationLayersEnabled)
2017     {
2018         instanceLayers.push_back(VALIDATION_LAYER_NAME);
2019     }
2020 
2021     bool VK_KHR_get_physical_device_properties2_enabled = false;
2022     bool VK_EXT_debug_utils_enabled = false;
2023     for(const auto& extensionProperties : availableInstanceExtensions)
2024     {
2025         if(strcmp(extensionProperties.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)
2026         {
2027             enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
2028             VK_KHR_get_physical_device_properties2_enabled = true;
2029         }
2030         else if(strcmp(extensionProperties.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)
2031         {
2032             if(validationLayersEnabled)
2033             {
2034                 enabledInstanceExtensions.push_back("VK_EXT_debug_utils");
2035                 VK_EXT_debug_utils_enabled = true;
2036             }
2037         }
2038     }
2039 
2040     VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
2041     appInfo.pApplicationName = "VmaReplay";
2042     appInfo.applicationVersion = VK_MAKE_VERSION(2, 3, 0);
2043     appInfo.pEngineName = "Vulkan Memory Allocator";
2044     appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 0);
2045     appInfo.apiVersion = VULKAN_API_VERSION;
2046 
2047     VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
2048     instInfo.pApplicationInfo = &appInfo;
2049     instInfo.enabledExtensionCount = (uint32_t)enabledInstanceExtensions.size();
2050     instInfo.ppEnabledExtensionNames = enabledInstanceExtensions.data();
2051     instInfo.enabledLayerCount = (uint32_t)instanceLayers.size();
2052     instInfo.ppEnabledLayerNames = instanceLayers.data();
2053 
2054     res = vkCreateInstance(&instInfo, NULL, &m_VulkanInstance);
2055     if(res != VK_SUCCESS)
2056     {
2057         printf("ERROR: vkCreateInstance failed (%d)\n", res);
2058         return RESULT_ERROR_VULKAN;
2059     }
2060 
2061     if(VK_EXT_debug_utils_enabled)
2062     {
2063         RegisterDebugCallbacks();
2064     }
2065 
2066     // Find physical device
2067 
2068     uint32_t physicalDeviceCount = 0;
2069     res = vkEnumeratePhysicalDevices(m_VulkanInstance, &physicalDeviceCount, nullptr);
2070     assert(res == VK_SUCCESS);
2071     if(physicalDeviceCount == 0)
2072     {
2073         printf("ERROR: No Vulkan physical devices found.\n");
2074         return RESULT_ERROR_VULKAN;
2075     }
2076 
2077     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
2078     res = vkEnumeratePhysicalDevices(m_VulkanInstance, &physicalDeviceCount, physicalDevices.data());
2079     assert(res == VK_SUCCESS);
2080 
2081     if(g_PhysicalDeviceIndex >= physicalDeviceCount)
2082     {
2083         printf("ERROR: Incorrect Vulkan physical device index %u. System has %u physical devices.\n",
2084             g_PhysicalDeviceIndex,
2085             physicalDeviceCount);
2086         return RESULT_ERROR_VULKAN;
2087     }
2088 
2089     m_PhysicalDevice = physicalDevices[0];
2090 
2091     // Find queue family index
2092 
2093     uint32_t queueFamilyCount = 0;
2094     vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, nullptr);
2095     if(queueFamilyCount)
2096     {
2097         std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
2098         vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data());
2099         for(uint32_t i = 0; i < queueFamilyCount; ++i)
2100         {
2101             if(queueFamilies[i].queueCount > 0)
2102             {
2103                 if(m_GraphicsQueueFamilyIndex == UINT32_MAX &&
2104                     (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
2105                 {
2106                     m_GraphicsQueueFamilyIndex = i;
2107                 }
2108                 if(m_TransferQueueFamilyIndex == UINT32_MAX &&
2109                     (queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) != 0)
2110                 {
2111                     m_TransferQueueFamilyIndex = i;
2112                 }
2113             }
2114         }
2115     }
2116     if(m_GraphicsQueueFamilyIndex == UINT_MAX)
2117     {
2118         printf("ERROR: Couldn't find graphics queue.\n");
2119         return RESULT_ERROR_VULKAN;
2120     }
2121     if(m_TransferQueueFamilyIndex == UINT_MAX)
2122     {
2123         printf("ERROR: Couldn't find transfer queue.\n");
2124         return RESULT_ERROR_VULKAN;
2125     }
2126 
2127     VkPhysicalDeviceFeatures supportedFeatures;
2128     vkGetPhysicalDeviceFeatures(m_PhysicalDevice, &supportedFeatures);
2129 
2130     // Create logical device
2131 
2132     const float queuePriority = 1.f;
2133 
2134     VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
2135     deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
2136     deviceQueueCreateInfo[0].queueFamilyIndex = m_GraphicsQueueFamilyIndex;
2137     deviceQueueCreateInfo[0].queueCount = 1;
2138     deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority;
2139 
2140     if(m_TransferQueueFamilyIndex != m_GraphicsQueueFamilyIndex)
2141     {
2142         deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
2143         deviceQueueCreateInfo[1].queueFamilyIndex = m_TransferQueueFamilyIndex;
2144         deviceQueueCreateInfo[1].queueCount = 1;
2145         deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority;
2146     }
2147 
2148     // Enable something what may interact with memory/buffer/image support.
2149     VkPhysicalDeviceFeatures enabledFeatures;
2150     InitVulkanFeatures(enabledFeatures, supportedFeatures);
2151 
2152     bool VK_KHR_get_memory_requirements2_available = false;
2153 
2154     // Determine list of device extensions to enable.
2155     std::vector<const char*> enabledDeviceExtensions;
2156     //enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
2157     bool memoryBudgetAvailable = false;
2158     {
2159         uint32_t propertyCount = 0;
2160         res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr);
2161         assert(res == VK_SUCCESS);
2162 
2163         if(propertyCount)
2164         {
2165             std::vector<VkExtensionProperties> properties{propertyCount};
2166             res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data());
2167             assert(res == VK_SUCCESS);
2168 
2169             for(uint32_t i = 0; i < propertyCount; ++i)
2170             {
2171                 if(strcmp(properties[i].extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME) == 0)
2172                 {
2173                     VK_KHR_get_memory_requirements2_available = true;
2174                 }
2175                 else if(strcmp(properties[i].extensionName, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0)
2176                 {
2177                     if(VK_KHR_get_physical_device_properties2_enabled)
2178                     {
2179                         memoryBudgetAvailable = true;
2180                     }
2181                 }
2182             }
2183         }
2184     }
2185 
2186     switch(g_VK_EXT_memory_budget_request)
2187     {
2188     case VULKAN_EXTENSION_REQUEST::DISABLED:
2189         break;
2190     case VULKAN_EXTENSION_REQUEST::DEFAULT:
2191         m_MemoryBudgetEnabled = memoryBudgetAvailable;
2192         break;
2193     case VULKAN_EXTENSION_REQUEST::ENABLED:
2194         m_MemoryBudgetEnabled = memoryBudgetAvailable;
2195         if(!memoryBudgetAvailable)
2196         {
2197             printf("WARNING: VK_EXT_memory_budget extension cannot be enabled.\n");
2198         }
2199         break;
2200     default: assert(0);
2201     }
2202 
2203     if(g_VK_AMD_device_coherent_memory_request == VULKAN_EXTENSION_REQUEST::ENABLED)
2204     {
2205         printf("WARNING: AMD_device_coherent_memory requested but not currently supported by the player.\n");
2206     }
2207 
2208     if(m_MemoryBudgetEnabled)
2209     {
2210         enabledDeviceExtensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
2211     }
2212 
2213     VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
2214     deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
2215     deviceCreateInfo.ppEnabledExtensionNames = !enabledDeviceExtensions.empty() ? enabledDeviceExtensions.data() : nullptr;
2216     deviceCreateInfo.queueCreateInfoCount = m_TransferQueueFamilyIndex != m_GraphicsQueueFamilyIndex ? 2 : 1;
2217     deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
2218     deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
2219 
2220     res = vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device);
2221     if(res != VK_SUCCESS)
2222     {
2223         printf("ERROR: vkCreateDevice failed (%d)\n", res);
2224         return RESULT_ERROR_VULKAN;
2225     }
2226 
2227     // Fetch queues
2228     vkGetDeviceQueue(m_Device, m_GraphicsQueueFamilyIndex, 0, &m_GraphicsQueue);
2229     vkGetDeviceQueue(m_Device, m_TransferQueueFamilyIndex, 0, &m_TransferQueue);
2230 
2231     // Create memory allocator
2232 
2233     VmaDeviceMemoryCallbacks deviceMemoryCallbacks = {};
2234     deviceMemoryCallbacks.pfnAllocate = AllocateDeviceMemoryCallback;
2235     deviceMemoryCallbacks.pfnFree = FreeDeviceMemoryCallback;
2236 
2237     VmaAllocatorCreateInfo allocatorInfo = {};
2238     allocatorInfo.instance = m_VulkanInstance;
2239     allocatorInfo.physicalDevice = m_PhysicalDevice;
2240     allocatorInfo.device = m_Device;
2241     allocatorInfo.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
2242     allocatorInfo.pDeviceMemoryCallbacks = &deviceMemoryCallbacks;
2243     allocatorInfo.vulkanApiVersion = VULKAN_API_VERSION;
2244 
2245     if(m_MemoryBudgetEnabled)
2246     {
2247         allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
2248     }
2249 
2250     res = vmaCreateAllocator(&allocatorInfo, &m_Allocator);
2251     if(res != VK_SUCCESS)
2252     {
2253         printf("ERROR: vmaCreateAllocator failed (%d)\n", res);
2254         return RESULT_ERROR_VULKAN;
2255     }
2256 
2257     vmaGetPhysicalDeviceProperties(m_Allocator, &m_DevProps);
2258     vmaGetMemoryProperties(m_Allocator, &m_MemProps);
2259 
2260     // Create command pool
2261 
2262     VkCommandPoolCreateInfo cmdPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
2263     cmdPoolCreateInfo.queueFamilyIndex = m_TransferQueueFamilyIndex;
2264     cmdPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
2265 
2266     res = vkCreateCommandPool(m_Device, &cmdPoolCreateInfo, nullptr, &m_CommandPool);
2267     if(res != VK_SUCCESS)
2268     {
2269         printf("ERROR: vkCreateCommandPool failed (%d)\n", res);
2270         return RESULT_ERROR_VULKAN;
2271     }
2272 
2273     // Create command buffer
2274 
2275     VkCommandBufferAllocateInfo cmdBufAllocInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
2276     cmdBufAllocInfo.commandBufferCount = 1;
2277     cmdBufAllocInfo.commandPool = m_CommandPool;
2278     cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2279     res = vkAllocateCommandBuffers(m_Device, &cmdBufAllocInfo, &m_CommandBuffer);
2280     if(res != VK_SUCCESS)
2281     {
2282         printf("ERROR: vkAllocateCommandBuffers failed (%d)\n", res);
2283         return RESULT_ERROR_VULKAN;
2284     }
2285 
2286     return 0;
2287 }
2288 
FinalizeVulkan()2289 void Player::FinalizeVulkan()
2290 {
2291     if(!m_DefragmentationContexts.empty())
2292     {
2293         printf("WARNING: Defragmentation contexts not destroyed: %zu.\n", m_DefragmentationContexts.size());
2294 
2295         if(CLEANUP_LEAKED_OBJECTS)
2296         {
2297             for(const auto& it : m_DefragmentationContexts)
2298             {
2299                 vmaDefragmentationEnd(m_Allocator, it.second);
2300             }
2301         }
2302 
2303         m_DefragmentationContexts.clear();
2304     }
2305 
2306     if(!m_Allocations.empty())
2307     {
2308         printf("WARNING: Allocations not destroyed: %zu.\n", m_Allocations.size());
2309 
2310         if(CLEANUP_LEAKED_OBJECTS)
2311         {
2312             for(const auto it : m_Allocations)
2313             {
2314                 Destroy(it.second);
2315             }
2316         }
2317 
2318         m_Allocations.clear();
2319     }
2320 
2321     if(!m_Pools.empty())
2322     {
2323         printf("WARNING: Custom pools not destroyed: %zu.\n", m_Pools.size());
2324 
2325         if(CLEANUP_LEAKED_OBJECTS)
2326         {
2327             for(const auto it : m_Pools)
2328             {
2329                 vmaDestroyPool(m_Allocator, it.second.pool);
2330             }
2331         }
2332 
2333         m_Pools.clear();
2334     }
2335 
2336     vkDeviceWaitIdle(m_Device);
2337 
2338     if(m_CommandBuffer != VK_NULL_HANDLE)
2339     {
2340         vkFreeCommandBuffers(m_Device, m_CommandPool, 1, &m_CommandBuffer);
2341         m_CommandBuffer = VK_NULL_HANDLE;
2342     }
2343 
2344     if(m_CommandPool != VK_NULL_HANDLE)
2345     {
2346         vkDestroyCommandPool(m_Device, m_CommandPool, nullptr);
2347         m_CommandPool = VK_NULL_HANDLE;
2348     }
2349 
2350     if(m_Allocator != VK_NULL_HANDLE)
2351     {
2352         vmaDestroyAllocator(m_Allocator);
2353         m_Allocator = nullptr;
2354     }
2355 
2356     if(m_Device != VK_NULL_HANDLE)
2357     {
2358         vkDestroyDevice(m_Device, nullptr);
2359         m_Device = nullptr;
2360     }
2361 
2362     UnregisterDebugCallbacks();
2363 
2364     if(m_VulkanInstance != VK_NULL_HANDLE)
2365     {
2366         vkDestroyInstance(m_VulkanInstance, NULL);
2367         m_VulkanInstance = VK_NULL_HANDLE;
2368     }
2369 }
2370 
RegisterDebugCallbacks()2371 void Player::RegisterDebugCallbacks()
2372 {
2373     static const VkDebugUtilsMessageSeverityFlagsEXT DEBUG_UTILS_MESSENGER_MESSAGE_SEVERITY =
2374         //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
2375         //VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
2376         VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
2377         VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
2378     static const VkDebugUtilsMessageTypeFlagsEXT DEBUG_UTILS_MESSENGER_MESSAGE_TYPE =
2379         VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
2380         VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
2381         VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
2382 
2383     m_vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
2384         m_VulkanInstance, "vkCreateDebugUtilsMessengerEXT");
2385     m_vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
2386         m_VulkanInstance, "vkDestroyDebugUtilsMessengerEXT");
2387     assert(m_vkCreateDebugUtilsMessengerEXT);
2388     assert(m_vkDestroyDebugUtilsMessengerEXT);
2389 
2390     VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
2391     messengerCreateInfo.messageSeverity = DEBUG_UTILS_MESSENGER_MESSAGE_SEVERITY;
2392     messengerCreateInfo.messageType = DEBUG_UTILS_MESSENGER_MESSAGE_TYPE;
2393     messengerCreateInfo.pfnUserCallback = MyDebugReportCallback;
2394     VkResult res = m_vkCreateDebugUtilsMessengerEXT(m_VulkanInstance, &messengerCreateInfo, nullptr, &m_DebugUtilsMessenger);
2395     if(res != VK_SUCCESS)
2396     {
2397         printf("ERROR: vkCreateDebugUtilsMessengerEXT failed (%d)\n", res);
2398         m_DebugUtilsMessenger = VK_NULL_HANDLE;
2399     }
2400 }
2401 
UnregisterDebugCallbacks()2402 void Player::UnregisterDebugCallbacks()
2403 {
2404     if(m_DebugUtilsMessenger)
2405     {
2406         m_vkDestroyDebugUtilsMessengerEXT(m_VulkanInstance, m_DebugUtilsMessenger, nullptr);
2407     }
2408 }
2409 
Defragment()2410 void Player::Defragment()
2411 {
2412     VmaStats stats;
2413     vmaCalculateStats(m_Allocator, &stats);
2414     PrintStats(stats, "before defragmentation");
2415 
2416     const size_t allocCount = m_Allocations.size();
2417     std::vector<VmaAllocation> allocations(allocCount);
2418     size_t notNullAllocCount = 0;
2419     for(const auto& it : m_Allocations)
2420     {
2421         if(it.second.allocation != VK_NULL_HANDLE)
2422         {
2423             allocations[notNullAllocCount] = it.second.allocation;
2424             ++notNullAllocCount;
2425         }
2426     }
2427     if(notNullAllocCount == 0)
2428     {
2429         printf("    Nothing to defragment.\n");
2430         return;
2431     }
2432 
2433     allocations.resize(notNullAllocCount);
2434     std::vector<VkBool32> allocationsChanged(notNullAllocCount);
2435 
2436     VmaDefragmentationStats defragStats = {};
2437 
2438     VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
2439     cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
2440     VkResult res = vkBeginCommandBuffer(m_CommandBuffer, &cmdBufBeginInfo);
2441     if(res != VK_SUCCESS)
2442     {
2443         printf("ERROR: vkBeginCommandBuffer failed (%d)\n", res);
2444         return;
2445     }
2446 
2447     const time_point timeBeg = std::chrono::high_resolution_clock::now();
2448 
2449     VmaDefragmentationInfo2 defragInfo = {};
2450     defragInfo.allocationCount = (uint32_t)notNullAllocCount;
2451     defragInfo.pAllocations = allocations.data();
2452     defragInfo.pAllocationsChanged = allocationsChanged.data();
2453     defragInfo.maxCpuAllocationsToMove = UINT32_MAX;
2454     defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE;
2455     defragInfo.maxGpuAllocationsToMove = UINT32_MAX;
2456     defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE;
2457     defragInfo.flags = g_DefragmentationFlags;
2458     defragInfo.commandBuffer = m_CommandBuffer;
2459 
2460     VmaDefragmentationContext defragCtx = VK_NULL_HANDLE;
2461     res = vmaDefragmentationBegin(m_Allocator, &defragInfo, &defragStats, &defragCtx);
2462 
2463     const time_point timeAfterDefragBegin = std::chrono::high_resolution_clock::now();
2464 
2465     vkEndCommandBuffer(m_CommandBuffer);
2466 
2467     if(res >= VK_SUCCESS)
2468     {
2469         VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
2470         submitInfo.commandBufferCount = 1;
2471         submitInfo.pCommandBuffers = &m_CommandBuffer;
2472         vkQueueSubmit(m_TransferQueue, 1, &submitInfo, VK_NULL_HANDLE);
2473         vkQueueWaitIdle(m_TransferQueue);
2474 
2475         const time_point timeAfterGpu = std::chrono::high_resolution_clock::now();
2476 
2477         vmaDefragmentationEnd(m_Allocator, defragCtx);
2478 
2479         const time_point timeAfterDefragEnd = std::chrono::high_resolution_clock::now();
2480 
2481         const duration defragDurationBegin = timeAfterDefragBegin - timeBeg;
2482         const duration defragDurationGpu   = timeAfterGpu - timeAfterDefragBegin;
2483         const duration defragDurationEnd   = timeAfterDefragEnd - timeAfterGpu;
2484 
2485         // If anything changed.
2486         if(defragStats.allocationsMoved > 0)
2487         {
2488             // Go over allocation that changed and destroy their buffers and images.
2489             size_t i = 0;
2490             for(auto& it : m_Allocations)
2491             {
2492                 if(allocationsChanged[i] != VK_FALSE)
2493                 {
2494                     if(it.second.buffer != VK_NULL_HANDLE)
2495                     {
2496                         vkDestroyBuffer(m_Device, it.second.buffer, nullptr);
2497                         it.second.buffer = VK_NULL_HANDLE;
2498                     }
2499                     if(it.second.image != VK_NULL_HANDLE)
2500                     {
2501                         vkDestroyImage(m_Device, it.second.image, nullptr);
2502                         it.second.image = VK_NULL_HANDLE;
2503                     }
2504                 }
2505                 ++i;
2506             }
2507         }
2508 
2509         // Print statistics
2510         std::string defragDurationBeginStr;
2511         std::string defragDurationGpuStr;
2512         std::string defragDurationEndStr;
2513         SecondsToFriendlyStr(ToFloatSeconds(defragDurationBegin), defragDurationBeginStr);
2514         SecondsToFriendlyStr(ToFloatSeconds(defragDurationGpu), defragDurationGpuStr);
2515         SecondsToFriendlyStr(ToFloatSeconds(defragDurationEnd), defragDurationEndStr);
2516 
2517         printf("    Defragmentation took:\n");
2518         printf("        vmaDefragmentationBegin: %s\n", defragDurationBeginStr.c_str());
2519         printf("        GPU: %s\n", defragDurationGpuStr.c_str());
2520         printf("        vmaDefragmentationEnd: %s\n", defragDurationEndStr.c_str());
2521         printf("    VmaDefragmentationStats:\n");
2522         printf("        bytesMoved: %llu\n", defragStats.bytesMoved);
2523         printf("        bytesFreed: %llu\n", defragStats.bytesFreed);
2524         printf("        allocationsMoved: %u\n", defragStats.allocationsMoved);
2525         printf("        deviceMemoryBlocksFreed: %u\n", defragStats.deviceMemoryBlocksFreed);
2526 
2527         vmaCalculateStats(m_Allocator, &stats);
2528         PrintStats(stats, "after defragmentation");
2529     }
2530     else
2531     {
2532         printf("vmaDefragmentationBegin failed (%d).\n", res);
2533     }
2534 
2535     vkResetCommandPool(m_Device, m_CommandPool, 0);
2536 }
2537 
PrintStats()2538 void Player::PrintStats()
2539 {
2540     if(g_Verbosity == VERBOSITY::MINIMUM)
2541     {
2542         return;
2543     }
2544 
2545     m_Stats.PrintDeviceMemStats();
2546 
2547     printf("Statistics:\n");
2548     if(m_Stats.GetAllocationCreationCount() > 0)
2549     {
2550         printf("    Total allocations created: %zu\n", m_Stats.GetAllocationCreationCount());
2551     }
2552 
2553     // Buffers
2554     if(m_Stats.GetBufferCreationCount())
2555     {
2556         printf("    Total buffers created: %zu\n", m_Stats.GetBufferCreationCount());
2557         if(g_Verbosity == VERBOSITY::MAXIMUM)
2558         {
2559             printf("        Class 0 (indirect/vertex/index): %zu\n", m_Stats.GetBufferCreationCount(0));
2560             printf("        Class 1 (storage): %zu\n", m_Stats.GetBufferCreationCount(1));
2561             printf("        Class 2 (uniform): %zu\n", m_Stats.GetBufferCreationCount(2));
2562             printf("        Class 3 (other): %zu\n", m_Stats.GetBufferCreationCount(3));
2563         }
2564     }
2565 
2566     // Images
2567     const size_t imageCreationCount =
2568         m_Stats.GetImageCreationCount(0) +
2569         m_Stats.GetImageCreationCount(1) +
2570         m_Stats.GetImageCreationCount(2) +
2571         m_Stats.GetImageCreationCount(3) +
2572         m_Stats.GetLinearImageCreationCount();
2573     if(imageCreationCount > 0)
2574     {
2575         printf("    Total images created: %zu\n", imageCreationCount);
2576         if(g_Verbosity == VERBOSITY::MAXIMUM)
2577         {
2578             printf("        Class 0 (depth/stencil): %zu\n", m_Stats.GetImageCreationCount(0));
2579             printf("        Class 1 (attachment): %zu\n", m_Stats.GetImageCreationCount(1));
2580             printf("        Class 2 (sampled): %zu\n", m_Stats.GetImageCreationCount(2));
2581             printf("        Class 3 (other): %zu\n", m_Stats.GetImageCreationCount(3));
2582             if(m_Stats.GetLinearImageCreationCount() > 0)
2583             {
2584                 printf("        LINEAR tiling: %zu\n", m_Stats.GetLinearImageCreationCount());
2585             }
2586         }
2587     }
2588 
2589     if(m_Stats.GetPoolCreationCount() > 0)
2590     {
2591         printf("    Total custom pools created: %zu\n", m_Stats.GetPoolCreationCount());
2592     }
2593 
2594     float lastTime;
2595     if(!m_LastLineTimeStr.empty() && StrRangeToFloat(StrRange(m_LastLineTimeStr), lastTime))
2596     {
2597         std::string origTimeStr;
2598         SecondsToFriendlyStr(lastTime, origTimeStr);
2599         printf("    Original recording time: %s\n", origTimeStr.c_str());
2600     }
2601 
2602     // Thread statistics.
2603     const size_t threadCount = m_Threads.size();
2604     if(threadCount > 1)
2605     {
2606         uint32_t threadCallCountMax = 0;
2607         uint32_t threadCallCountSum = 0;
2608         for(const auto& it : m_Threads)
2609         {
2610             threadCallCountMax = std::max(threadCallCountMax, it.second.callCount);
2611             threadCallCountSum += it.second.callCount;
2612         }
2613         printf("    Threads making calls to VMA: %zu\n", threadCount);
2614         printf("        %.2f%% calls from most active thread.\n",
2615             (float)threadCallCountMax * 100.f / (float)threadCallCountSum);
2616     }
2617     else
2618     {
2619         printf("    VMA used from only one thread.\n");
2620     }
2621 
2622     // Function call count
2623     if(g_Verbosity == VERBOSITY::MAXIMUM)
2624     {
2625         printf("    Function call count:\n");
2626         const size_t* const functionCallCount = m_Stats.GetFunctionCallCount();
2627         for(size_t i = 0; i < (size_t)VMA_FUNCTION::Count; ++i)
2628         {
2629             if(functionCallCount[i] > 0)
2630             {
2631                 printf("        %s %zu\n", VMA_FUNCTION_NAMES[i], functionCallCount[i]);
2632             }
2633         }
2634     }
2635 
2636     // Detailed stats
2637     if(g_Verbosity == VERBOSITY::MAXIMUM)
2638     {
2639         m_Stats.PrintDetailedStats();
2640     }
2641 
2642     if(g_MemStatsEnabled)
2643     {
2644         m_Stats.PrintMemStats();
2645     }
2646 }
2647 
ValidateFunctionParameterCount(size_t lineNumber,const CsvSplit & csvSplit,size_t expectedParamCount,bool lastUnbound)2648 bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound)
2649 {
2650     bool ok;
2651     if(lastUnbound)
2652         ok = csvSplit.GetCount() >= FIRST_PARAM_INDEX + expectedParamCount - 1;
2653     else
2654         ok = csvSplit.GetCount() == FIRST_PARAM_INDEX + expectedParamCount;
2655 
2656     if(!ok)
2657     {
2658         if(IssueWarning())
2659         {
2660             printf("Line %zu: Incorrect number of function parameters.\n", lineNumber);
2661         }
2662     }
2663 
2664     return ok;
2665 }
2666 
PrepareUserData(size_t lineNumber,uint32_t allocCreateFlags,const StrRange & userDataColumn,const StrRange & wholeLine,void * & outUserData)2667 bool Player::PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData)
2668 {
2669     if(!g_UserDataEnabled)
2670     {
2671         outUserData = nullptr;
2672         return true;
2673     }
2674 
2675     // String
2676     if((allocCreateFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
2677     {
2678         const size_t len = wholeLine.end - userDataColumn.beg;
2679         m_UserDataTmpStr.resize(len + 1);
2680         memcpy(m_UserDataTmpStr.data(), userDataColumn.beg, len);
2681         m_UserDataTmpStr[len] = '\0';
2682         outUserData = m_UserDataTmpStr.data();
2683         return true;
2684     }
2685     // Pointer
2686     else
2687     {
2688         uint64_t pUserData = 0;
2689         if(StrRangeToPtr(userDataColumn, pUserData))
2690         {
2691             outUserData = (void*)(uintptr_t)pUserData;
2692             return true;
2693         }
2694     }
2695 
2696     if(IssueWarning())
2697     {
2698         printf("Line %zu: Invalid pUserData.\n", lineNumber);
2699     }
2700     outUserData = 0;
2701     return false;
2702 }
2703 
UpdateMemStats()2704 void Player::UpdateMemStats()
2705 {
2706     if(!g_MemStatsEnabled)
2707     {
2708         return;
2709     }
2710 
2711     VmaStats stats;
2712     vmaCalculateStats(m_Allocator, &stats);
2713     m_Stats.UpdateMemStats(stats);
2714 }
2715 
ExecuteCreatePool(size_t lineNumber,const CsvSplit & csvSplit)2716 void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit)
2717 {
2718     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreatePool);
2719 
2720     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false))
2721     {
2722         VmaPoolCreateInfo poolCreateInfo = {};
2723         uint64_t origPtr = 0;
2724 
2725         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), poolCreateInfo.memoryTypeIndex) &&
2726             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), poolCreateInfo.flags) &&
2727             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolCreateInfo.blockSize) &&
2728             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), poolCreateInfo.minBlockCount) &&
2729             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), poolCreateInfo.maxBlockCount) &&
2730             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) &&
2731             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr))
2732         {
2733             m_Stats.RegisterCreatePool(poolCreateInfo);
2734 
2735             Pool poolDesc = {};
2736             VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool);
2737 
2738             if(origPtr)
2739             {
2740                 if(res == VK_SUCCESS)
2741                 {
2742                     // Originally succeeded, currently succeeded.
2743                     // Just save pointer (done below).
2744                 }
2745                 else
2746                 {
2747                     // Originally succeeded, currently failed.
2748                     // Print warning. Save null pointer.
2749                     if(IssueWarning())
2750                     {
2751                         printf("Line %zu: vmaCreatePool failed (%d), while originally succeeded.\n", lineNumber, res);
2752                     }
2753                }
2754 
2755                 const auto existingIt = m_Pools.find(origPtr);
2756                 if(existingIt != m_Pools.end())
2757                 {
2758                     if(IssueWarning())
2759                     {
2760                         printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr);
2761                     }
2762                 }
2763                 m_Pools[origPtr] = poolDesc;
2764             }
2765             else
2766             {
2767                 if(res == VK_SUCCESS)
2768                 {
2769                     // Originally failed, currently succeeded.
2770                     // Print warning, destroy the pool.
2771                     if(IssueWarning())
2772                     {
2773                         printf("Line %zu: vmaCreatePool succeeded, originally failed.\n", lineNumber);
2774                     }
2775 
2776                     vmaDestroyPool(m_Allocator, poolDesc.pool);
2777                 }
2778                 else
2779                 {
2780                     // Originally failed, currently failed.
2781                     // Print warning.
2782                     if(IssueWarning())
2783                     {
2784                         printf("Line %zu: vmaCreatePool failed (%d), originally also failed.\n", lineNumber, res);
2785                     }
2786                 }
2787             }
2788 
2789             UpdateMemStats();
2790         }
2791         else
2792         {
2793             if(IssueWarning())
2794             {
2795                 printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber);
2796             }
2797         }
2798     }
2799 }
2800 
ExecuteDestroyPool(size_t lineNumber,const CsvSplit & csvSplit)2801 void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit)
2802 {
2803     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DestroyPool);
2804 
2805     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
2806     {
2807         uint64_t origPtr = 0;
2808 
2809         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
2810         {
2811             if(origPtr != 0)
2812             {
2813                 const auto it = m_Pools.find(origPtr);
2814                 if(it != m_Pools.end())
2815                 {
2816                     vmaDestroyPool(m_Allocator, it->second.pool);
2817                     UpdateMemStats();
2818                     m_Pools.erase(it);
2819                 }
2820                 else
2821                 {
2822                     if(IssueWarning())
2823                     {
2824                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
2825                     }
2826                 }
2827             }
2828         }
2829         else
2830         {
2831             if(IssueWarning())
2832             {
2833                 printf("Line %zu: Invalid parameters for vmaDestroyPool.\n", lineNumber);
2834             }
2835         }
2836     }
2837 }
2838 
ExecuteSetAllocationUserData(size_t lineNumber,const CsvSplit & csvSplit)2839 void Player::ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit)
2840 {
2841     m_Stats.RegisterFunctionCall(VMA_FUNCTION::SetAllocationUserData);
2842 
2843     if(!g_UserDataEnabled)
2844     {
2845         return;
2846     }
2847 
2848     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true))
2849     {
2850         uint64_t origPtr = 0;
2851         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
2852         {
2853             const auto it = m_Allocations.find(origPtr);
2854             if(it != m_Allocations.end())
2855             {
2856                 void* pUserData = nullptr;
2857                 if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 1)
2858                 {
2859                     PrepareUserData(
2860                         lineNumber,
2861                         it->second.allocationFlags,
2862                         csvSplit.GetRange(FIRST_PARAM_INDEX + 1),
2863                         csvSplit.GetLine(),
2864                         pUserData);
2865                 }
2866 
2867                 vmaSetAllocationUserData(m_Allocator, it->second.allocation, pUserData);
2868             }
2869             else
2870             {
2871                 if(IssueWarning())
2872                 {
2873                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
2874                 }
2875             }
2876         }
2877         else
2878         {
2879             if(IssueWarning())
2880             {
2881                 printf("Line %zu: Invalid parameters for vmaSetAllocationUserData.\n", lineNumber);
2882             }
2883         }
2884     }
2885 }
2886 
ExecuteCreateBuffer(size_t lineNumber,const CsvSplit & csvSplit)2887 void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit)
2888 {
2889     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateBuffer);
2890 
2891     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true))
2892     {
2893         VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2894         VmaAllocationCreateInfo allocCreateInfo = {};
2895         uint64_t origPool = 0;
2896         uint64_t origPtr = 0;
2897 
2898         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), bufCreateInfo.flags) &&
2899             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), bufCreateInfo.size) &&
2900             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), bufCreateInfo.usage) &&
2901             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), (uint32_t&)bufCreateInfo.sharingMode) &&
2902             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), allocCreateInfo.flags) &&
2903             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), (uint32_t&)allocCreateInfo.usage) &&
2904             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.requiredFlags) &&
2905             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.preferredFlags) &&
2906             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.memoryTypeBits) &&
2907             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPool) &&
2908             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPtr))
2909         {
2910             FindPool(lineNumber, origPool, allocCreateInfo.pool);
2911 
2912             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 11)
2913             {
2914                 PrepareUserData(
2915                     lineNumber,
2916                     allocCreateInfo.flags,
2917                     csvSplit.GetRange(FIRST_PARAM_INDEX + 11),
2918                     csvSplit.GetLine(),
2919                     allocCreateInfo.pUserData);
2920             }
2921 
2922             m_Stats.RegisterCreateBuffer(bufCreateInfo);
2923             m_Stats.RegisterCreateAllocation(allocCreateInfo);
2924 
2925             // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
2926             bufCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2927 
2928             Allocation allocDesc = { };
2929             allocDesc.allocationFlags = allocCreateInfo.flags;
2930             VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr);
2931             UpdateMemStats();
2932             AddAllocation(lineNumber, origPtr, res, "vmaCreateBuffer", std::move(allocDesc));
2933         }
2934         else
2935         {
2936             if(IssueWarning())
2937             {
2938                 printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber);
2939             }
2940         }
2941     }
2942 }
2943 
DestroyAllocation(size_t lineNumber,const CsvSplit & csvSplit,const char * functionName)2944 void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit, const char* functionName)
2945 {
2946     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
2947     {
2948         uint64_t origAllocPtr = 0;
2949 
2950         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr))
2951         {
2952             if(origAllocPtr != 0)
2953             {
2954                 const auto it = m_Allocations.find(origAllocPtr);
2955                 if(it != m_Allocations.end())
2956                 {
2957                     Destroy(it->second);
2958                     UpdateMemStats();
2959                     m_Allocations.erase(it);
2960                 }
2961                 else
2962                 {
2963                     if(IssueWarning())
2964                     {
2965                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);
2966                     }
2967                 }
2968             }
2969         }
2970         else
2971         {
2972             if(IssueWarning())
2973             {
2974                 printf("Line %zu: Invalid parameters for %s.\n", lineNumber, functionName);
2975             }
2976         }
2977     }
2978 }
2979 
PrintStats(const VmaStats & stats,const char * suffix)2980 void Player::PrintStats(const VmaStats& stats, const char* suffix)
2981 {
2982     printf("    VmaStats %s:\n", suffix);
2983     printf("        total:\n");
2984     PrintStatInfo(stats.total);
2985 
2986     if(g_Verbosity == VERBOSITY::MAXIMUM)
2987     {
2988         for(uint32_t i = 0; i < m_MemProps->memoryHeapCount; ++i)
2989         {
2990             printf("        memoryHeap[%u]:\n", i);
2991             PrintStatInfo(stats.memoryHeap[i]);
2992         }
2993         for(uint32_t i = 0; i < m_MemProps->memoryTypeCount; ++i)
2994         {
2995             printf("        memoryType[%u]:\n", i);
2996             PrintStatInfo(stats.memoryType[i]);
2997         }
2998     }
2999 }
3000 
PrintStatInfo(const VmaStatInfo & info)3001 void Player::PrintStatInfo(const VmaStatInfo& info)
3002 {
3003     printf("            blockCount: %u\n", info.blockCount);
3004     printf("            allocationCount: %u\n", info.allocationCount);
3005     printf("            unusedRangeCount: %u\n", info.unusedRangeCount);
3006     printf("            usedBytes: %llu\n", info.usedBytes);
3007     printf("            unusedBytes: %llu\n", info.unusedBytes);
3008     printf("            allocationSizeMin: %llu\n", info.allocationSizeMin);
3009     printf("            allocationSizeAvg: %llu\n", info.allocationSizeAvg);
3010     printf("            allocationSizeMax: %llu\n", info.allocationSizeMax);
3011     printf("            unusedRangeSizeMin: %llu\n", info.unusedRangeSizeMin);
3012     printf("            unusedRangeSizeAvg: %llu\n", info.unusedRangeSizeAvg);
3013     printf("            unusedRangeSizeMax: %llu\n", info.unusedRangeSizeMax);
3014 }
3015 
ExecuteCreateImage(size_t lineNumber,const CsvSplit & csvSplit)3016 void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit)
3017 {
3018     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateImage);
3019 
3020     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 21, true))
3021     {
3022         VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
3023         VmaAllocationCreateInfo allocCreateInfo = {};
3024         uint64_t origPool = 0;
3025         uint64_t origPtr = 0;
3026 
3027         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), imageCreateInfo.flags) &&
3028             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), (uint32_t&)imageCreateInfo.imageType) &&
3029             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), (uint32_t&)imageCreateInfo.format) &&
3030             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), imageCreateInfo.extent.width) &&
3031             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), imageCreateInfo.extent.height) &&
3032             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), imageCreateInfo.extent.depth) &&
3033             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), imageCreateInfo.mipLevels) &&
3034             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), imageCreateInfo.arrayLayers) &&
3035             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), (uint32_t&)imageCreateInfo.samples) &&
3036             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), (uint32_t&)imageCreateInfo.tiling) &&
3037             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), imageCreateInfo.usage) &&
3038             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), (uint32_t&)imageCreateInfo.sharingMode) &&
3039             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 12), (uint32_t&)imageCreateInfo.initialLayout) &&
3040             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 13), allocCreateInfo.flags) &&
3041             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 14), (uint32_t&)allocCreateInfo.usage) &&
3042             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 15), allocCreateInfo.requiredFlags) &&
3043             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 16), allocCreateInfo.preferredFlags) &&
3044             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 17), allocCreateInfo.memoryTypeBits) &&
3045             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 18), origPool) &&
3046             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 19), origPtr))
3047         {
3048             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3049 
3050             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 20)
3051             {
3052                 PrepareUserData(
3053                     lineNumber,
3054                     allocCreateInfo.flags,
3055                     csvSplit.GetRange(FIRST_PARAM_INDEX + 20),
3056                     csvSplit.GetLine(),
3057                     allocCreateInfo.pUserData);
3058             }
3059 
3060             m_Stats.RegisterCreateImage(imageCreateInfo);
3061             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3062 
3063             // Forcing VK_SHARING_MODE_EXCLUSIVE because we use only one queue anyway.
3064             imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
3065 
3066             Allocation allocDesc = {};
3067             allocDesc.allocationFlags = allocCreateInfo.flags;
3068             VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr);
3069             UpdateMemStats();
3070             AddAllocation(lineNumber, origPtr, res, "vmaCreateImage", std::move(allocDesc));
3071         }
3072         else
3073         {
3074             if(IssueWarning())
3075             {
3076                 printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber);
3077             }
3078         }
3079     }
3080 }
3081 
ExecuteFreeMemoryPages(size_t lineNumber,const CsvSplit & csvSplit)3082 void Player::ExecuteFreeMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)
3083 {
3084     m_Stats.RegisterFunctionCall(VMA_FUNCTION::FreeMemoryPages);
3085 
3086     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3087     {
3088         std::vector<uint64_t> origAllocPtrs;
3089         if(StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtrs))
3090         {
3091             const size_t allocCount = origAllocPtrs.size();
3092             size_t notNullCount = 0;
3093             for(size_t i = 0; i < allocCount; ++i)
3094             {
3095                 const uint64_t origAllocPtr = origAllocPtrs[i];
3096                 if(origAllocPtr != 0)
3097                 {
3098                     const auto it = m_Allocations.find(origAllocPtr);
3099                     if(it != m_Allocations.end())
3100                     {
3101                         Destroy(it->second);
3102                         m_Allocations.erase(it);
3103                         ++notNullCount;
3104                     }
3105                     else
3106                     {
3107                         if(IssueWarning())
3108                         {
3109                             printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);
3110                         }
3111                     }
3112                 }
3113             }
3114             if(notNullCount)
3115             {
3116                 UpdateMemStats();
3117             }
3118         }
3119         else
3120         {
3121             if(IssueWarning())
3122             {
3123                 printf("Line %zu: Invalid parameters for vmaFreeMemoryPages.\n", lineNumber);
3124             }
3125         }
3126     }
3127 }
3128 
ExecuteCreateLostAllocation(size_t lineNumber,const CsvSplit & csvSplit)3129 void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3130 {
3131     m_Stats.RegisterFunctionCall(VMA_FUNCTION::CreateLostAllocation);
3132 
3133     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3134     {
3135         uint64_t origPtr = 0;
3136 
3137         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3138         {
3139             Allocation allocDesc = {};
3140             vmaCreateLostAllocation(m_Allocator, &allocDesc.allocation);
3141             UpdateMemStats();
3142             m_Stats.RegisterCreateLostAllocation();
3143 
3144             AddAllocation(lineNumber, origPtr, VK_SUCCESS, "vmaCreateLostAllocation", std::move(allocDesc));
3145         }
3146         else
3147         {
3148             if(IssueWarning())
3149             {
3150                 printf("Line %zu: Invalid parameters for vmaCreateLostAllocation.\n", lineNumber);
3151             }
3152         }
3153     }
3154 }
3155 
ExecuteAllocateMemory(size_t lineNumber,const CsvSplit & csvSplit)3156 void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit)
3157 {
3158     m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemory);
3159 
3160     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true))
3161     {
3162         VkMemoryRequirements memReq = {};
3163         VmaAllocationCreateInfo allocCreateInfo = {};
3164         uint64_t origPool = 0;
3165         uint64_t origPtr = 0;
3166 
3167         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3168             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3169             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3170             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3171             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) &&
3172             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) &&
3173             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) &&
3174             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) &&
3175             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) &&
3176             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtr))
3177         {
3178             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3179 
3180             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10)
3181             {
3182                 PrepareUserData(
3183                     lineNumber,
3184                     allocCreateInfo.flags,
3185                     csvSplit.GetRange(FIRST_PARAM_INDEX + 10),
3186                     csvSplit.GetLine(),
3187                     allocCreateInfo.pUserData);
3188             }
3189 
3190             UpdateMemStats();
3191             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3192 
3193             Allocation allocDesc = {};
3194             allocDesc.allocationFlags = allocCreateInfo.flags;
3195             VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr);
3196             AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory", std::move(allocDesc));
3197         }
3198         else
3199         {
3200             if(IssueWarning())
3201             {
3202                 printf("Line %zu: Invalid parameters for vmaAllocateMemory.\n", lineNumber);
3203             }
3204         }
3205     }
3206 }
3207 
ExecuteAllocateMemoryPages(size_t lineNumber,const CsvSplit & csvSplit)3208 void Player::ExecuteAllocateMemoryPages(size_t lineNumber, const CsvSplit& csvSplit)
3209 {
3210     m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryPages);
3211 
3212     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true))
3213     {
3214         VkMemoryRequirements memReq = {};
3215         VmaAllocationCreateInfo allocCreateInfo = {};
3216         uint64_t origPool = 0;
3217         std::vector<uint64_t> origPtrs;
3218 
3219         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3220             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3221             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3222             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3223             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) &&
3224             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) &&
3225             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) &&
3226             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) &&
3227             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) &&
3228             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtrs))
3229         {
3230             const size_t allocCount = origPtrs.size();
3231             if(allocCount > 0)
3232             {
3233                 FindPool(lineNumber, origPool, allocCreateInfo.pool);
3234 
3235                 if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10)
3236                 {
3237                     PrepareUserData(
3238                         lineNumber,
3239                         allocCreateInfo.flags,
3240                         csvSplit.GetRange(FIRST_PARAM_INDEX + 10),
3241                         csvSplit.GetLine(),
3242                         allocCreateInfo.pUserData);
3243                 }
3244 
3245                 UpdateMemStats();
3246                 m_Stats.RegisterCreateAllocation(allocCreateInfo, allocCount);
3247                 m_Stats.RegisterAllocateMemoryPages(allocCount);
3248 
3249                 std::vector<VmaAllocation> allocations(allocCount);
3250 
3251                 VkResult res = vmaAllocateMemoryPages(m_Allocator, &memReq, &allocCreateInfo, allocCount, allocations.data(), nullptr);
3252                 for(size_t i = 0; i < allocCount; ++i)
3253                 {
3254                     Allocation allocDesc = {};
3255                     allocDesc.allocationFlags = allocCreateInfo.flags;
3256                     allocDesc.allocation = allocations[i];
3257                     AddAllocation(lineNumber, origPtrs[i], res, "vmaAllocateMemoryPages", std::move(allocDesc));
3258                 }
3259             }
3260         }
3261         else
3262         {
3263             if(IssueWarning())
3264             {
3265                 printf("Line %zu: Invalid parameters for vmaAllocateMemoryPages.\n", lineNumber);
3266             }
3267         }
3268     }
3269 }
3270 
ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber,const CsvSplit & csvSplit,OBJECT_TYPE objType)3271 void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType)
3272 {
3273     switch(objType)
3274     {
3275     case OBJECT_TYPE::BUFFER:
3276         m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryForBuffer);
3277         break;
3278     case OBJECT_TYPE::IMAGE:
3279         m_Stats.RegisterFunctionCall(VMA_FUNCTION::AllocateMemoryForImage);
3280         break;
3281     default: assert(0);
3282     }
3283 
3284     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 13, true))
3285     {
3286         VkMemoryRequirements memReq = {};
3287         VmaAllocationCreateInfo allocCreateInfo = {};
3288         bool requiresDedicatedAllocation = false;
3289         bool prefersDedicatedAllocation = false;
3290         uint64_t origPool = 0;
3291         uint64_t origPtr = 0;
3292 
3293         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) &&
3294             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) &&
3295             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) &&
3296             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) &&
3297             StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), requiresDedicatedAllocation) &&
3298             StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), prefersDedicatedAllocation) &&
3299             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), (uint32_t&)allocCreateInfo.usage) &&
3300             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.requiredFlags) &&
3301             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.preferredFlags) &&
3302             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), allocCreateInfo.memoryTypeBits) &&
3303             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPool) &&
3304             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), origPtr))
3305         {
3306             FindPool(lineNumber, origPool, allocCreateInfo.pool);
3307 
3308             if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 12)
3309             {
3310                 PrepareUserData(
3311                     lineNumber,
3312                     allocCreateInfo.flags,
3313                     csvSplit.GetRange(FIRST_PARAM_INDEX + 12),
3314                     csvSplit.GetLine(),
3315                     allocCreateInfo.pUserData);
3316             }
3317 
3318             UpdateMemStats();
3319             m_Stats.RegisterCreateAllocation(allocCreateInfo);
3320 
3321             if(requiresDedicatedAllocation || prefersDedicatedAllocation)
3322             {
3323                 allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
3324             }
3325 
3326             if(!m_AllocateForBufferImageWarningIssued)
3327             {
3328                 if(IssueWarning())
3329                 {
3330                     printf("Line %zu: vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage cannot be replayed accurately. Using vmaCreateAllocation instead.\n", lineNumber);
3331                 }
3332                 m_AllocateForBufferImageWarningIssued = true;
3333             }
3334 
3335             Allocation allocDesc = {};
3336             allocDesc.allocationFlags = allocCreateInfo.flags;
3337             VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr);
3338             AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage)", std::move(allocDesc));
3339         }
3340         else
3341         {
3342             if(IssueWarning())
3343             {
3344                 printf("Line %zu: Invalid parameters for vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage.\n", lineNumber);
3345             }
3346         }
3347     }
3348 }
3349 
ExecuteMapMemory(size_t lineNumber,const CsvSplit & csvSplit)3350 void Player::ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit)
3351 {
3352     m_Stats.RegisterFunctionCall(VMA_FUNCTION::MapMemory);
3353 
3354     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3355     {
3356         uint64_t origPtr = 0;
3357 
3358         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3359         {
3360             if(origPtr != 0)
3361             {
3362                 const auto it = m_Allocations.find(origPtr);
3363                 if(it != m_Allocations.end())
3364                 {
3365                     if(it->second.allocation)
3366                     {
3367                         void* pData;
3368                         VkResult res = vmaMapMemory(m_Allocator, it->second.allocation, &pData);
3369                         if(res != VK_SUCCESS)
3370                         {
3371                             printf("Line %zu: vmaMapMemory failed (%d)\n", lineNumber, res);
3372                         }
3373                     }
3374                     else
3375                     {
3376                         if(IssueWarning())
3377                         {
3378                             printf("Line %zu: Cannot call vmaMapMemory - allocation is null.\n", lineNumber);
3379                         }
3380                     }
3381                 }
3382                 else
3383                 {
3384                     if(IssueWarning())
3385                     {
3386                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3387                     }
3388                 }
3389             }
3390         }
3391         else
3392         {
3393             if(IssueWarning())
3394             {
3395                 printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber);
3396             }
3397         }
3398     }
3399 }
3400 
ExecuteUnmapMemory(size_t lineNumber,const CsvSplit & csvSplit)3401 void Player::ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit)
3402 {
3403     m_Stats.RegisterFunctionCall(VMA_FUNCTION::UnmapMemory);
3404 
3405     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3406     {
3407         uint64_t origPtr = 0;
3408 
3409         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3410         {
3411             if(origPtr != 0)
3412             {
3413                 const auto it = m_Allocations.find(origPtr);
3414                 if(it != m_Allocations.end())
3415                 {
3416                     if(it->second.allocation)
3417                     {
3418                         vmaUnmapMemory(m_Allocator, it->second.allocation);
3419                     }
3420                     else
3421                     {
3422                         if(IssueWarning())
3423                         {
3424                             printf("Line %zu: Cannot call vmaUnmapMemory - allocation is null.\n", lineNumber);
3425                         }
3426                     }
3427                 }
3428                 else
3429                 {
3430                     if(IssueWarning())
3431                     {
3432                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3433                     }
3434                 }
3435             }
3436         }
3437         else
3438         {
3439             if(IssueWarning())
3440             {
3441                 printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber);
3442             }
3443         }
3444     }
3445 }
3446 
ExecuteFlushAllocation(size_t lineNumber,const CsvSplit & csvSplit)3447 void Player::ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3448 {
3449     m_Stats.RegisterFunctionCall(VMA_FUNCTION::FlushAllocation);
3450 
3451     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false))
3452     {
3453         uint64_t origPtr = 0;
3454         uint64_t offset = 0;
3455         uint64_t size = 0;
3456 
3457         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3458             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) &&
3459             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size))
3460         {
3461             if(origPtr != 0)
3462             {
3463                 const auto it = m_Allocations.find(origPtr);
3464                 if(it != m_Allocations.end())
3465                 {
3466                     if(it->second.allocation)
3467                     {
3468                         vmaFlushAllocation(m_Allocator, it->second.allocation, offset, size);
3469                     }
3470                     else
3471                     {
3472                         if(IssueWarning())
3473                         {
3474                             printf("Line %zu: Cannot call vmaFlushAllocation - allocation is null.\n", lineNumber);
3475                         }
3476                     }
3477                 }
3478                 else
3479                 {
3480                     if(IssueWarning())
3481                     {
3482                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3483                     }
3484                 }
3485             }
3486         }
3487         else
3488         {
3489             if(IssueWarning())
3490             {
3491                 printf("Line %zu: Invalid parameters for vmaFlushAllocation.\n", lineNumber);
3492             }
3493         }
3494     }
3495 }
3496 
ExecuteInvalidateAllocation(size_t lineNumber,const CsvSplit & csvSplit)3497 void Player::ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3498 {
3499     m_Stats.RegisterFunctionCall(VMA_FUNCTION::InvalidateAllocation);
3500 
3501     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false))
3502     {
3503         uint64_t origPtr = 0;
3504         uint64_t offset = 0;
3505         uint64_t size = 0;
3506 
3507         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3508             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) &&
3509             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size))
3510         {
3511             if(origPtr != 0)
3512             {
3513                 const auto it = m_Allocations.find(origPtr);
3514                 if(it != m_Allocations.end())
3515                 {
3516                     if(it->second.allocation)
3517                     {
3518                         vmaInvalidateAllocation(m_Allocator, it->second.allocation, offset, size);
3519                     }
3520                     else
3521                     {
3522                         if(IssueWarning())
3523                         {
3524                             printf("Line %zu: Cannot call vmaInvalidateAllocation - allocation is null.\n", lineNumber);
3525                         }
3526                     }
3527                 }
3528                 else
3529                 {
3530                     if(IssueWarning())
3531                     {
3532                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3533                     }
3534                 }
3535             }
3536         }
3537         else
3538         {
3539             if(IssueWarning())
3540             {
3541                 printf("Line %zu: Invalid parameters for vmaInvalidateAllocation.\n", lineNumber);
3542             }
3543         }
3544     }
3545 }
3546 
ExecuteTouchAllocation(size_t lineNumber,const CsvSplit & csvSplit)3547 void Player::ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3548 {
3549     m_Stats.RegisterFunctionCall(VMA_FUNCTION::TouchAllocation);
3550 
3551     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3552     {
3553         uint64_t origPtr = 0;
3554         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3555         {
3556             const auto it = m_Allocations.find(origPtr);
3557             if(it != m_Allocations.end())
3558             {
3559                 if(it->second.allocation)
3560                 {
3561                     vmaTouchAllocation(m_Allocator, it->second.allocation);
3562                 }
3563                 else
3564                 {
3565                     if(IssueWarning())
3566                     {
3567                         printf("Line %zu: Cannot call vmaTouchAllocation - allocation is null.\n", lineNumber);
3568                     }
3569                 }
3570             }
3571             else
3572             {
3573                 if(IssueWarning())
3574                 {
3575                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3576                 }
3577             }
3578         }
3579         else
3580         {
3581             if(IssueWarning())
3582             {
3583                 printf("Line %zu: Invalid parameters for vmaTouchAllocation.\n", lineNumber);
3584             }
3585         }
3586     }
3587 }
3588 
ExecuteGetAllocationInfo(size_t lineNumber,const CsvSplit & csvSplit)3589 void Player::ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit)
3590 {
3591     m_Stats.RegisterFunctionCall(VMA_FUNCTION::GetAllocationInfo);
3592 
3593     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3594     {
3595         uint64_t origPtr = 0;
3596         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3597         {
3598             const auto it = m_Allocations.find(origPtr);
3599             if(it != m_Allocations.end())
3600             {
3601                 if(it->second.allocation)
3602                 {
3603                     VmaAllocationInfo allocInfo;
3604                     vmaGetAllocationInfo(m_Allocator, it->second.allocation, &allocInfo);
3605                 }
3606                 else
3607                 {
3608                     if(IssueWarning())
3609                     {
3610                         printf("Line %zu: Cannot call vmaGetAllocationInfo - allocation is null.\n", lineNumber);
3611                     }
3612                 }
3613             }
3614             else
3615             {
3616                 if(IssueWarning())
3617                 {
3618                     printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3619                 }
3620             }
3621         }
3622         else
3623         {
3624             if(IssueWarning())
3625             {
3626                 printf("Line %zu: Invalid parameters for vmaGetAllocationInfo.\n", lineNumber);
3627             }
3628         }
3629     }
3630 }
3631 
ExecuteMakePoolAllocationsLost(size_t lineNumber,const CsvSplit & csvSplit)3632 void Player::ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit)
3633 {
3634     m_Stats.RegisterFunctionCall(VMA_FUNCTION::MakePoolAllocationsLost);
3635 
3636     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3637     {
3638         uint64_t origPtr = 0;
3639 
3640         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3641         {
3642             if(origPtr != 0)
3643             {
3644                 const auto it = m_Pools.find(origPtr);
3645                 if(it != m_Pools.end())
3646                 {
3647                     vmaMakePoolAllocationsLost(m_Allocator, it->second.pool, nullptr);
3648                     UpdateMemStats();
3649                 }
3650                 else
3651                 {
3652                     if(IssueWarning())
3653                     {
3654                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
3655                     }
3656                 }
3657             }
3658         }
3659         else
3660         {
3661             if(IssueWarning())
3662             {
3663                 printf("Line %zu: Invalid parameters for vmaMakePoolAllocationsLost.\n", lineNumber);
3664             }
3665         }
3666     }
3667 }
3668 
ExecuteResizeAllocation(size_t lineNumber,const CsvSplit & csvSplit)3669 void Player::ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit)
3670 {
3671     m_Stats.RegisterFunctionCall(VMA_FUNCTION::ResizeAllocation);
3672 
3673     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, false))
3674     {
3675         uint64_t origPtr = 0;
3676         uint64_t newSize = 0;
3677 
3678         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&
3679             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), newSize))
3680         {
3681             if(origPtr != 0)
3682             {
3683                 const auto it = m_Allocations.find(origPtr);
3684                 if(it != m_Allocations.end())
3685                 {
3686                     // Do nothing - the function was deprecated and has been removed.
3687                     //vmaResizeAllocation(m_Allocator, it->second.allocation, newSize);
3688                     UpdateMemStats();
3689                 }
3690                 else
3691                 {
3692                     if(IssueWarning())
3693                     {
3694                         printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);
3695                     }
3696                 }
3697             }
3698         }
3699         else
3700         {
3701             if(IssueWarning())
3702             {
3703                 printf("Line %zu: Invalid parameters for vmaResizeAllocation.\n", lineNumber);
3704             }
3705         }
3706     }
3707 }
3708 
ExecuteDefragmentationBegin(size_t lineNumber,const CsvSplit & csvSplit)3709 void Player::ExecuteDefragmentationBegin(size_t lineNumber, const CsvSplit& csvSplit)
3710 {
3711     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationBegin);
3712 
3713     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 9, false))
3714     {
3715         VmaDefragmentationInfo2 defragInfo = {};
3716         std::vector<uint64_t> allocationOrigPtrs;
3717         std::vector<uint64_t> poolOrigPtrs;
3718         uint64_t cmdBufOrigPtr = 0;
3719         uint64_t defragCtxOrigPtr = 0;
3720 
3721         if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), defragInfo.flags) &&
3722             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), allocationOrigPtrs) &&
3723             StrRangeToPtrList(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolOrigPtrs) &&
3724             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), defragInfo.maxCpuBytesToMove) &&
3725             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), defragInfo.maxCpuAllocationsToMove) &&
3726             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), defragInfo.maxGpuBytesToMove) &&
3727             StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), defragInfo.maxGpuAllocationsToMove) &&
3728             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), cmdBufOrigPtr) &&
3729             StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), defragCtxOrigPtr))
3730         {
3731             const size_t allocationOrigPtrCount = allocationOrigPtrs.size();
3732             std::vector<VmaAllocation> allocations;
3733             allocations.reserve(allocationOrigPtrCount);
3734             for(size_t i = 0; i < allocationOrigPtrCount; ++i)
3735             {
3736                 const auto it = m_Allocations.find(allocationOrigPtrs[i]);
3737                 if(it != m_Allocations.end() && it->second.allocation)
3738                 {
3739                     allocations.push_back(it->second.allocation);
3740                 }
3741             }
3742             if(!allocations.empty())
3743             {
3744                 defragInfo.allocationCount = (uint32_t)allocations.size();
3745                 defragInfo.pAllocations = allocations.data();
3746             }
3747 
3748             const size_t poolOrigPtrCount = poolOrigPtrs.size();
3749             std::vector<VmaPool> pools;
3750             pools.reserve(poolOrigPtrCount);
3751             for(size_t i = 0; i < poolOrigPtrCount; ++i)
3752             {
3753                 const auto it = m_Pools.find(poolOrigPtrs[i]);
3754                 if(it != m_Pools.end() && it->second.pool)
3755                 {
3756                     pools.push_back(it->second.pool);
3757                 }
3758             }
3759             if(!pools.empty())
3760             {
3761                 defragInfo.poolCount = (uint32_t)pools.size();
3762                 defragInfo.pPools = pools.data();
3763             }
3764 
3765             if(allocations.size() != allocationOrigPtrCount ||
3766                 pools.size() != poolOrigPtrCount)
3767             {
3768                 if(IssueWarning())
3769                 {
3770                     printf("Line %zu: Passing %zu allocations and %zu pools to vmaDefragmentationBegin, while originally %zu allocations and %zu pools were passed.\n",
3771                         lineNumber,
3772                         allocations.size(), pools.size(),
3773                         allocationOrigPtrCount, poolOrigPtrCount);
3774                 }
3775             }
3776 
3777             if(cmdBufOrigPtr)
3778             {
3779                 VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
3780                 cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
3781                 VkResult res = vkBeginCommandBuffer(m_CommandBuffer, &cmdBufBeginInfo);
3782                 if(res == VK_SUCCESS)
3783                 {
3784                     defragInfo.commandBuffer = m_CommandBuffer;
3785                 }
3786                 else
3787                 {
3788                     printf("Line %zu: vkBeginCommandBuffer failed (%d)\n", lineNumber, res);
3789                 }
3790             }
3791 
3792             m_Stats.RegisterDefragmentation(defragInfo);
3793 
3794             VmaDefragmentationContext defragCtx = nullptr;
3795             VkResult res = vmaDefragmentationBegin(m_Allocator, &defragInfo, nullptr, &defragCtx);
3796 
3797             if(defragInfo.commandBuffer)
3798             {
3799                 vkEndCommandBuffer(m_CommandBuffer);
3800 
3801                 VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
3802                 submitInfo.commandBufferCount = 1;
3803                 submitInfo.pCommandBuffers = &m_CommandBuffer;
3804                 vkQueueSubmit(m_TransferQueue, 1, &submitInfo, VK_NULL_HANDLE);
3805                 vkQueueWaitIdle(m_TransferQueue);
3806             }
3807 
3808             if(res >= VK_SUCCESS)
3809             {
3810                 if(defragCtx)
3811                 {
3812                     if(defragCtxOrigPtr)
3813                     {
3814                         // We have defragmentation context, originally had defragmentation context: Store it.
3815                         m_DefragmentationContexts[defragCtxOrigPtr] = defragCtx;
3816                     }
3817                     else
3818                     {
3819                         // We have defragmentation context, originally it was null: End immediately.
3820                         vmaDefragmentationEnd(m_Allocator, defragCtx);
3821                     }
3822                 }
3823                 else
3824                 {
3825                     if(defragCtxOrigPtr)
3826                     {
3827                         // We have no defragmentation context, originally there was one: Store null.
3828                         m_DefragmentationContexts[defragCtxOrigPtr] = nullptr;
3829                     }
3830                     else
3831                     {
3832                         // We have no defragmentation context, originally there wasn't as well - nothing to do.
3833                     }
3834                 }
3835             }
3836             else
3837             {
3838                 if(defragCtxOrigPtr)
3839                 {
3840                     // Currently failed, originally succeeded.
3841                     if(IssueWarning())
3842                     {
3843                         printf("Line %zu: vmaDefragmentationBegin failed (%d), while originally succeeded.\n", lineNumber, res);
3844                     }
3845                 }
3846                 else
3847                 {
3848                     // Currently failed, originally don't know.
3849                     if(IssueWarning())
3850                     {
3851                         printf("Line %zu: vmaDefragmentationBegin failed (%d).\n", lineNumber, res);
3852                     }
3853                 }
3854             }
3855         }
3856         else
3857         {
3858             if(IssueWarning())
3859             {
3860                 printf("Line %zu: Invalid parameters for vmaDefragmentationBegin.\n", lineNumber);
3861             }
3862         }
3863     }
3864 }
3865 
ExecuteDefragmentationEnd(size_t lineNumber,const CsvSplit & csvSplit)3866 void Player::ExecuteDefragmentationEnd(size_t lineNumber, const CsvSplit& csvSplit)
3867 {
3868     m_Stats.RegisterFunctionCall(VMA_FUNCTION::DefragmentationEnd);
3869 
3870     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
3871     {
3872         uint64_t origPtr = 0;
3873 
3874         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3875         {
3876             if(origPtr != 0)
3877             {
3878                 const auto it = m_DefragmentationContexts.find(origPtr);
3879                 if(it != m_DefragmentationContexts.end())
3880                 {
3881                     vmaDefragmentationEnd(m_Allocator, it->second);
3882                     m_DefragmentationContexts.erase(it);
3883                 }
3884                 else
3885                 {
3886                     if(IssueWarning())
3887                     {
3888                         printf("Line %zu: Defragmentation context %llX not found.\n", lineNumber, origPtr);
3889                     }
3890                 }
3891             }
3892         }
3893         else
3894         {
3895             if(IssueWarning())
3896             {
3897                 printf("Line %zu: Invalid parameters for vmaDefragmentationEnd.\n", lineNumber);
3898             }
3899         }
3900     }
3901 }
3902 
ExecuteSetPoolName(size_t lineNumber,const CsvSplit & csvSplit)3903 void Player::ExecuteSetPoolName(size_t lineNumber, const CsvSplit& csvSplit)
3904 {
3905     m_Stats.RegisterFunctionCall(VMA_FUNCTION::SetPoolName);
3906 
3907     if(!g_UserDataEnabled)
3908     {
3909         return;
3910     }
3911 
3912     if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true))
3913     {
3914         uint64_t origPtr = 0;
3915         if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
3916         {
3917             if(origPtr != 0)
3918             {
3919                 const auto it = m_Pools.find(origPtr);
3920                 if(it != m_Pools.end())
3921                 {
3922                     std::string poolName;
3923                     csvSplit.GetRange(FIRST_PARAM_INDEX + 1).to_str(poolName);
3924                     vmaSetPoolName(m_Allocator, it->second.pool, !poolName.empty() ? poolName.c_str() : nullptr);
3925                 }
3926                 else
3927                 {
3928                     if(IssueWarning())
3929                     {
3930                         printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
3931                     }
3932                 }
3933             }
3934         }
3935         else
3936         {
3937             if(IssueWarning())
3938             {
3939                 printf("Line %zu: Invalid parameters for vmaSetPoolName.\n", lineNumber);
3940             }
3941         }
3942     }
3943 }
3944 
3945 ////////////////////////////////////////////////////////////////////////////////
3946 // Main functions
3947 
PrintCommandLineSyntax()3948 static void PrintCommandLineSyntax()
3949 {
3950     printf(
3951         "Command line syntax:\n"
3952         "    VmaReplay [Options] <SrcFile.csv>\n"
3953         "Available options:\n"
3954         "    -v <Number> - Verbosity level:\n"
3955         "        0 - Minimum verbosity. Prints only warnings and errors.\n"
3956         "        1 - Default verbosity. Prints important messages and statistics.\n"
3957         "        2 - Maximum verbosity. Prints a lot of information.\n"
3958         "    -i <Number> - Repeat playback given number of times (iterations)\n"
3959         "        Default is 1. Vulkan is reinitialized with every iteration.\n"
3960         "    --MemStats <Value> - 0 to disable or 1 to enable memory statistics.\n"
3961         "        Default is 0. Enabling it may negatively impact playback performance.\n"
3962         "    --DumpStatsAfterLine <Line> - Dump VMA statistics to JSON file after specified source file line finishes execution.\n"
3963         "        File is written to current directory with name: VmaReplay_Line####.json.\n"
3964         "        This parameter can be repeated.\n"
3965         "    --DumpDetailedStatsAfterLine <Line> - Like command above, but includes detailed map.\n"
3966         "    --DefragmentAfterLine <Line> - Defragment memory after specified source file line and print statistics.\n"
3967         "        It also prints detailed statistics to files VmaReplay_Line####_Defragment*.json\n"
3968         "    --DefragmentationFlags <Flags> - Flags to be applied when using DefragmentAfterLine.\n"
3969         "    --Lines <Ranges> - Replay only limited set of lines from file\n"
3970         "        Ranges is comma-separated list of ranges, e.g. \"-10,15,18-25,31-\".\n"
3971         "    --PhysicalDevice <Index> - Choice of Vulkan physical device. Default: 0.\n"
3972         "    --UserData <Value> - 0 to disable or 1 to enable setting pUserData during playback.\n"
3973         "        Default is 1. Affects both creation of buffers and images, as well as calls to vmaSetAllocationUserData.\n"
3974         "    --VK_LAYER_KHRONOS_validation <Value> - 0 to disable or 1 to enable validation layers.\n"
3975         "        By default the layers are silently enabled if available.\n"
3976         "    --VK_EXT_memory_budget <Value> - 0 to disable or 1 to enable this extension.\n"
3977         "        By default the extension is silently enabled if available.\n"
3978     );
3979 }
3980 
ProcessFile(size_t iterationIndex,const char * data,size_t numBytes,duration & outDuration)3981 static int ProcessFile(size_t iterationIndex, const char* data, size_t numBytes, duration& outDuration)
3982 {
3983     outDuration = duration::max();
3984 
3985     const bool useLineRanges = !g_LineRanges.IsEmpty();
3986     const bool useDumpStatsAfterLine = !g_DumpStatsAfterLine.empty();
3987     const bool useDefragmentAfterLine = !g_DefragmentAfterLine.empty();
3988 
3989     LineSplit lineSplit(data, numBytes);
3990     StrRange line;
3991 
3992     if(!lineSplit.GetNextLine(line) ||
3993         !StrRangeEq(line, "Vulkan Memory Allocator,Calls recording"))
3994     {
3995         printf("ERROR: Incorrect file format.\n");
3996         return RESULT_ERROR_FORMAT;
3997     }
3998 
3999     if(!lineSplit.GetNextLine(line) || !ParseFileVersion(line) || !ValidateFileVersion())
4000     {
4001         printf("ERROR: Incorrect file format version.\n");
4002         return RESULT_ERROR_FORMAT;
4003     }
4004 
4005     if(g_Verbosity == VERBOSITY::MAXIMUM)
4006     {
4007         printf("Format version: %u,%u\n",
4008             GetVersionMajor(g_FileVersion),
4009             GetVersionMinor(g_FileVersion));
4010     }
4011 
4012     // Parse configuration
4013     const bool configEnabled = g_FileVersion >= MakeVersion(1, 3);
4014     ConfigurationParser configParser;
4015     if(configEnabled)
4016     {
4017         if(!configParser.Parse(lineSplit))
4018         {
4019             return RESULT_ERROR_FORMAT;
4020         }
4021     }
4022 
4023     Player player;
4024     int result = player.Init();
4025 
4026     if(configEnabled)
4027     {
4028         player.ApplyConfig(configParser);
4029     }
4030 
4031     size_t executedLineCount = 0;
4032     if(result == 0)
4033     {
4034         if(g_Verbosity > VERBOSITY::MINIMUM)
4035         {
4036             if(useLineRanges)
4037             {
4038                 printf("Playing #%zu (limited range of lines)...\n", iterationIndex + 1);
4039             }
4040             else
4041             {
4042                 printf("Playing #%zu...\n", iterationIndex + 1);
4043             }
4044         }
4045 
4046         const time_point timeBeg = std::chrono::high_resolution_clock::now();
4047 
4048         while(lineSplit.GetNextLine(line))
4049         {
4050             const size_t currLineNumber = lineSplit.GetNextLineIndex();
4051 
4052             bool execute = true;
4053             if(useLineRanges)
4054             {
4055                 execute = g_LineRanges.Includes(currLineNumber);
4056             }
4057 
4058             if(execute)
4059             {
4060                 player.ExecuteLine(currLineNumber, line);
4061                 ++executedLineCount;
4062             }
4063 
4064             while(useDumpStatsAfterLine &&
4065                 g_DumpStatsAfterLineNextIndex < g_DumpStatsAfterLine.size() &&
4066                 currLineNumber >= g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line)
4067             {
4068                 const size_t requestedLine = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].line;
4069                 const bool detailed = g_DumpStatsAfterLine[g_DumpStatsAfterLineNextIndex].detailed;
4070 
4071                 if(g_Verbosity == VERBOSITY::MAXIMUM)
4072                 {
4073                     printf("Dumping %sstats after line %zu actual line %zu...\n",
4074                         detailed ? "detailed " : "",
4075                         requestedLine,
4076                         currLineNumber);
4077                 }
4078 
4079                 player.DumpStats("VmaReplay_Line%04zu.json", requestedLine, detailed);
4080 
4081                 ++g_DumpStatsAfterLineNextIndex;
4082             }
4083 
4084             while(useDefragmentAfterLine &&
4085                 g_DefragmentAfterLineNextIndex < g_DefragmentAfterLine.size() &&
4086                 currLineNumber >= g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex])
4087             {
4088                 const size_t requestedLine = g_DefragmentAfterLine[g_DefragmentAfterLineNextIndex];
4089                 if(g_Verbosity >= VERBOSITY::DEFAULT)
4090                 {
4091                     printf("Defragmenting after line %zu actual line %zu...\n",
4092                         requestedLine,
4093                         currLineNumber);
4094                 }
4095 
4096                 player.DumpStats("VmaReplay_Line%04zu_Defragment_1Before.json", requestedLine, true);
4097                 player.Defragment();
4098                 player.DumpStats("VmaReplay_Line%04zu_Defragment_2After.json", requestedLine, true);
4099 
4100                 ++g_DefragmentAfterLineNextIndex;
4101             }
4102         }
4103 
4104         const duration playDuration = std::chrono::high_resolution_clock::now() - timeBeg;
4105         outDuration = playDuration;
4106 
4107         // End stats.
4108         if(g_Verbosity > VERBOSITY::MINIMUM)
4109         {
4110             std::string playDurationStr;
4111             SecondsToFriendlyStr(ToFloatSeconds(playDuration), playDurationStr);
4112 
4113             printf("Done.\n");
4114             printf("Playback took: %s\n", playDurationStr.c_str());
4115         }
4116         if(g_Verbosity == VERBOSITY::MAXIMUM)
4117         {
4118             printf("File lines: %zu\n", lineSplit.GetNextLineIndex());
4119             printf("Executed %zu file lines\n", executedLineCount);
4120         }
4121 
4122         player.PrintStats();
4123     }
4124 
4125     return result;
4126 }
4127 
ProcessFile()4128 static int ProcessFile()
4129 {
4130     if(g_Verbosity > VERBOSITY::MINIMUM)
4131     {
4132         printf("Loading file \"%s\"...\n", g_FilePath.c_str());
4133     }
4134     int result = 0;
4135 
4136     FILE* file = nullptr;
4137     const errno_t err = fopen_s(&file, g_FilePath.c_str(), "rb");
4138     if(err == 0)
4139     {
4140         _fseeki64(file, 0, SEEK_END);
4141         const size_t fileSize = (size_t)_ftelli64(file);
4142         _fseeki64(file, 0, SEEK_SET);
4143 
4144         if(fileSize > 0)
4145         {
4146             std::vector<char> fileContents(fileSize);
4147             fread(fileContents.data(), 1, fileSize, file);
4148 
4149             // Begin stats.
4150             if(g_Verbosity == VERBOSITY::MAXIMUM)
4151             {
4152                 printf("File size: %zu B\n", fileSize);
4153             }
4154 
4155             duration durationSum = duration::zero();
4156             for(size_t i = 0; i < g_IterationCount; ++i)
4157             {
4158                 duration currDuration;
4159                 ProcessFile(i, fileContents.data(), fileContents.size(), currDuration);
4160                 durationSum += currDuration;
4161             }
4162 
4163             if(g_IterationCount > 1)
4164             {
4165                 std::string playDurationStr;
4166                 SecondsToFriendlyStr(ToFloatSeconds(durationSum / g_IterationCount), playDurationStr);
4167                 printf("Average playback time from %zu iterations: %s\n", g_IterationCount, playDurationStr.c_str());
4168             }
4169         }
4170         else
4171         {
4172             printf("ERROR: Source file is empty.\n");
4173             result = RESULT_ERROR_SOURCE_FILE;
4174         }
4175 
4176         fclose(file);
4177     }
4178     else
4179     {
4180         printf("ERROR: Couldn't open file (%i).\n", err);
4181         result = RESULT_ERROR_SOURCE_FILE;
4182     }
4183 
4184     return result;
4185 }
4186 
main2(int argc,char ** argv)4187 static int main2(int argc, char** argv)
4188 {
4189     CmdLineParser cmdLineParser(argc, argv);
4190 
4191     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VERBOSITY, 'v', true);
4192     cmdLineParser.RegisterOpt(CMD_LINE_OPT_ITERATIONS, 'i', true);
4193     cmdLineParser.RegisterOpt(CMD_LINE_OPT_LINES, "Lines", true);
4194     cmdLineParser.RegisterOpt(CMD_LINE_OPT_PHYSICAL_DEVICE, "PhysicalDevice", true);
4195     cmdLineParser.RegisterOpt(CMD_LINE_OPT_USER_DATA, "UserData", true);
4196     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_EXT_MEMORY_BUDGET, "VK_EXT_memory_budget", true);
4197     cmdLineParser.RegisterOpt(CMD_LINE_OPT_VK_LAYER_KHRONOS_VALIDATION, VALIDATION_LAYER_NAME, true);
4198     cmdLineParser.RegisterOpt(CMD_LINE_OPT_MEM_STATS, "MemStats", true);
4199     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_STATS_AFTER_LINE, "DumpStatsAfterLine", true);
4200     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE, "DefragmentAfterLine", true);
4201     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DEFRAGMENTATION_FLAGS, "DefragmentationFlags", true);
4202     cmdLineParser.RegisterOpt(CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE, "DumpDetailedStatsAfterLine", true);
4203 
4204     CmdLineParser::RESULT res;
4205     while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END)
4206     {
4207         switch(res)
4208         {
4209         case CmdLineParser::RESULT_OPT:
4210             switch(cmdLineParser.GetOptId())
4211             {
4212             case CMD_LINE_OPT_VERBOSITY:
4213                 {
4214                     uint32_t verbosityVal = UINT32_MAX;
4215                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), verbosityVal) &&
4216                         verbosityVal < (uint32_t)VERBOSITY::COUNT)
4217                     {
4218                         g_Verbosity = (VERBOSITY)verbosityVal;
4219                     }
4220                     else
4221                     {
4222                         PrintCommandLineSyntax();
4223                         return RESULT_ERROR_COMMAND_LINE;
4224                     }
4225                 }
4226                 break;
4227             case CMD_LINE_OPT_ITERATIONS:
4228                 if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_IterationCount))
4229                 {
4230                     PrintCommandLineSyntax();
4231                     return RESULT_ERROR_COMMAND_LINE;
4232                 }
4233                 break;
4234             case CMD_LINE_OPT_LINES:
4235                 if(!g_LineRanges.Parse(StrRange(cmdLineParser.GetParameter())))
4236                 {
4237                     PrintCommandLineSyntax();
4238                     return RESULT_ERROR_COMMAND_LINE;
4239                 }
4240                 break;
4241             case CMD_LINE_OPT_PHYSICAL_DEVICE:
4242                 if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_PhysicalDeviceIndex))
4243                 {
4244                     PrintCommandLineSyntax();
4245                     return RESULT_ERROR_COMMAND_LINE;
4246                 }
4247                 break;
4248             case CMD_LINE_OPT_USER_DATA:
4249                 if(!StrRangeToBool(StrRange(cmdLineParser.GetParameter()), g_UserDataEnabled))
4250                 {
4251                     PrintCommandLineSyntax();
4252                     return RESULT_ERROR_COMMAND_LINE;
4253                 }
4254                 break;
4255             case CMD_LINE_OPT_VK_EXT_MEMORY_BUDGET:
4256                 {
4257                     bool newValue;
4258                     if(StrRangeToBool(StrRange(cmdLineParser.GetParameter()), newValue))
4259                     {
4260                         g_VK_EXT_memory_budget_request = newValue ?
4261                             VULKAN_EXTENSION_REQUEST::ENABLED :
4262                             VULKAN_EXTENSION_REQUEST::DISABLED;
4263                     }
4264                     else
4265                     {
4266                         PrintCommandLineSyntax();
4267                         return RESULT_ERROR_COMMAND_LINE;
4268                     }
4269                 }
4270                 break;
4271             case CMD_LINE_OPT_VK_LAYER_KHRONOS_VALIDATION:
4272                 {
4273                     bool newValue;
4274                     if(StrRangeToBool(StrRange(cmdLineParser.GetParameter()), newValue))
4275                     {
4276                         g_VK_LAYER_KHRONOS_validation = newValue ?
4277                             VULKAN_EXTENSION_REQUEST::ENABLED :
4278                             VULKAN_EXTENSION_REQUEST::DISABLED;
4279                     }
4280                     else
4281                     {
4282                         PrintCommandLineSyntax();
4283                         return RESULT_ERROR_COMMAND_LINE;
4284                     }
4285                 }
4286                 break;
4287             case CMD_LINE_OPT_MEM_STATS:
4288                 if(!StrRangeToBool(StrRange(cmdLineParser.GetParameter()), g_MemStatsEnabled))
4289                 {
4290                     PrintCommandLineSyntax();
4291                     return RESULT_ERROR_COMMAND_LINE;
4292                 }
4293                 break;
4294             case CMD_LINE_OPT_DUMP_STATS_AFTER_LINE:
4295             case CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE:
4296                 {
4297                     size_t line;
4298                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))
4299                     {
4300                         const bool detailed =
4301                             cmdLineParser.GetOptId() == CMD_LINE_OPT_DUMP_DETAILED_STATS_AFTER_LINE;
4302                         g_DumpStatsAfterLine.push_back({line, detailed});
4303                     }
4304                     else
4305                     {
4306                         PrintCommandLineSyntax();
4307                         return RESULT_ERROR_COMMAND_LINE;
4308                     }
4309                 }
4310                 break;
4311             case CMD_LINE_OPT_DEFRAGMENT_AFTER_LINE:
4312                 {
4313                     size_t line;
4314                     if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), line))
4315                     {
4316                         g_DefragmentAfterLine.push_back(line);
4317                     }
4318                     else
4319                     {
4320                         PrintCommandLineSyntax();
4321                         return RESULT_ERROR_COMMAND_LINE;
4322                     }
4323                 }
4324                 break;
4325             case CMD_LINE_OPT_DEFRAGMENTATION_FLAGS:
4326                 {
4327                     if(!StrRangeToUint(StrRange(cmdLineParser.GetParameter()), g_DefragmentationFlags))
4328                     {
4329                         PrintCommandLineSyntax();
4330                         return RESULT_ERROR_COMMAND_LINE;
4331                     }
4332                 }
4333                 break;
4334             default:
4335                 assert(0);
4336             }
4337             break;
4338         case CmdLineParser::RESULT_PARAMETER:
4339             if(g_FilePath.empty())
4340             {
4341                 g_FilePath = cmdLineParser.GetParameter();
4342             }
4343             else
4344             {
4345                 PrintCommandLineSyntax();
4346                 return RESULT_ERROR_COMMAND_LINE;
4347             }
4348             break;
4349         case CmdLineParser::RESULT_ERROR:
4350             PrintCommandLineSyntax();
4351             return RESULT_ERROR_COMMAND_LINE;
4352             break;
4353         default:
4354             assert(0);
4355         }
4356     }
4357 
4358     // Postprocess command line parameters.
4359 
4360     if(g_FilePath.empty())
4361     {
4362         PrintCommandLineSyntax();
4363         return RESULT_ERROR_COMMAND_LINE;
4364     }
4365 
4366     // Sort g_DumpStatsAfterLine and make unique.
4367     std::sort(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end());
4368     g_DumpStatsAfterLine.erase(
4369         std::unique(g_DumpStatsAfterLine.begin(), g_DumpStatsAfterLine.end()),
4370         g_DumpStatsAfterLine.end());
4371 
4372     // Sort g_DefragmentAfterLine and make unique.
4373     std::sort(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end());
4374     g_DefragmentAfterLine.erase(
4375         std::unique(g_DefragmentAfterLine.begin(), g_DefragmentAfterLine.end()),
4376         g_DefragmentAfterLine.end());
4377 
4378     return ProcessFile();
4379 }
4380 
main(int argc,char ** argv)4381 int main(int argc, char** argv)
4382 {
4383     try
4384     {
4385         return main2(argc, argv);
4386     }
4387     catch(const std::exception& e)
4388     {
4389         printf("ERROR: %s\n", e.what());
4390         return RESULT_EXCEPTION;
4391     }
4392     catch(...)
4393     {
4394         printf("UNKNOWN ERROR\n");
4395         return RESULT_EXCEPTION;
4396     }
4397 }
4398