• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &macros,
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> &macros,
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> &macrosIn,
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 &macro : 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