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