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// mtl_library_cache.mm: 7// Defines classes for caching of mtl libraries 8// 9 10#include "libANGLE/renderer/metal/mtl_library_cache.h" 11 12#include <stdio.h> 13 14#include <limits> 15 16#include "common/MemoryBuffer.h" 17#include "common/angleutils.h" 18#include "common/hash_utils.h" 19#include "common/mathutil.h" 20#include "common/string_utils.h" 21#include "common/system_utils.h" 22#include "libANGLE/histogram_macros.h" 23#include "libANGLE/renderer/metal/DisplayMtl.h" 24#include "platform/PlatformMethods.h" 25 26namespace rx 27{ 28namespace mtl 29{ 30 31LibraryCache::LibraryCache() : mCache(kMaxCachedLibraries) {} 32 33angle::ObjCPtr<id<MTLLibrary>> LibraryCache::get(const std::shared_ptr<const std::string> &source, 34 const std::map<std::string, std::string> ¯os, 35 bool disableFastMath, 36 bool usesInvariance) 37{ 38 ASSERT(source != nullptr); 39 LibraryCache::LibraryCacheEntry &entry = 40 getCacheEntry(LibraryKey(source, macros, disableFastMath, usesInvariance)); 41 42 // Try to lock the entry and return the library if it exists. If we can't lock then it means 43 // another thread is currently compiling. 44 std::unique_lock<std::mutex> entryLockGuard(entry.lock, std::try_to_lock); 45 if (entryLockGuard) 46 { 47 return entry.library; 48 } 49 else 50 { 51 return nil; 52 } 53} 54 55angle::ObjCPtr<id<MTLLibrary>> LibraryCache::getOrCompileShaderLibrary( 56 DisplayMtl *displayMtl, 57 const std::shared_ptr<const std::string> &source, 58 const std::map<std::string, std::string> ¯os, 59 bool disableFastMath, 60 bool usesInvariance, 61 angle::ObjCPtr<NSError> *errorOut) 62{ 63 id<MTLDevice> metalDevice = displayMtl->getMetalDevice(); 64 const angle::FeaturesMtl &features = displayMtl->getFeatures(); 65 if (!features.enableInMemoryMtlLibraryCache.enabled) 66 { 67 return CreateShaderLibrary(metalDevice, *source, macros, disableFastMath, usesInvariance, 68 errorOut); 69 } 70 71 ASSERT(source != nullptr); 72 LibraryCache::LibraryCacheEntry &entry = 73 getCacheEntry(LibraryKey(source, macros, disableFastMath, usesInvariance)); 74 75 // Lock this cache entry while compiling the shader. This causes other threads calling this 76 // function to wait and not duplicate the compilation. 77 std::lock_guard<std::mutex> entryLockGuard(entry.lock); 78 if (entry.library) 79 { 80 return entry.library; 81 } 82 83 entry.library = CreateShaderLibrary(metalDevice, *source, macros, disableFastMath, 84 usesInvariance, errorOut); 85 return entry.library; 86} 87 88LibraryCache::LibraryCacheEntry &LibraryCache::getCacheEntry(LibraryKey &&key) 89{ 90 // Lock while searching or adding new items to the cache. 91 std::lock_guard<std::mutex> cacheLockGuard(mCacheLock); 92 93 auto iter = mCache.Get(key); 94 if (iter != mCache.end()) 95 { 96 return iter->second; 97 } 98 99 angle::TrimCache(kMaxCachedLibraries, kGCLimit, "metal library", &mCache); 100 101 iter = mCache.Put(std::move(key), LibraryCacheEntry()); 102 return iter->second; 103} 104 105LibraryCache::LibraryKey::LibraryKey(const std::shared_ptr<const std::string> &sourceIn, 106 const std::map<std::string, std::string> ¯osIn, 107 bool disableFastMathIn, 108 bool usesInvarianceIn) 109 : source(sourceIn), 110 macros(macrosIn), 111 disableFastMath(disableFastMathIn), 112 usesInvariance(usesInvarianceIn) 113{} 114 115bool LibraryCache::LibraryKey::operator==(const LibraryKey &other) const 116{ 117 return std::tie(*source, macros, disableFastMath, usesInvariance) == 118 std::tie(*other.source, other.macros, other.disableFastMath, other.usesInvariance); 119} 120 121size_t LibraryCache::LibraryKeyHasher::operator()(const LibraryKey &k) const 122{ 123 size_t hash = 0; 124 angle::HashCombine(hash, *k.source); 125 for (const auto ¯o : k.macros) 126 { 127 angle::HashCombine(hash, macro.first); 128 angle::HashCombine(hash, macro.second); 129 } 130 angle::HashCombine(hash, k.disableFastMath); 131 angle::HashCombine(hash, k.usesInvariance); 132 return hash; 133} 134 135LibraryCache::LibraryCacheEntry::~LibraryCacheEntry() 136{ 137 // Lock the cache entry before deletion to ensure there is no other thread compiling and 138 // preparing to write to the library. LibraryCacheEntry objects can only be deleted while the 139 // mCacheLock is held so only one thread modifies mCache at a time. 140 std::lock_guard<std::mutex> entryLockGuard(lock); 141} 142 143LibraryCache::LibraryCacheEntry::LibraryCacheEntry(LibraryCacheEntry &&moveFrom) 144{ 145 // Lock the cache entry being moved from to make sure the library can be safely accessed. 146 // Mutexes cannot be moved so a new one will be created in this entry 147 std::lock_guard<std::mutex> entryLockGuard(moveFrom.lock); 148 149 library = std::move(moveFrom.library); 150 moveFrom.library = nullptr; 151} 152 153} // namespace mtl 154} // namespace rx 155