• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // MemoryTracking.cpp:
7 //    Implements the class methods in MemoryTracking.h.
8 //
9 
10 #include "libANGLE/renderer/vulkan/MemoryTracking.h"
11 
12 #include "common/debug.h"
13 #include "libANGLE/renderer/vulkan/RendererVk.h"
14 
15 // Consts
16 namespace
17 {
18 // This flag is used for memory allocation tracking using allocation size counters.
19 constexpr bool kTrackMemoryAllocationSizes = true;
20 #if defined(ANGLE_ENABLE_MEMORY_ALLOC_LOGGING)
21 // Flag used for logging memory allocations and deallocations.
22 constexpr bool kTrackMemoryAllocationDebug = true;
23 static_assert(kTrackMemoryAllocationSizes,
24               "kTrackMemoryAllocationSizes must be enabled to use kTrackMemoryAllocationDebug.");
25 #else
26 // Only the allocation size counters are used (if enabled).
27 constexpr bool kTrackMemoryAllocationDebug = false;
28 #endif
29 }  // namespace
30 
31 namespace rx
32 {
33 
34 namespace
35 {
36 // Output memory log stream based on level of severity.
OutputMemoryLogStream(std::stringstream & outStream,vk::MemoryLogSeverity severity)37 void OutputMemoryLogStream(std::stringstream &outStream, vk::MemoryLogSeverity severity)
38 {
39     if (!kTrackMemoryAllocationSizes)
40     {
41         return;
42     }
43 
44     switch (severity)
45     {
46         case vk::MemoryLogSeverity::INFO:
47             INFO() << outStream.str();
48             break;
49         case vk::MemoryLogSeverity::WARN:
50             WARN() << outStream.str();
51             break;
52         default:
53             UNREACHABLE();
54             break;
55     }
56 }
57 
58 // Check for currently allocated memory. It is used at the end of the renderer object and when
59 // there is an allocation error (from ANGLE_VK_TRY()).
CheckForCurrentMemoryAllocations(RendererVk * renderer,vk::MemoryLogSeverity severity)60 void CheckForCurrentMemoryAllocations(RendererVk *renderer, vk::MemoryLogSeverity severity)
61 {
62     if (kTrackMemoryAllocationSizes)
63     {
64         for (uint32_t i = 0; i < vk::kMemoryAllocationTypeCount; i++)
65         {
66             if (renderer->getMemoryAllocationTracker()->getActiveMemoryAllocationsSize(i) == 0)
67             {
68                 continue;
69             }
70 
71             std::stringstream outStream;
72 
73             outStream << "Currently allocated size for memory allocation type ("
74                       << vk::kMemoryAllocationTypeMessage[i] << "): "
75                       << renderer->getMemoryAllocationTracker()->getActiveMemoryAllocationsSize(i)
76                       << " | Count: "
77                       << renderer->getMemoryAllocationTracker()->getActiveMemoryAllocationsCount(i)
78                       << std::endl;
79 
80             for (uint32_t heapIndex = 0;
81                  heapIndex < renderer->getMemoryProperties().getMemoryHeapCount(); heapIndex++)
82             {
83                 outStream
84                     << "--> Heap index " << heapIndex << ": "
85                     << renderer->getMemoryAllocationTracker()->getActiveHeapMemoryAllocationsSize(
86                            i, heapIndex)
87                     << " | Count: "
88                     << renderer->getMemoryAllocationTracker()->getActiveHeapMemoryAllocationsCount(
89                            i, heapIndex)
90                     << std::endl;
91             }
92 
93             // Output the log stream based on the level of severity.
94             OutputMemoryLogStream(outStream, severity);
95         }
96     }
97 }
98 
99 // In case of an allocation error, log pending memory allocation if the size in non-zero.
LogPendingMemoryAllocation(RendererVk * renderer,vk::MemoryLogSeverity severity)100 void LogPendingMemoryAllocation(RendererVk *renderer, vk::MemoryLogSeverity severity)
101 {
102     if (!kTrackMemoryAllocationSizes)
103     {
104         return;
105     }
106 
107     vk::MemoryAllocationType allocInfo =
108         renderer->getMemoryAllocationTracker()->getPendingMemoryAllocationType();
109     VkDeviceSize allocSize =
110         renderer->getMemoryAllocationTracker()->getPendingMemoryAllocationSize();
111     uint32_t memoryHeapIndex = renderer->getMemoryProperties().getHeapIndexForMemoryType(
112         renderer->getMemoryAllocationTracker()->getPendingMemoryTypeIndex());
113 
114     if (allocSize != 0)
115     {
116         std::stringstream outStream;
117 
118         outStream << "Pending allocation size for memory allocation type ("
119                   << vk::kMemoryAllocationTypeMessage[ToUnderlying(allocInfo)]
120                   << ") for heap index " << memoryHeapIndex << ": " << allocSize;
121 
122         // Output the log stream based on the level of severity.
123         OutputMemoryLogStream(outStream, severity);
124     }
125 }
126 
LogMemoryHeapStats(RendererVk * renderer,vk::MemoryLogSeverity severity)127 void LogMemoryHeapStats(RendererVk *renderer, vk::MemoryLogSeverity severity)
128 {
129     if (!kTrackMemoryAllocationSizes)
130     {
131         return;
132     }
133 
134     // Log stream for the heap information.
135     std::stringstream outStream;
136 
137     // VkPhysicalDeviceMemoryProperties2 enables the use of memory budget properties if
138     // supported.
139     VkPhysicalDeviceMemoryProperties2KHR memoryProperties;
140     memoryProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR;
141     memoryProperties.pNext = nullptr;
142 
143     VkPhysicalDeviceMemoryBudgetPropertiesEXT memoryBudgetProperties;
144     memoryBudgetProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
145     memoryBudgetProperties.pNext = nullptr;
146 
147     if (renderer->getFeatures().supportsMemoryBudget.enabled)
148     {
149         vk::AddToPNextChain(&memoryProperties, &memoryBudgetProperties);
150     }
151 
152     vkGetPhysicalDeviceMemoryProperties2KHR(renderer->getPhysicalDevice(), &memoryProperties);
153 
154     // Add memory heap information to the stream.
155     outStream << "Memory heap info" << std::endl;
156 
157     outStream << std::endl << "* Available memory heaps:" << std::endl;
158     for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++)
159     {
160         outStream << std::dec << i
161                   << " | Heap size: " << memoryProperties.memoryProperties.memoryHeaps[i].size
162                   << " | Flags: 0x" << std::hex
163                   << memoryProperties.memoryProperties.memoryHeaps[i].flags << std::endl;
164     }
165 
166     if (renderer->getFeatures().supportsMemoryBudget.enabled)
167     {
168         outStream << std::endl << "* Available memory budget and usage per heap:" << std::endl;
169         for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++)
170         {
171             outStream << std::dec << i << " | Heap budget: " << memoryBudgetProperties.heapBudget[i]
172                       << " | Heap usage: " << memoryBudgetProperties.heapUsage[i] << std::endl;
173         }
174     }
175 
176     outStream << std::endl << "* Available memory types:" << std::endl;
177     for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryTypeCount; i++)
178     {
179         outStream << std::dec << i
180                   << " | Heap index: " << memoryProperties.memoryProperties.memoryTypes[i].heapIndex
181                   << " | Property flags: 0x" << std::hex
182                   << memoryProperties.memoryProperties.memoryTypes[i].propertyFlags << std::endl;
183     }
184 
185     // Output the log stream based on the level of severity.
186     OutputMemoryLogStream(outStream, severity);
187 }
188 }  // namespace
189 
MemoryAllocationTracker(RendererVk * renderer)190 MemoryAllocationTracker::MemoryAllocationTracker(RendererVk *renderer)
191     : mRenderer(renderer), mMemoryAllocationID(0)
192 {}
193 
initMemoryTrackers()194 void MemoryAllocationTracker::initMemoryTrackers()
195 {
196     // Allocation counters are initialized here to keep track of the size and count of the memory
197     // allocations.
198     for (size_t allocTypeIndex = 0; allocTypeIndex < mActiveMemoryAllocationsSize.size();
199          allocTypeIndex++)
200     {
201         mActiveMemoryAllocationsSize[allocTypeIndex]  = 0;
202         mActiveMemoryAllocationsCount[allocTypeIndex] = 0;
203 
204         // Per-heap allocation counters are initialized here.
205         for (size_t heapIndex = 0;
206              heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount(); heapIndex++)
207         {
208             mActivePerHeapMemoryAllocationsSize[allocTypeIndex][heapIndex]  = 0;
209             mActivePerHeapMemoryAllocationsCount[allocTypeIndex][heapIndex] = 0;
210         }
211     }
212 
213     resetPendingMemoryAlloc();
214 }
215 
onDestroy()216 void MemoryAllocationTracker::onDestroy()
217 {
218     if (kTrackMemoryAllocationDebug)
219     {
220         CheckForCurrentMemoryAllocations(mRenderer, vk::MemoryLogSeverity::INFO);
221     }
222 }
223 
onDeviceInit()224 void MemoryAllocationTracker::onDeviceInit()
225 {
226     if (kTrackMemoryAllocationDebug)
227     {
228         LogMemoryHeapStats(mRenderer, vk::MemoryLogSeverity::INFO);
229     }
230 }
231 
logMemoryStatsOnError()232 void MemoryAllocationTracker::logMemoryStatsOnError()
233 {
234     CheckForCurrentMemoryAllocations(mRenderer, vk::MemoryLogSeverity::WARN);
235     LogPendingMemoryAllocation(mRenderer, vk::MemoryLogSeverity::WARN);
236     LogMemoryHeapStats(mRenderer, vk::MemoryLogSeverity::WARN);
237 }
238 
onMemoryAllocImpl(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex,void * handle)239 void MemoryAllocationTracker::onMemoryAllocImpl(vk::MemoryAllocationType allocType,
240                                                 VkDeviceSize size,
241                                                 uint32_t memoryTypeIndex,
242                                                 void *handle)
243 {
244     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
245 
246     if (kTrackMemoryAllocationDebug)
247     {
248         // If enabled (debug layers), we keep more details in the memory tracker, such as handle,
249         // and log the action to the output.
250         std::unique_lock<std::mutex> lock(mMemoryAllocationMutex);
251 
252         uint32_t allocTypeIndex = ToUnderlying(allocType);
253         uint32_t memoryHeapIndex =
254             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
255         mActiveMemoryAllocationsCount[allocTypeIndex]++;
256         mActiveMemoryAllocationsSize[allocTypeIndex] += size;
257         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex]++;
258         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] += size;
259 
260         // Add the new allocation to the memory tracker.
261         vk::MemoryAllocationInfo memAllocLogInfo;
262         memAllocLogInfo.id              = ++mMemoryAllocationID;
263         memAllocLogInfo.allocType       = allocType;
264         memAllocLogInfo.memoryHeapIndex = memoryHeapIndex;
265         memAllocLogInfo.size            = size;
266         memAllocLogInfo.handle          = handle;
267 
268         vk::MemoryAllocInfoMapKey memoryAllocInfoMapKey(memAllocLogInfo.handle);
269         mMemoryAllocationRecord[angle::getBacktraceInfo()].insert(
270             std::make_pair(memoryAllocInfoMapKey, memAllocLogInfo));
271 
272         INFO() << "Memory allocation: (id " << memAllocLogInfo.id << ") for object "
273                << memAllocLogInfo.handle << " | Size: " << memAllocLogInfo.size
274                << " | Type: " << vk::kMemoryAllocationTypeMessage[allocTypeIndex]
275                << " | Memory type index: " << memoryTypeIndex
276                << " | Heap index: " << memAllocLogInfo.memoryHeapIndex;
277 
278         resetPendingMemoryAlloc();
279     }
280     else if (kTrackMemoryAllocationSizes)
281     {
282         // Add the new allocation size to the allocation counter.
283         uint32_t allocTypeIndex = ToUnderlying(allocType);
284         mActiveMemoryAllocationsCount[allocTypeIndex]++;
285         mActiveMemoryAllocationsSize[allocTypeIndex] += size;
286 
287         uint32_t memoryHeapIndex =
288             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
289         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex].fetch_add(
290             1, std::memory_order_relaxed);
291         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex].fetch_add(
292             size, std::memory_order_relaxed);
293 
294         resetPendingMemoryAlloc();
295     }
296 }
297 
onMemoryDeallocImpl(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex,void * handle)298 void MemoryAllocationTracker::onMemoryDeallocImpl(vk::MemoryAllocationType allocType,
299                                                   VkDeviceSize size,
300                                                   uint32_t memoryTypeIndex,
301                                                   void *handle)
302 {
303     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
304 
305     if (kTrackMemoryAllocationDebug)
306     {
307         // If enabled (debug layers), we keep more details in the memory tracker, such as handle,
308         // and log the action to the output. The memory allocation tracker uses the backtrace info
309         // as key, if available.
310         for (auto &memInfoPerBacktrace : mMemoryAllocationRecord)
311         {
312             vk::MemoryAllocInfoMapKey memoryAllocInfoMapKey(handle);
313             MemoryAllocInfoMap &memInfoMap = memInfoPerBacktrace.second;
314             std::unique_lock<std::mutex> lock(mMemoryAllocationMutex);
315 
316             if (memInfoMap.find(memoryAllocInfoMapKey) != memInfoMap.end())
317             {
318                 // Object found; remove it from the allocation tracker.
319                 vk::MemoryAllocationInfo *memInfoEntry = &memInfoMap[memoryAllocInfoMapKey];
320                 ASSERT(memInfoEntry->allocType == allocType && memInfoEntry->size == size);
321 
322                 uint32_t allocTypeIndex = ToUnderlying(memInfoEntry->allocType);
323                 uint32_t memoryHeapIndex =
324                     mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
325                 ASSERT(mActiveMemoryAllocationsCount[allocTypeIndex] != 0 &&
326                        mActiveMemoryAllocationsSize[allocTypeIndex] >= size);
327                 ASSERT(memoryHeapIndex == memInfoEntry->memoryHeapIndex &&
328                        mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex] != 0 &&
329                        mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] >=
330                            size);
331                 mActiveMemoryAllocationsCount[allocTypeIndex]--;
332                 mActiveMemoryAllocationsSize[allocTypeIndex] -= size;
333                 mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex]--;
334                 mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] -= size;
335 
336                 INFO() << "Memory deallocation: (id " << memInfoEntry->id << ") for object "
337                        << memInfoEntry->handle << " | Size: " << memInfoEntry->size
338                        << " | Type: " << vk::kMemoryAllocationTypeMessage[allocTypeIndex]
339                        << " | Memory type index: " << memoryTypeIndex
340                        << " | Heap index: " << memInfoEntry->memoryHeapIndex;
341 
342                 memInfoMap.erase(memoryAllocInfoMapKey);
343             }
344         }
345     }
346     else if (kTrackMemoryAllocationSizes)
347     {
348         // Remove the allocation size from the allocation counter.
349         uint32_t allocTypeIndex = ToUnderlying(allocType);
350         ASSERT(mActiveMemoryAllocationsCount[allocTypeIndex] != 0 &&
351                mActiveMemoryAllocationsSize[allocTypeIndex] >= size);
352         mActiveMemoryAllocationsCount[allocTypeIndex]--;
353         mActiveMemoryAllocationsSize[allocTypeIndex] -= size;
354 
355         uint32_t memoryHeapIndex =
356             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
357         ASSERT(mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] >= size);
358         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex].fetch_add(
359             -1, std::memory_order_relaxed);
360         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex].fetch_add(
361             -size, std::memory_order_relaxed);
362     }
363 }
364 
getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const365 VkDeviceSize MemoryAllocationTracker::getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const
366 {
367     if (!kTrackMemoryAllocationSizes)
368     {
369         return 0;
370     }
371 
372     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount);
373     return mActiveMemoryAllocationsSize[allocTypeIndex];
374 }
375 
getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex,uint32_t heapIndex) const376 VkDeviceSize MemoryAllocationTracker::getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex,
377                                                                          uint32_t heapIndex) const
378 {
379     if (!kTrackMemoryAllocationSizes)
380     {
381         return 0;
382     }
383 
384     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount &&
385            heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount());
386     return mActivePerHeapMemoryAllocationsSize[allocTypeIndex][heapIndex];
387 }
388 
getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const389 uint64_t MemoryAllocationTracker::getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const
390 {
391     if (!kTrackMemoryAllocationSizes)
392     {
393         return 0;
394     }
395 
396     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount);
397     return mActiveMemoryAllocationsCount[allocTypeIndex];
398 }
399 
getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex,uint32_t heapIndex) const400 uint64_t MemoryAllocationTracker::getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex,
401                                                                       uint32_t heapIndex) const
402 {
403     if (!kTrackMemoryAllocationSizes)
404     {
405         return 0;
406     }
407 
408     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount &&
409            heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount());
410     return mActivePerHeapMemoryAllocationsCount[allocTypeIndex][heapIndex];
411 }
412 
compareExpectedFlagsWithAllocatedFlags(VkMemoryPropertyFlags requiredFlags,VkMemoryPropertyFlags preferredFlags,VkMemoryPropertyFlags allocatedFlags,void * handle)413 void MemoryAllocationTracker::compareExpectedFlagsWithAllocatedFlags(
414     VkMemoryPropertyFlags requiredFlags,
415     VkMemoryPropertyFlags preferredFlags,
416     VkMemoryPropertyFlags allocatedFlags,
417     void *handle)
418 {
419     if (!kTrackMemoryAllocationDebug)
420     {
421         return;
422     }
423 
424     ASSERT((requiredFlags & ~allocatedFlags) == 0);
425     if (((preferredFlags | requiredFlags) & ~allocatedFlags) != 0)
426     {
427         INFO() << "Memory type index chosen for object " << handle
428                << " lacks some of the preferred property flags.";
429     }
430 
431     if ((~allocatedFlags & preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
432     {
433         WARN() << "Device-local memory allocation fallback to system memory.";
434     }
435 }
436 
getPendingMemoryAllocationSize() const437 VkDeviceSize MemoryAllocationTracker::getPendingMemoryAllocationSize() const
438 {
439     if (!kTrackMemoryAllocationSizes)
440     {
441         return 0;
442     }
443 
444     return mPendingMemoryAllocationSize;
445 }
446 
getPendingMemoryAllocationType() const447 vk::MemoryAllocationType MemoryAllocationTracker::getPendingMemoryAllocationType() const
448 {
449     if (!kTrackMemoryAllocationSizes)
450     {
451         return vk::MemoryAllocationType::Unspecified;
452     }
453 
454     return mPendingMemoryAllocationType;
455 }
456 
getPendingMemoryTypeIndex() const457 uint32_t MemoryAllocationTracker::getPendingMemoryTypeIndex() const
458 {
459     if (!kTrackMemoryAllocationSizes)
460     {
461         return 0;
462     }
463 
464     return mPendingMemoryTypeIndex;
465 }
466 
setPendingMemoryAlloc(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex)467 void MemoryAllocationTracker::setPendingMemoryAlloc(vk::MemoryAllocationType allocType,
468                                                     VkDeviceSize size,
469                                                     uint32_t memoryTypeIndex)
470 {
471     if (!kTrackMemoryAllocationSizes)
472     {
473         return;
474     }
475 
476     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
477     mPendingMemoryAllocationType = allocType;
478     mPendingMemoryAllocationSize = size;
479     mPendingMemoryTypeIndex      = memoryTypeIndex;
480 }
481 
resetPendingMemoryAlloc()482 void MemoryAllocationTracker::resetPendingMemoryAlloc()
483 {
484     if (!kTrackMemoryAllocationSizes)
485     {
486         return;
487     }
488 
489     mPendingMemoryAllocationType = vk::MemoryAllocationType::Unspecified;
490     mPendingMemoryAllocationSize = 0;
491     mPendingMemoryTypeIndex      = kInvalidMemoryTypeIndex;
492 }
493 
494 namespace vk
495 {
MemoryReport()496 MemoryReport::MemoryReport()
497     : mCurrentTotalAllocatedMemory(0),
498       mMaxTotalAllocatedMemory(0),
499       mCurrentTotalImportedMemory(0),
500       mMaxTotalImportedMemory(0)
501 {}
502 
processCallback(const VkDeviceMemoryReportCallbackDataEXT & callbackData,bool logCallback)503 void MemoryReport::processCallback(const VkDeviceMemoryReportCallbackDataEXT &callbackData,
504                                    bool logCallback)
505 {
506     std::unique_lock<std::mutex> lock(mMemoryReportMutex);
507     VkDeviceSize size = 0;
508     std::string reportType;
509     switch (callbackData.type)
510     {
511         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT:
512             reportType = "Allocate";
513             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
514             {
515                 break;
516             }
517             size = mSizesPerType[callbackData.objectType].allocatedMemory + callbackData.size;
518             mSizesPerType[callbackData.objectType].allocatedMemory = size;
519             if (mSizesPerType[callbackData.objectType].allocatedMemoryMax < size)
520             {
521                 mSizesPerType[callbackData.objectType].allocatedMemoryMax = size;
522             }
523             mCurrentTotalAllocatedMemory += callbackData.size;
524             if (mMaxTotalAllocatedMemory < mCurrentTotalAllocatedMemory)
525             {
526                 mMaxTotalAllocatedMemory = mCurrentTotalAllocatedMemory;
527             }
528             break;
529         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT:
530             reportType = "Free";
531             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
532             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
533             size = mSizesPerType[callbackData.objectType].allocatedMemory - callbackData.size;
534             mSizesPerType[callbackData.objectType].allocatedMemory = size;
535             mCurrentTotalAllocatedMemory -= callbackData.size;
536             break;
537         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT:
538             reportType = "Import";
539             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
540             {
541                 break;
542             }
543             size = mSizesPerType[callbackData.objectType].importedMemory + callbackData.size;
544             mSizesPerType[callbackData.objectType].importedMemory = size;
545             if (mSizesPerType[callbackData.objectType].importedMemoryMax < size)
546             {
547                 mSizesPerType[callbackData.objectType].importedMemoryMax = size;
548             }
549             mCurrentTotalImportedMemory += callbackData.size;
550             if (mMaxTotalImportedMemory < mCurrentTotalImportedMemory)
551             {
552                 mMaxTotalImportedMemory = mCurrentTotalImportedMemory;
553             }
554             break;
555         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT:
556             reportType = "Un-Import";
557             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
558             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
559             size = mSizesPerType[callbackData.objectType].importedMemory - callbackData.size;
560             mSizesPerType[callbackData.objectType].importedMemory = size;
561             mCurrentTotalImportedMemory -= callbackData.size;
562             break;
563         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT:
564             reportType = "allocFail";
565             break;
566         default:
567             UNREACHABLE();
568             return;
569     }
570     if (logCallback)
571     {
572         INFO() << std::right << std::setw(9) << reportType << ": size=" << std::setw(10)
573                << callbackData.size << "; type=" << std::setw(15) << std::left
574                << RendererVk::GetVulkanObjectTypeName(callbackData.objectType)
575                << "; heapIdx=" << callbackData.heapIndex << "; id=" << std::hex
576                << callbackData.memoryObjectId << "; handle=" << std::hex
577                << callbackData.objectHandle << ": Total=" << std::right << std::setw(10) << std::dec
578                << size;
579     }
580 }
581 
logMemoryReportStats() const582 void MemoryReport::logMemoryReportStats() const
583 {
584     std::unique_lock<std::mutex> lock(mMemoryReportMutex);
585 
586     INFO() << std::right << "GPU Memory Totals:       Allocated=" << std::setw(10)
587            << mCurrentTotalAllocatedMemory << " (max=" << std::setw(10) << mMaxTotalAllocatedMemory
588            << ");  Imported=" << std::setw(10) << mCurrentTotalImportedMemory
589            << " (max=" << std::setw(10) << mMaxTotalImportedMemory << ")";
590     INFO() << "Sub-Totals per type:";
591     for (const auto &it : mSizesPerType)
592     {
593         VkObjectType objectType         = it.first;
594         MemorySizes memorySizes         = it.second;
595         VkDeviceSize allocatedMemory    = memorySizes.allocatedMemory;
596         VkDeviceSize allocatedMemoryMax = memorySizes.allocatedMemoryMax;
597         VkDeviceSize importedMemory     = memorySizes.importedMemory;
598         VkDeviceSize importedMemoryMax  = memorySizes.importedMemoryMax;
599         INFO() << std::right << "- Type=" << std::setw(15)
600                << RendererVk::GetVulkanObjectTypeName(objectType)
601                << ":  Allocated=" << std::setw(10) << allocatedMemory << " (max=" << std::setw(10)
602                << allocatedMemoryMax << ");  Imported=" << std::setw(10) << importedMemory
603                << " (max=" << std::setw(10) << importedMemoryMax << ")";
604     }
605 }
606 }  // namespace vk
607 }  // namespace rx
608