// // Copyright 2023 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // MemoryTracking.h: // Defines the classes used for memory tracking in ANGLE. // #ifndef LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_ #define LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_ #include #include #include #include "common/angleutils.h" #include "common/backtrace_utils.h" #include "common/vulkan/vk_headers.h" namespace rx { class RendererVk; namespace vk { // Used to designate memory allocation type for tracking purposes. enum class MemoryAllocationType { Unspecified = 0, ImageExternal = 1, OffscreenSurfaceAttachmentImage = 2, SwapchainMSAAImage = 3, SwapchainDepthStencilImage = 4, StagingImage = 5, ImplicitMultisampledRenderToTextureImage = 6, TextureImage = 7, FontImage = 8, RenderBufferStorageImage = 9, Buffer = 10, BufferExternal = 11, InvalidEnum = 12, EnumCount = InvalidEnum, }; constexpr const char *kMemoryAllocationTypeMessage[] = { "Unspecified", "ImageExternal", "OffscreenSurfaceAttachmentImage", "SwapchainMSAAImage", "SwapchainDepthStencilImage", "StagingImage", "ImplicitMultisampledRenderToTextureImage", "TextureImage", "FontImage", "RenderBufferStorageImage", "Buffer", "BufferExternal", "Invalid", }; constexpr const uint32_t kMemoryAllocationTypeCount = static_cast(MemoryAllocationType::EnumCount); // Used to select the severity for memory allocation logs. enum class MemoryLogSeverity { INFO, WARN, }; // Used to store memory allocation information for tracking purposes. struct MemoryAllocationInfo { MemoryAllocationInfo() = default; uint64_t id; MemoryAllocationType allocType; uint32_t memoryHeapIndex; void *handle; VkDeviceSize size; }; class MemoryAllocInfoMapKey { public: MemoryAllocInfoMapKey() : handle(nullptr) {} MemoryAllocInfoMapKey(void *handle) : handle(handle) {} bool operator==(const MemoryAllocInfoMapKey &rhs) const { return reinterpret_cast(handle) == reinterpret_cast(rhs.handle); } size_t hash() const; private: void *handle; }; // Process GPU memory reports class MemoryReport final : angle::NonCopyable { public: MemoryReport(); void processCallback(const VkDeviceMemoryReportCallbackDataEXT &callbackData, bool logCallback); void logMemoryReportStats() const; private: struct MemorySizes { VkDeviceSize allocatedMemory; VkDeviceSize allocatedMemoryMax; VkDeviceSize importedMemory; VkDeviceSize importedMemoryMax; }; mutable std::mutex mMemoryReportMutex; VkDeviceSize mCurrentTotalAllocatedMemory; VkDeviceSize mMaxTotalAllocatedMemory; angle::HashMap mSizesPerType; VkDeviceSize mCurrentTotalImportedMemory; VkDeviceSize mMaxTotalImportedMemory; angle::HashMap mUniqueIDCounts; }; } // namespace vk // Memory tracker for allocations and deallocations, which is used in RendererVk. class MemoryAllocationTracker : angle::NonCopyable { public: MemoryAllocationTracker(RendererVk *renderer); void initMemoryTrackers(); void onDeviceInit(); void onDestroy(); // Memory statistics are logged when handling a context error. void logMemoryStatsOnError(); // Collect information regarding memory allocations and deallocations. void onMemoryAllocImpl(vk::MemoryAllocationType allocType, VkDeviceSize size, uint32_t memoryTypeIndex, void *handle); void onMemoryDeallocImpl(vk::MemoryAllocationType allocType, VkDeviceSize size, uint32_t memoryTypeIndex, void *handle); // Memory allocation statistics functions. VkDeviceSize getActiveMemoryAllocationsSize(uint32_t allocTypeIndex) const; VkDeviceSize getActiveHeapMemoryAllocationsSize(uint32_t allocTypeIndex, uint32_t heapIndex) const; uint64_t getActiveMemoryAllocationsCount(uint32_t allocTypeIndex) const; uint64_t getActiveHeapMemoryAllocationsCount(uint32_t allocTypeIndex, uint32_t heapIndex) const; // Compare the expected flags with the flags of the allocated memory. void compareExpectedFlagsWithAllocatedFlags(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, VkMemoryPropertyFlags allocatedFlags, void *handle); // Pending memory allocation information is used for logging in case of an unsuccessful // allocation. It is cleared in onMemoryAlloc(). VkDeviceSize getPendingMemoryAllocationSize() const; vk::MemoryAllocationType getPendingMemoryAllocationType() const; uint32_t getPendingMemoryTypeIndex() const; void resetPendingMemoryAlloc(); void setPendingMemoryAlloc(vk::MemoryAllocationType allocType, VkDeviceSize size, uint32_t memoryTypeIndex); private: // Pointer to parent renderer object. RendererVk *const mRenderer; // For tracking the overall memory allocation sizes and counts per memory allocation type. std::array, vk::kMemoryAllocationTypeCount> mActiveMemoryAllocationsSize; std::array, vk::kMemoryAllocationTypeCount> mActiveMemoryAllocationsCount; // Memory allocation data per memory heap. using PerHeapMemoryAllocationSizeArray = std::array, VK_MAX_MEMORY_HEAPS>; using PerHeapMemoryAllocationCountArray = std::array, VK_MAX_MEMORY_HEAPS>; std::array mActivePerHeapMemoryAllocationsSize; std::array mActivePerHeapMemoryAllocationsCount; // Pending memory allocation information is used for logging in case of an allocation error. // It includes the size and type of the last attempted allocation, which are cleared after // the allocation is successful. std::atomic mPendingMemoryAllocationSize; std::atomic mPendingMemoryAllocationType; std::atomic mPendingMemoryTypeIndex; // Mutex is used to update the data when debug layers are enabled. std::mutex mMemoryAllocationMutex; // Additional information regarding memory allocation with debug layers enabled, including // allocation ID and a record of all active allocations. uint64_t mMemoryAllocationID; using MemoryAllocInfoMap = angle::HashMap; std::unordered_map mMemoryAllocationRecord; }; } // namespace rx // Introduce std::hash for MemoryAllocInfoMapKey. namespace std { template <> struct hash { size_t operator()(const rx::vk::MemoryAllocInfoMapKey &key) const { return key.hash(); } }; } // namespace std #endif // LIBANGLE_RENDERER_VULKAN_MEMORYTRACKING_H_