• 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/vk_renderer.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(vk::Renderer * renderer,vk::MemoryLogSeverity severity)60 void CheckForCurrentMemoryAllocations(vk::Renderer *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(vk::Renderer * renderer,vk::MemoryLogSeverity severity)100 void LogPendingMemoryAllocation(vk::Renderer *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 memoryTypeIndex = renderer->getMemoryAllocationTracker()->getPendingMemoryTypeIndex();
112     uint32_t memoryHeapIndex =
113         renderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
114 
115     if (allocSize != 0)
116     {
117         std::stringstream outStream;
118 
119         outStream << "Pending allocation size for memory allocation type ("
120                   << vk::kMemoryAllocationTypeMessage[ToUnderlying(allocInfo)]
121                   << ") for heap index " << memoryHeapIndex << " (type index " << memoryTypeIndex
122                   << "): " << allocSize;
123 
124         // Output the log stream based on the level of severity.
125         OutputMemoryLogStream(outStream, severity);
126     }
127 }
128 
LogMemoryHeapStats(vk::Renderer * renderer,vk::MemoryLogSeverity severity)129 void LogMemoryHeapStats(vk::Renderer *renderer, vk::MemoryLogSeverity severity)
130 {
131     if (!kTrackMemoryAllocationSizes)
132     {
133         return;
134     }
135 
136     // Log stream for the heap information.
137     std::stringstream outStream;
138 
139     // VkPhysicalDeviceMemoryProperties2 enables the use of memory budget properties if
140     // supported.
141     VkPhysicalDeviceMemoryProperties2KHR memoryProperties;
142     memoryProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR;
143     memoryProperties.pNext = nullptr;
144 
145     VkPhysicalDeviceMemoryBudgetPropertiesEXT memoryBudgetProperties;
146     memoryBudgetProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
147     memoryBudgetProperties.pNext = nullptr;
148 
149     if (renderer->getFeatures().supportsMemoryBudget.enabled)
150     {
151         vk::AddToPNextChain(&memoryProperties, &memoryBudgetProperties);
152     }
153 
154     vkGetPhysicalDeviceMemoryProperties2(renderer->getPhysicalDevice(), &memoryProperties);
155 
156     // Add memory heap information to the stream.
157     outStream << "Memory heap info" << std::endl;
158 
159     outStream << std::endl << "* Available memory heaps:" << std::endl;
160     for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++)
161     {
162         outStream << std::dec << i
163                   << " | Heap size: " << memoryProperties.memoryProperties.memoryHeaps[i].size
164                   << " | Flags: 0x" << std::hex
165                   << memoryProperties.memoryProperties.memoryHeaps[i].flags << std::endl;
166     }
167 
168     if (renderer->getFeatures().supportsMemoryBudget.enabled)
169     {
170         outStream << std::endl << "* Available memory budget and usage per heap:" << std::endl;
171         for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++)
172         {
173             outStream << std::dec << i << " | Heap budget: " << memoryBudgetProperties.heapBudget[i]
174                       << " | Heap usage: " << memoryBudgetProperties.heapUsage[i] << std::endl;
175         }
176     }
177 
178     outStream << std::endl << "* Available memory types:" << std::endl;
179     for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryTypeCount; i++)
180     {
181         outStream << std::dec << i
182                   << " | Heap index: " << memoryProperties.memoryProperties.memoryTypes[i].heapIndex
183                   << " | Property flags: 0x" << std::hex
184                   << memoryProperties.memoryProperties.memoryTypes[i].propertyFlags << std::endl;
185     }
186 
187     // Output the log stream based on the level of severity.
188     OutputMemoryLogStream(outStream, severity);
189 }
190 }  // namespace
191 
MemoryAllocationTracker(vk::Renderer * renderer)192 MemoryAllocationTracker::MemoryAllocationTracker(vk::Renderer *renderer)
193     : mRenderer(renderer), mMemoryAllocationID(0)
194 {}
195 
initMemoryTrackers()196 void MemoryAllocationTracker::initMemoryTrackers()
197 {
198     // Allocation counters are initialized here to keep track of the size and count of the memory
199     // allocations.
200     for (size_t allocTypeIndex = 0; allocTypeIndex < mActiveMemoryAllocationsSize.size();
201          allocTypeIndex++)
202     {
203         mActiveMemoryAllocationsSize[allocTypeIndex]  = 0;
204         mActiveMemoryAllocationsCount[allocTypeIndex] = 0;
205 
206         // Per-heap allocation counters are initialized here.
207         for (size_t heapIndex = 0;
208              heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount(); heapIndex++)
209         {
210             mActivePerHeapMemoryAllocationsSize[allocTypeIndex][heapIndex]  = 0;
211             mActivePerHeapMemoryAllocationsCount[allocTypeIndex][heapIndex] = 0;
212         }
213     }
214 
215     resetPendingMemoryAlloc();
216 }
217 
onDestroy()218 void MemoryAllocationTracker::onDestroy()
219 {
220     if (kTrackMemoryAllocationDebug)
221     {
222         CheckForCurrentMemoryAllocations(mRenderer, vk::MemoryLogSeverity::INFO);
223     }
224 }
225 
onDeviceInit()226 void MemoryAllocationTracker::onDeviceInit()
227 {
228     if (kTrackMemoryAllocationDebug)
229     {
230         LogMemoryHeapStats(mRenderer, vk::MemoryLogSeverity::INFO);
231     }
232 }
233 
logMemoryStatsOnError()234 void MemoryAllocationTracker::logMemoryStatsOnError()
235 {
236     CheckForCurrentMemoryAllocations(mRenderer, vk::MemoryLogSeverity::WARN);
237     LogPendingMemoryAllocation(mRenderer, vk::MemoryLogSeverity::WARN);
238     LogMemoryHeapStats(mRenderer, vk::MemoryLogSeverity::WARN);
239 }
240 
onMemoryAllocImpl(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex,void * handle)241 void MemoryAllocationTracker::onMemoryAllocImpl(vk::MemoryAllocationType allocType,
242                                                 VkDeviceSize size,
243                                                 uint32_t memoryTypeIndex,
244                                                 void *handle)
245 {
246     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
247 
248     if (kTrackMemoryAllocationDebug)
249     {
250         // If enabled (debug layers), we keep more details in the memory tracker, such as handle,
251         // and log the action to the output.
252         std::unique_lock<angle::SimpleMutex> lock(mMemoryAllocationMutex);
253 
254         uint32_t allocTypeIndex = ToUnderlying(allocType);
255         uint32_t memoryHeapIndex =
256             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
257         mActiveMemoryAllocationsCount[allocTypeIndex]++;
258         mActiveMemoryAllocationsSize[allocTypeIndex] += size;
259         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex]++;
260         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] += size;
261 
262         // Add the new allocation to the memory tracker.
263         vk::MemoryAllocationInfo memAllocLogInfo;
264         memAllocLogInfo.id              = ++mMemoryAllocationID;
265         memAllocLogInfo.allocType       = allocType;
266         memAllocLogInfo.memoryHeapIndex = memoryHeapIndex;
267         memAllocLogInfo.size            = size;
268         memAllocLogInfo.handle          = handle;
269 
270         vk::MemoryAllocInfoMapKey memoryAllocInfoMapKey(memAllocLogInfo.handle);
271         mMemoryAllocationRecord[angle::getBacktraceInfo()].insert(
272             std::make_pair(memoryAllocInfoMapKey, memAllocLogInfo));
273 
274         INFO() << "Memory allocation: (id " << memAllocLogInfo.id << ") for object "
275                << memAllocLogInfo.handle << " | Size: " << memAllocLogInfo.size
276                << " | Type: " << vk::kMemoryAllocationTypeMessage[allocTypeIndex]
277                << " | Memory type index: " << memoryTypeIndex
278                << " | Heap index: " << memAllocLogInfo.memoryHeapIndex;
279 
280         resetPendingMemoryAlloc();
281     }
282     else if (kTrackMemoryAllocationSizes)
283     {
284         // Add the new allocation size to the allocation counter.
285         uint32_t allocTypeIndex = ToUnderlying(allocType);
286         mActiveMemoryAllocationsCount[allocTypeIndex]++;
287         mActiveMemoryAllocationsSize[allocTypeIndex] += size;
288 
289         uint32_t memoryHeapIndex =
290             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
291         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex].fetch_add(
292             1, std::memory_order_relaxed);
293         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex].fetch_add(
294             size, std::memory_order_relaxed);
295 
296         resetPendingMemoryAlloc();
297     }
298 }
299 
onMemoryDeallocImpl(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex,void * handle)300 void MemoryAllocationTracker::onMemoryDeallocImpl(vk::MemoryAllocationType allocType,
301                                                   VkDeviceSize size,
302                                                   uint32_t memoryTypeIndex,
303                                                   void *handle)
304 {
305     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
306 
307     if (kTrackMemoryAllocationDebug)
308     {
309         // If enabled (debug layers), we keep more details in the memory tracker, such as handle,
310         // and log the action to the output. The memory allocation tracker uses the backtrace info
311         // as key, if available.
312         for (auto &memInfoPerBacktrace : mMemoryAllocationRecord)
313         {
314             vk::MemoryAllocInfoMapKey memoryAllocInfoMapKey(handle);
315             MemoryAllocInfoMap &memInfoMap = memInfoPerBacktrace.second;
316             std::unique_lock<angle::SimpleMutex> lock(mMemoryAllocationMutex);
317 
318             if (memInfoMap.find(memoryAllocInfoMapKey) != memInfoMap.end())
319             {
320                 // Object found; remove it from the allocation tracker.
321                 vk::MemoryAllocationInfo *memInfoEntry = &memInfoMap[memoryAllocInfoMapKey];
322                 ASSERT(memInfoEntry->allocType == allocType && memInfoEntry->size == size);
323 
324                 uint32_t allocTypeIndex = ToUnderlying(memInfoEntry->allocType);
325                 uint32_t memoryHeapIndex =
326                     mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
327                 ASSERT(mActiveMemoryAllocationsCount[allocTypeIndex] != 0 &&
328                        mActiveMemoryAllocationsSize[allocTypeIndex] >= size);
329                 ASSERT(memoryHeapIndex == memInfoEntry->memoryHeapIndex &&
330                        mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex] != 0 &&
331                        mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] >=
332                            size);
333                 mActiveMemoryAllocationsCount[allocTypeIndex]--;
334                 mActiveMemoryAllocationsSize[allocTypeIndex] -= size;
335                 mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex]--;
336                 mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] -= size;
337 
338                 INFO() << "Memory deallocation: (id " << memInfoEntry->id << ") for object "
339                        << memInfoEntry->handle << " | Size: " << memInfoEntry->size
340                        << " | Type: " << vk::kMemoryAllocationTypeMessage[allocTypeIndex]
341                        << " | Memory type index: " << memoryTypeIndex
342                        << " | Heap index: " << memInfoEntry->memoryHeapIndex;
343 
344                 memInfoMap.erase(memoryAllocInfoMapKey);
345             }
346         }
347     }
348     else if (kTrackMemoryAllocationSizes)
349     {
350         // Remove the allocation size from the allocation counter.
351         uint32_t allocTypeIndex = ToUnderlying(allocType);
352         ASSERT(mActiveMemoryAllocationsCount[allocTypeIndex] != 0 &&
353                mActiveMemoryAllocationsSize[allocTypeIndex] >= size);
354         mActiveMemoryAllocationsCount[allocTypeIndex]--;
355         mActiveMemoryAllocationsSize[allocTypeIndex] -= size;
356 
357         uint32_t memoryHeapIndex =
358             mRenderer->getMemoryProperties().getHeapIndexForMemoryType(memoryTypeIndex);
359         ASSERT(mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex] >= size);
360         mActivePerHeapMemoryAllocationsCount[allocTypeIndex][memoryHeapIndex].fetch_add(
361             -1, std::memory_order_relaxed);
362         mActivePerHeapMemoryAllocationsSize[allocTypeIndex][memoryHeapIndex].fetch_add(
363             -size, std::memory_order_relaxed);
364     }
365 }
366 
getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const367 VkDeviceSize MemoryAllocationTracker::getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const
368 {
369     if (!kTrackMemoryAllocationSizes)
370     {
371         return 0;
372     }
373 
374     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount);
375     return mActiveMemoryAllocationsSize[allocTypeIndex];
376 }
377 
getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex,uint32_t heapIndex) const378 VkDeviceSize MemoryAllocationTracker::getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex,
379                                                                          uint32_t heapIndex) const
380 {
381     if (!kTrackMemoryAllocationSizes)
382     {
383         return 0;
384     }
385 
386     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount &&
387            heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount());
388     return mActivePerHeapMemoryAllocationsSize[allocTypeIndex][heapIndex];
389 }
390 
getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const391 uint64_t MemoryAllocationTracker::getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const
392 {
393     if (!kTrackMemoryAllocationSizes)
394     {
395         return 0;
396     }
397 
398     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount);
399     return mActiveMemoryAllocationsCount[allocTypeIndex];
400 }
401 
getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex,uint32_t heapIndex) const402 uint64_t MemoryAllocationTracker::getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex,
403                                                                       uint32_t heapIndex) const
404 {
405     if (!kTrackMemoryAllocationSizes)
406     {
407         return 0;
408     }
409 
410     ASSERT(allocTypeIndex < vk::kMemoryAllocationTypeCount &&
411            heapIndex < mRenderer->getMemoryProperties().getMemoryHeapCount());
412     return mActivePerHeapMemoryAllocationsCount[allocTypeIndex][heapIndex];
413 }
414 
compareExpectedFlagsWithAllocatedFlags(VkMemoryPropertyFlags requiredFlags,VkMemoryPropertyFlags preferredFlags,VkMemoryPropertyFlags allocatedFlags,void * handle)415 void MemoryAllocationTracker::compareExpectedFlagsWithAllocatedFlags(
416     VkMemoryPropertyFlags requiredFlags,
417     VkMemoryPropertyFlags preferredFlags,
418     VkMemoryPropertyFlags allocatedFlags,
419     void *handle)
420 {
421     if (!kTrackMemoryAllocationDebug)
422     {
423         return;
424     }
425 
426     ASSERT((requiredFlags & ~allocatedFlags) == 0);
427     if (((preferredFlags | requiredFlags) & ~allocatedFlags) != 0)
428     {
429         INFO() << "Memory type index chosen for object " << handle
430                << " lacks some of the preferred property flags.";
431     }
432 
433     if ((~allocatedFlags & preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
434     {
435         WARN() << "Device-local memory allocation fallback to system memory.";
436     }
437 }
438 
onExceedingMaxMemoryAllocationSize(VkDeviceSize size)439 void MemoryAllocationTracker::onExceedingMaxMemoryAllocationSize(VkDeviceSize size)
440 {
441     VkDeviceSize maxAllocationSize = mRenderer->getMaxMemoryAllocationSize();
442     ASSERT(size > maxAllocationSize);
443 
444     WARN() << "Attempted allocation size (" << size
445            << ") is greater than the maximum allocation size allowed (" << maxAllocationSize
446            << ").";
447 }
448 
getPendingMemoryAllocationSize() const449 VkDeviceSize MemoryAllocationTracker::getPendingMemoryAllocationSize() const
450 {
451     if (!kTrackMemoryAllocationSizes)
452     {
453         return 0;
454     }
455 
456     return mPendingMemoryAllocationSize;
457 }
458 
getPendingMemoryAllocationType() const459 vk::MemoryAllocationType MemoryAllocationTracker::getPendingMemoryAllocationType() const
460 {
461     if (!kTrackMemoryAllocationSizes)
462     {
463         return vk::MemoryAllocationType::Unspecified;
464     }
465 
466     return mPendingMemoryAllocationType;
467 }
468 
getPendingMemoryTypeIndex() const469 uint32_t MemoryAllocationTracker::getPendingMemoryTypeIndex() const
470 {
471     if (!kTrackMemoryAllocationSizes)
472     {
473         return 0;
474     }
475 
476     return mPendingMemoryTypeIndex;
477 }
478 
setPendingMemoryAlloc(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex)479 void MemoryAllocationTracker::setPendingMemoryAlloc(vk::MemoryAllocationType allocType,
480                                                     VkDeviceSize size,
481                                                     uint32_t memoryTypeIndex)
482 {
483     if (!kTrackMemoryAllocationSizes)
484     {
485         return;
486     }
487 
488     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
489     mPendingMemoryAllocationType = allocType;
490     mPendingMemoryAllocationSize = size;
491     mPendingMemoryTypeIndex      = memoryTypeIndex;
492 }
493 
resetPendingMemoryAlloc()494 void MemoryAllocationTracker::resetPendingMemoryAlloc()
495 {
496     if (!kTrackMemoryAllocationSizes)
497     {
498         return;
499     }
500 
501     mPendingMemoryAllocationType = vk::MemoryAllocationType::Unspecified;
502     mPendingMemoryAllocationSize = 0;
503     mPendingMemoryTypeIndex      = kInvalidMemoryTypeIndex;
504 }
505 
506 namespace vk
507 {
MemoryReport()508 MemoryReport::MemoryReport()
509     : mCurrentTotalAllocatedMemory(0),
510       mMaxTotalAllocatedMemory(0),
511       mCurrentTotalImportedMemory(0),
512       mMaxTotalImportedMemory(0)
513 {}
514 
processCallback(const VkDeviceMemoryReportCallbackDataEXT & callbackData,bool logCallback)515 void MemoryReport::processCallback(const VkDeviceMemoryReportCallbackDataEXT &callbackData,
516                                    bool logCallback)
517 {
518     std::unique_lock<angle::SimpleMutex> lock(mMemoryReportMutex);
519     VkDeviceSize size = 0;
520     std::string reportType;
521     switch (callbackData.type)
522     {
523         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT:
524             reportType = "Allocate";
525             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
526             {
527                 break;
528             }
529             size = mSizesPerType[callbackData.objectType].allocatedMemory + callbackData.size;
530             mSizesPerType[callbackData.objectType].allocatedMemory = size;
531             if (mSizesPerType[callbackData.objectType].allocatedMemoryMax < size)
532             {
533                 mSizesPerType[callbackData.objectType].allocatedMemoryMax = size;
534             }
535             mCurrentTotalAllocatedMemory += callbackData.size;
536             if (mMaxTotalAllocatedMemory < mCurrentTotalAllocatedMemory)
537             {
538                 mMaxTotalAllocatedMemory = mCurrentTotalAllocatedMemory;
539             }
540             break;
541         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT:
542             reportType = "Free";
543             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
544             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
545             size = mSizesPerType[callbackData.objectType].allocatedMemory - callbackData.size;
546             mSizesPerType[callbackData.objectType].allocatedMemory = size;
547             mCurrentTotalAllocatedMemory -= callbackData.size;
548             break;
549         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT:
550             reportType = "Import";
551             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
552             {
553                 break;
554             }
555             size = mSizesPerType[callbackData.objectType].importedMemory + callbackData.size;
556             mSizesPerType[callbackData.objectType].importedMemory = size;
557             if (mSizesPerType[callbackData.objectType].importedMemoryMax < size)
558             {
559                 mSizesPerType[callbackData.objectType].importedMemoryMax = size;
560             }
561             mCurrentTotalImportedMemory += callbackData.size;
562             if (mMaxTotalImportedMemory < mCurrentTotalImportedMemory)
563             {
564                 mMaxTotalImportedMemory = mCurrentTotalImportedMemory;
565             }
566             break;
567         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT:
568             reportType = "Un-Import";
569             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
570             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
571             size = mSizesPerType[callbackData.objectType].importedMemory - callbackData.size;
572             mSizesPerType[callbackData.objectType].importedMemory = size;
573             mCurrentTotalImportedMemory -= callbackData.size;
574             break;
575         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT:
576             reportType = "allocFail";
577             break;
578         default:
579             UNREACHABLE();
580             return;
581     }
582     if (logCallback)
583     {
584         INFO() << std::right << std::setw(9) << reportType << ": size=" << std::setw(10)
585                << callbackData.size << "; type=" << std::setw(15) << std::left
586                << Renderer::GetVulkanObjectTypeName(callbackData.objectType)
587                << "; heapIdx=" << callbackData.heapIndex << "; id=" << std::hex
588                << callbackData.memoryObjectId << "; handle=" << std::hex
589                << callbackData.objectHandle << ": Total=" << std::right << std::setw(10) << std::dec
590                << size;
591     }
592 }
593 
logMemoryReportStats() const594 void MemoryReport::logMemoryReportStats() const
595 {
596     std::unique_lock<angle::SimpleMutex> lock(mMemoryReportMutex);
597 
598     INFO() << std::right << "GPU Memory Totals:       Allocated=" << std::setw(10)
599            << mCurrentTotalAllocatedMemory << " (max=" << std::setw(10) << mMaxTotalAllocatedMemory
600            << ");  Imported=" << std::setw(10) << mCurrentTotalImportedMemory
601            << " (max=" << std::setw(10) << mMaxTotalImportedMemory << ")";
602     INFO() << "Sub-Totals per type:";
603     for (const auto &it : mSizesPerType)
604     {
605         VkObjectType objectType         = it.first;
606         MemorySizes memorySizes         = it.second;
607         VkDeviceSize allocatedMemory    = memorySizes.allocatedMemory;
608         VkDeviceSize allocatedMemoryMax = memorySizes.allocatedMemoryMax;
609         VkDeviceSize importedMemory     = memorySizes.importedMemory;
610         VkDeviceSize importedMemoryMax  = memorySizes.importedMemoryMax;
611         INFO() << std::right << "- Type=" << std::setw(15)
612                << Renderer::GetVulkanObjectTypeName(objectType) << ":  Allocated=" << std::setw(10)
613                << allocatedMemory << " (max=" << std::setw(10) << allocatedMemoryMax
614                << ");  Imported=" << std::setw(10) << importedMemory << " (max=" << std::setw(10)
615                << importedMemoryMax << ")";
616     }
617 }
618 }  // namespace vk
619 }  // namespace rx
620