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