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.h: 7 // Defines the classes used for memory tracking in ANGLE. 8 // 9 10 #ifndef LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_ 11 #define LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_ 12 13 #include <array> 14 #include <atomic> 15 #include <mutex> 16 17 #include "common/angleutils.h" 18 #include "common/backtrace_utils.h" 19 #include "common/vulkan/vk_headers.h" 20 21 namespace rx 22 { 23 class RendererVk; 24 25 namespace vk 26 { 27 // Used to designate memory allocation type for tracking purposes. 28 enum class MemoryAllocationType 29 { 30 Unspecified = 0, 31 ImageExternal = 1, 32 OffscreenSurfaceAttachmentImage = 2, 33 SwapchainMSAAImage = 3, 34 SwapchainDepthStencilImage = 4, 35 StagingImage = 5, 36 ImplicitMultisampledRenderToTextureImage = 6, 37 TextureImage = 7, 38 FontImage = 8, 39 RenderBufferStorageImage = 9, 40 Buffer = 10, 41 BufferExternal = 11, 42 43 InvalidEnum = 12, 44 EnumCount = InvalidEnum, 45 }; 46 47 constexpr const char *kMemoryAllocationTypeMessage[] = { 48 "Unspecified", 49 "ImageExternal", 50 "OffscreenSurfaceAttachmentImage", 51 "SwapchainMSAAImage", 52 "SwapchainDepthStencilImage", 53 "StagingImage", 54 "ImplicitMultisampledRenderToTextureImage", 55 "TextureImage", 56 "FontImage", 57 "RenderBufferStorageImage", 58 "Buffer", 59 "BufferExternal", 60 "Invalid", 61 }; 62 constexpr const uint32_t kMemoryAllocationTypeCount = 63 static_cast<uint32_t>(MemoryAllocationType::EnumCount); 64 65 // Used to select the severity for memory allocation logs. 66 enum class MemoryLogSeverity 67 { 68 INFO, 69 WARN, 70 }; 71 72 // Used to store memory allocation information for tracking purposes. 73 struct MemoryAllocationInfo 74 { 75 MemoryAllocationInfo() = default; 76 uint64_t id; 77 MemoryAllocationType allocType; 78 uint32_t memoryHeapIndex; 79 void *handle; 80 VkDeviceSize size; 81 }; 82 83 class MemoryAllocInfoMapKey 84 { 85 public: MemoryAllocInfoMapKey()86 MemoryAllocInfoMapKey() : handle(nullptr) {} MemoryAllocInfoMapKey(void * handle)87 MemoryAllocInfoMapKey(void *handle) : handle(handle) {} 88 89 bool operator==(const MemoryAllocInfoMapKey &rhs) const 90 { 91 return reinterpret_cast<uint64_t>(handle) == reinterpret_cast<uint64_t>(rhs.handle); 92 } 93 94 size_t hash() const; 95 96 private: 97 void *handle; 98 }; 99 100 // Process GPU memory reports 101 class MemoryReport final : angle::NonCopyable 102 { 103 public: 104 MemoryReport(); 105 void processCallback(const VkDeviceMemoryReportCallbackDataEXT &callbackData, bool logCallback); 106 void logMemoryReportStats() const; 107 108 private: 109 struct MemorySizes 110 { 111 VkDeviceSize allocatedMemory; 112 VkDeviceSize allocatedMemoryMax; 113 VkDeviceSize importedMemory; 114 VkDeviceSize importedMemoryMax; 115 }; 116 mutable std::mutex mMemoryReportMutex; 117 VkDeviceSize mCurrentTotalAllocatedMemory; 118 VkDeviceSize mMaxTotalAllocatedMemory; 119 angle::HashMap<VkObjectType, MemorySizes> mSizesPerType; 120 VkDeviceSize mCurrentTotalImportedMemory; 121 VkDeviceSize mMaxTotalImportedMemory; 122 angle::HashMap<uint64_t, int> mUniqueIDCounts; 123 }; 124 } // namespace vk 125 126 // Memory tracker for allocations and deallocations, which is used in RendererVk. 127 class MemoryAllocationTracker : angle::NonCopyable 128 { 129 public: 130 MemoryAllocationTracker(RendererVk *renderer); 131 void initMemoryTrackers(); 132 void onDeviceInit(); 133 void onDestroy(); 134 135 // Memory statistics are logged when handling a context error. 136 void logMemoryStatsOnError(); 137 138 // Collect information regarding memory allocations and deallocations. 139 void onMemoryAllocImpl(vk::MemoryAllocationType allocType, 140 VkDeviceSize size, 141 uint32_t memoryTypeIndex, 142 void *handle); 143 void onMemoryDeallocImpl(vk::MemoryAllocationType allocType, 144 VkDeviceSize size, 145 uint32_t memoryTypeIndex, 146 void *handle); 147 148 // Memory allocation statistics functions. 149 VkDeviceSize getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const; 150 VkDeviceSize getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex, 151 uint32_t heapIndex) const; 152 153 uint64_t getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const; 154 uint64_t getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex, uint32_t heapIndex) const; 155 156 // Compare the expected flags with the flags of the allocated memory. 157 void compareExpectedFlagsWithAllocatedFlags(VkMemoryPropertyFlags requiredFlags, 158 VkMemoryPropertyFlags preferredFlags, 159 VkMemoryPropertyFlags allocatedFlags, 160 void *handle); 161 162 // Pending memory allocation information is used for logging in case of an unsuccessful 163 // allocation. It is cleared in onMemoryAlloc(). 164 VkDeviceSize getPendingMemoryAllocationSize() const; 165 vk::MemoryAllocationType getPendingMemoryAllocationType() const; 166 uint32_t getPendingMemoryTypeIndex() const; 167 168 void resetPendingMemoryAlloc(); 169 void setPendingMemoryAlloc(vk::MemoryAllocationType allocType, 170 VkDeviceSize size, 171 uint32_t memoryTypeIndex); 172 173 private: 174 // Pointer to parent renderer object. 175 RendererVk *const mRenderer; 176 177 // For tracking the overall memory allocation sizes and counts per memory allocation type. 178 std::array<std::atomic<VkDeviceSize>, vk::kMemoryAllocationTypeCount> 179 mActiveMemoryAllocationsSize; 180 std::array<std::atomic<uint64_t>, vk::kMemoryAllocationTypeCount> mActiveMemoryAllocationsCount; 181 182 // Memory allocation data per memory heap. 183 using PerHeapMemoryAllocationSizeArray = 184 std::array<std::atomic<VkDeviceSize>, VK_MAX_MEMORY_HEAPS>; 185 using PerHeapMemoryAllocationCountArray = 186 std::array<std::atomic<uint64_t>, VK_MAX_MEMORY_HEAPS>; 187 188 std::array<PerHeapMemoryAllocationSizeArray, vk::kMemoryAllocationTypeCount> 189 mActivePerHeapMemoryAllocationsSize; 190 std::array<PerHeapMemoryAllocationCountArray, vk::kMemoryAllocationTypeCount> 191 mActivePerHeapMemoryAllocationsCount; 192 193 // Pending memory allocation information is used for logging in case of an allocation error. 194 // It includes the size and type of the last attempted allocation, which are cleared after 195 // the allocation is successful. 196 std::atomic<VkDeviceSize> mPendingMemoryAllocationSize; 197 std::atomic<vk::MemoryAllocationType> mPendingMemoryAllocationType; 198 std::atomic<uint32_t> mPendingMemoryTypeIndex; 199 200 // Mutex is used to update the data when debug layers are enabled. 201 std::mutex mMemoryAllocationMutex; 202 203 // Additional information regarding memory allocation with debug layers enabled, including 204 // allocation ID and a record of all active allocations. 205 uint64_t mMemoryAllocationID; 206 using MemoryAllocInfoMap = angle::HashMap<vk::MemoryAllocInfoMapKey, vk::MemoryAllocationInfo>; 207 std::unordered_map<angle::BacktraceInfo, MemoryAllocInfoMap> mMemoryAllocationRecord; 208 }; 209 } // namespace rx 210 211 // Introduce std::hash for MemoryAllocInfoMapKey. 212 namespace std 213 { 214 template <> 215 struct hash<rx::vk::MemoryAllocInfoMapKey> 216 { 217 size_t operator()(const rx::vk::MemoryAllocInfoMapKey &key) const { return key.hash(); } 218 }; 219 } // namespace std 220 221 #endif // LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_ 222