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