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