xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/vulkan/MemoryTracking.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 
getPendingMemoryAllocationSize() const439 VkDeviceSize MemoryAllocationTracker::getPendingMemoryAllocationSize() const
440 {
441     if (!kTrackMemoryAllocationSizes)
442     {
443         return 0;
444     }
445 
446     return mPendingMemoryAllocationSize;
447 }
448 
getPendingMemoryAllocationType() const449 vk::MemoryAllocationType MemoryAllocationTracker::getPendingMemoryAllocationType() const
450 {
451     if (!kTrackMemoryAllocationSizes)
452     {
453         return vk::MemoryAllocationType::Unspecified;
454     }
455 
456     return mPendingMemoryAllocationType;
457 }
458 
getPendingMemoryTypeIndex() const459 uint32_t MemoryAllocationTracker::getPendingMemoryTypeIndex() const
460 {
461     if (!kTrackMemoryAllocationSizes)
462     {
463         return 0;
464     }
465 
466     return mPendingMemoryTypeIndex;
467 }
468 
setPendingMemoryAlloc(vk::MemoryAllocationType allocType,VkDeviceSize size,uint32_t memoryTypeIndex)469 void MemoryAllocationTracker::setPendingMemoryAlloc(vk::MemoryAllocationType allocType,
470                                                     VkDeviceSize size,
471                                                     uint32_t memoryTypeIndex)
472 {
473     if (!kTrackMemoryAllocationSizes)
474     {
475         return;
476     }
477 
478     ASSERT(allocType != vk::MemoryAllocationType::InvalidEnum && size != 0);
479     mPendingMemoryAllocationType = allocType;
480     mPendingMemoryAllocationSize = size;
481     mPendingMemoryTypeIndex      = memoryTypeIndex;
482 }
483 
resetPendingMemoryAlloc()484 void MemoryAllocationTracker::resetPendingMemoryAlloc()
485 {
486     if (!kTrackMemoryAllocationSizes)
487     {
488         return;
489     }
490 
491     mPendingMemoryAllocationType = vk::MemoryAllocationType::Unspecified;
492     mPendingMemoryAllocationSize = 0;
493     mPendingMemoryTypeIndex      = kInvalidMemoryTypeIndex;
494 }
495 
496 namespace vk
497 {
MemoryReport()498 MemoryReport::MemoryReport()
499     : mCurrentTotalAllocatedMemory(0),
500       mMaxTotalAllocatedMemory(0),
501       mCurrentTotalImportedMemory(0),
502       mMaxTotalImportedMemory(0)
503 {}
504 
processCallback(const VkDeviceMemoryReportCallbackDataEXT & callbackData,bool logCallback)505 void MemoryReport::processCallback(const VkDeviceMemoryReportCallbackDataEXT &callbackData,
506                                    bool logCallback)
507 {
508     std::unique_lock<angle::SimpleMutex> lock(mMemoryReportMutex);
509     VkDeviceSize size = 0;
510     std::string reportType;
511     switch (callbackData.type)
512     {
513         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT:
514             reportType = "Allocate";
515             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
516             {
517                 break;
518             }
519             size = mSizesPerType[callbackData.objectType].allocatedMemory + callbackData.size;
520             mSizesPerType[callbackData.objectType].allocatedMemory = size;
521             if (mSizesPerType[callbackData.objectType].allocatedMemoryMax < size)
522             {
523                 mSizesPerType[callbackData.objectType].allocatedMemoryMax = size;
524             }
525             mCurrentTotalAllocatedMemory += callbackData.size;
526             if (mMaxTotalAllocatedMemory < mCurrentTotalAllocatedMemory)
527             {
528                 mMaxTotalAllocatedMemory = mCurrentTotalAllocatedMemory;
529             }
530             break;
531         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT:
532             reportType = "Free";
533             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
534             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
535             size = mSizesPerType[callbackData.objectType].allocatedMemory - callbackData.size;
536             mSizesPerType[callbackData.objectType].allocatedMemory = size;
537             mCurrentTotalAllocatedMemory -= callbackData.size;
538             break;
539         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT:
540             reportType = "Import";
541             if ((mUniqueIDCounts[callbackData.memoryObjectId] += 1) > 1)
542             {
543                 break;
544             }
545             size = mSizesPerType[callbackData.objectType].importedMemory + callbackData.size;
546             mSizesPerType[callbackData.objectType].importedMemory = size;
547             if (mSizesPerType[callbackData.objectType].importedMemoryMax < size)
548             {
549                 mSizesPerType[callbackData.objectType].importedMemoryMax = size;
550             }
551             mCurrentTotalImportedMemory += callbackData.size;
552             if (mMaxTotalImportedMemory < mCurrentTotalImportedMemory)
553             {
554                 mMaxTotalImportedMemory = mCurrentTotalImportedMemory;
555             }
556             break;
557         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT:
558             reportType = "Un-Import";
559             ASSERT(mUniqueIDCounts[callbackData.memoryObjectId] > 0);
560             mUniqueIDCounts[callbackData.memoryObjectId] -= 1;
561             size = mSizesPerType[callbackData.objectType].importedMemory - callbackData.size;
562             mSizesPerType[callbackData.objectType].importedMemory = size;
563             mCurrentTotalImportedMemory -= callbackData.size;
564             break;
565         case VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT:
566             reportType = "allocFail";
567             break;
568         default:
569             UNREACHABLE();
570             return;
571     }
572     if (logCallback)
573     {
574         INFO() << std::right << std::setw(9) << reportType << ": size=" << std::setw(10)
575                << callbackData.size << "; type=" << std::setw(15) << std::left
576                << Renderer::GetVulkanObjectTypeName(callbackData.objectType)
577                << "; heapIdx=" << callbackData.heapIndex << "; id=" << std::hex
578                << callbackData.memoryObjectId << "; handle=" << std::hex
579                << callbackData.objectHandle << ": Total=" << std::right << std::setw(10) << std::dec
580                << size;
581     }
582 }
583 
logMemoryReportStats() const584 void MemoryReport::logMemoryReportStats() const
585 {
586     std::unique_lock<angle::SimpleMutex> lock(mMemoryReportMutex);
587 
588     INFO() << std::right << "GPU Memory Totals:       Allocated=" << std::setw(10)
589            << mCurrentTotalAllocatedMemory << " (max=" << std::setw(10) << mMaxTotalAllocatedMemory
590            << ");  Imported=" << std::setw(10) << mCurrentTotalImportedMemory
591            << " (max=" << std::setw(10) << mMaxTotalImportedMemory << ")";
592     INFO() << "Sub-Totals per type:";
593     for (const auto &it : mSizesPerType)
594     {
595         VkObjectType objectType         = it.first;
596         MemorySizes memorySizes         = it.second;
597         VkDeviceSize allocatedMemory    = memorySizes.allocatedMemory;
598         VkDeviceSize allocatedMemoryMax = memorySizes.allocatedMemoryMax;
599         VkDeviceSize importedMemory     = memorySizes.importedMemory;
600         VkDeviceSize importedMemoryMax  = memorySizes.importedMemoryMax;
601         INFO() << std::right << "- Type=" << std::setw(15)
602                << Renderer::GetVulkanObjectTypeName(objectType) << ":  Allocated=" << std::setw(10)
603                << allocatedMemory << " (max=" << std::setw(10) << allocatedMemoryMax
604                << ");  Imported=" << std::setw(10) << importedMemory << " (max=" << std::setw(10)
605                << importedMemoryMax << ")";
606     }
607 }
608 }  // namespace vk
609 }  // namespace rx
610