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